From 8ca471e7304e813fa55ebf512a8f30f146fe6b41 Mon Sep 17 00:00:00 2001 From: Kevin Smith <git@kismith.co.uk> Date: Mon, 4 Oct 2010 17:08:10 +0100 Subject: Assign contacts to groups. Another patch will follow shortly to stop them appearing offline after a roster change like this. Resolves: #272 Release-Notes: It's now possible to assign your contacts to groups. diff --git a/Swift/QtUI/ContextMenus/QtRosterContextMenu.cpp b/Swift/QtUI/ContextMenus/QtRosterContextMenu.cpp index 59c3b78..1641266 100644 --- a/Swift/QtUI/ContextMenus/QtRosterContextMenu.cpp +++ b/Swift/QtUI/ContextMenus/QtRosterContextMenu.cpp @@ -10,20 +10,26 @@ #include <QLineEdit> #include <QMenu> #include <QDebug> +#include <QDialog> #include <boost/shared_ptr.hpp> #include "Swiften/Roster/ContactRosterItem.h" +#include "Swiften/Roster/GroupRosterItem.h" #include "Swiften/Base/String.h" +#include "Swiften/Roster/Roster.h" #include "Swift/Controllers/UIEvents/UIEvent.h" #include "Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h" #include "Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h" #include "Swift/QtUI/QtSwiftUtil.h" +#include "Swift/QtUI/QtSetGroupsDialog.h" + namespace Swift { -QtRosterContextMenu::QtRosterContextMenu(UIEventStream* eventStream) { +QtRosterContextMenu::QtRosterContextMenu(UIEventStream* eventStream, QtTreeWidget* treeWidget) { eventStream_ = eventStream; + treeWidget_ = treeWidget; } void QtRosterContextMenu::show(RosterItem* item) { @@ -32,10 +38,36 @@ void QtRosterContextMenu::show(RosterItem* item) { return; } item_ = item; - QMenu* contextMenu = new QMenu(); - contextMenu->addAction("Remove", this, SLOT(handleRemoveContact())); - contextMenu->addAction("Rename", this, SLOT(handleRenameContact())); - contextMenu->exec(QCursor::pos()); + QMenu contextMenu; + contextMenu.addAction("Remove", this, SLOT(handleRemoveContact())); + contextMenu.addAction("Rename", this, SLOT(handleRenameContact())); + contextMenu.addAction("Groups", this, SLOT(handleRegroupContact())); + /*QMenu* groupsMenu = contextMenu.addMenu("Groups"); + std::map<QAction, String> groupActions; + for (int i = 0; i < 0; i++) { + String groupName; + groupActions[groupsMenu->addAction(P2QSTRING(groupName))] = groupName; + } + groupsMenu->addSeparator(); + groupsMenu->addAction("New Group", this SLOT(handleNewGroup()));*/ + contextMenu.exec(QCursor::pos()); +} + +void QtRosterContextMenu::handleRegroupContact() { + QList<QString> allGroups; + foreach (RosterItem* item, treeWidget_->getRoster()->getRoot()->getChildren()) { + GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item); + if (group) { + allGroups.push_back(P2QSTRING(group->getDisplayName())); + } + } + ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item_); + assert(contact); + QtSetGroupsDialog groupDialog(contact, allGroups); + + if (groupDialog.exec() == QDialog::Accepted) { + eventStream_->send(groupDialog.getRegroupEvent()); + } } void QtRosterContextMenu::handleRemoveContact() { diff --git a/Swift/QtUI/ContextMenus/QtRosterContextMenu.h b/Swift/QtUI/ContextMenus/QtRosterContextMenu.h index 44f3314..f2c7e1f 100644 --- a/Swift/QtUI/ContextMenus/QtRosterContextMenu.h +++ b/Swift/QtUI/ContextMenus/QtRosterContextMenu.h @@ -9,6 +9,7 @@ #include <QObject> #include "Swift/QtUI/ContextMenus/QtContextMenu.h" +#include "Swift/QtUI/Roster/QtTreeWidget.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" namespace Swift { @@ -16,15 +17,17 @@ namespace Swift { class QtRosterContextMenu : public QObject, public QtContextMenu { Q_OBJECT public: - QtRosterContextMenu(UIEventStream* eventStream); + QtRosterContextMenu(UIEventStream* eventStream, QtTreeWidget* treeWidget); void show(RosterItem* item); private slots: void handleRemoveContact(); void handleRenameContact(); + void handleRegroupContact(); private: RosterItem* item_; UIEventStream* eventStream_; + QtTreeWidget* treeWidget_; }; } diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index 6f7783f..f669e95 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -53,7 +53,7 @@ QtMainWindow::QtMainWindow(UIEventStream* uiEventStream) : QWidget() { contactTabLayout->setContentsMargins(0, 0, 0, 0); treeWidget_ = new QtTreeWidget(uiEventStream_); - contextMenu_ = new QtRosterContextMenu(uiEventStream_); + contextMenu_ = new QtRosterContextMenu(uiEventStream_, treeWidget_); treeWidget_->setContextMenu(contextMenu_); treeWidget_->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); contactTabLayout->addWidget(treeWidget_); diff --git a/Swift/QtUI/QtSetGroupsDialog.cpp b/Swift/QtUI/QtSetGroupsDialog.cpp new file mode 100644 index 0000000..ad24122 --- /dev/null +++ b/Swift/QtUI/QtSetGroupsDialog.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "QtSetGroupsDialog.h" + +#include <algorithm> + +#include <QScrollArea> +#include <QBoxLayout> +#include <QLabel> +#include <QDialogButtonBox> + +#include "Swift/QtUI/QtSwiftUtil.h" + +namespace Swift { + +QtSetGroupsDialog::QtSetGroupsDialog(ContactRosterItem* contact, const QList<QString>& allGroups) : contact_(contact) { + QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, this); + QScrollArea* scroll = new QScrollArea(this); + layout->addWidget(scroll); + QBoxLayout* scrollLayout = new QBoxLayout(QBoxLayout::TopToBottom, scroll); + QLabel* label = new QLabel(scroll); + label->setText("Choose new groups for " + P2QSTRING(contact->getDisplayName())); + scrollLayout->addWidget(label); + foreach (QString group, allGroups) { + QCheckBox* check = new QCheckBox(scroll); + check->setText(group); + check->setCheckState(Qt::Unchecked); + checkBoxes_[Q2PSTRING(group)] = check; + scrollLayout->addWidget(check); + } + foreach (String group, contact->getGroups()) { + checkBoxes_[group]->setCheckState(Qt::Checked); + } + QWidget* newGroupWidget = new QWidget(scroll); + QBoxLayout* newGroupLayout = new QBoxLayout(QBoxLayout::LeftToRight, newGroupWidget); + scrollLayout->addWidget(newGroupWidget); + newGroup_ = new QCheckBox(newGroupWidget); + newGroup_->setText("New Group:"); + newGroup_->setCheckState(Qt::Unchecked); + newGroupLayout->addWidget(newGroup_); + newGroupName_ = new QLineEdit(newGroupWidget); + newGroupLayout->addWidget(newGroupName_); + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); + layout->addWidget(buttons); + connect(buttons, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttons, SIGNAL(rejected()), this, SLOT(reject())); +} + +QtSetGroupsDialog::~QtSetGroupsDialog() { + // TODO Auto-generated destructor stub +} + +typedef std::pair<String, QCheckBox*> CheckStringPair; + +boost::shared_ptr<RegroupRosterItemUIEvent> QtSetGroupsDialog::getRegroupEvent() { + std::vector<String> addedGroups; + std::vector<String> removedGroups; + std::vector<String> existingGroups = contact_->getGroups(); + int tickedCount = 0; + bool wantsContacts = false; + foreach (CheckStringPair pair, checkBoxes_) { + bool existing = std::find(existingGroups.begin(), existingGroups.end(), pair.first) != existingGroups.end(); + if (pair.second->checkState() == Qt::Checked) { + tickedCount++; + if (pair.first == "Contacts") { + wantsContacts = true; + } + if (!existing) { + addedGroups.push_back(pair.first); + } + } else { + if (existing) { + removedGroups.push_back(pair.first); + } + } + } + if (newGroup_->checkState() == Qt::Checked) { + tickedCount++; + String name = Q2PSTRING(newGroupName_->text()); + if (std::find(existingGroups.begin(), existingGroups.end(), name) == existingGroups.end()) { + addedGroups.push_back(name); + } + } + if (tickedCount > 1 && wantsContacts) { + addedGroups.push_back("Contacts"); + } + boost::shared_ptr<RegroupRosterItemUIEvent> result(new RegroupRosterItemUIEvent(contact_->getJID(), addedGroups, removedGroups)); + return result; +} + +} diff --git a/Swift/QtUI/QtSetGroupsDialog.h b/Swift/QtUI/QtSetGroupsDialog.h new file mode 100644 index 0000000..e8300f5 --- /dev/null +++ b/Swift/QtUI/QtSetGroupsDialog.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <map> + +#include <boost/shared_ptr.hpp> + +#include <QDialog> +#include <QCheckBox> +#include <QLineEdit> +#include <QList> + +#include "Swiften/Roster/ContactRosterItem.h" +#include "Swift/Controllers/UIEvents/RegroupRosterItemUIEvent.h" + +namespace Swift { + +class QtSetGroupsDialog : public QDialog { + Q_OBJECT + public: + QtSetGroupsDialog(ContactRosterItem* contact, const QList<QString>& allGroups); + virtual ~QtSetGroupsDialog(); + boost::shared_ptr<RegroupRosterItemUIEvent> getRegroupEvent(); + private: + ContactRosterItem* contact_; + std::map<String, QCheckBox*> checkBoxes_; + QCheckBox* newGroup_; + QLineEdit* newGroupName_; +}; + +} + diff --git a/Swift/QtUI/Roster/QtTreeWidget.cpp b/Swift/QtUI/Roster/QtTreeWidget.cpp index 6ace3df..885cba6 100644 --- a/Swift/QtUI/Roster/QtTreeWidget.cpp +++ b/Swift/QtUI/Roster/QtTreeWidget.cpp @@ -46,6 +46,7 @@ QtTreeWidget::~QtTreeWidget() { } void QtTreeWidget::setRosterModel(Roster* roster) { + roster_ = roster; model_->setRoster(roster); expandAll(); } diff --git a/Swift/QtUI/Roster/QtTreeWidget.h b/Swift/QtUI/Roster/QtTreeWidget.h index 796afed..838c453 100644 --- a/Swift/QtUI/Roster/QtTreeWidget.h +++ b/Swift/QtUI/Roster/QtTreeWidget.h @@ -26,6 +26,7 @@ class QtTreeWidget : public QTreeView{ QtTreeWidgetItem* getRoot(); void setContextMenu(QtContextMenu* contextMenu); void setRosterModel(Roster* roster); + Roster* getRoster() {return roster_;} private slots: void handleItemActivated(const QModelIndex&); void handleModelItemExpanded(const QModelIndex&, bool expanded); @@ -38,6 +39,7 @@ class QtTreeWidget : public QTreeView{ private: void drawBranches(QPainter*, const QRect&, const QModelIndex&) const; RosterModel* model_; + Roster* roster_; RosterDelegate* delegate_; QtTreeWidgetItem* treeRoot_; QtContextMenu* contextMenu_; diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 609eb5d..a3631e5 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -83,6 +83,7 @@ sources = [ "QtBookmarkDetailWindow.cpp", "QtAddBookmarkWindow.cpp", "QtEditBookmarkWindow.cpp", + "QtSetGroupsDialog.cpp", "ChatSnippet.cpp", "MessageSnippet.cpp", "SystemMessageSnippet.cpp", diff --git a/Swiften/Roster/ContactRosterItem.cpp b/Swiften/Roster/ContactRosterItem.cpp index 9251fad..bbf3928 100644 --- a/Swiften/Roster/ContactRosterItem.cpp +++ b/Swiften/Roster/ContactRosterItem.cpp @@ -98,6 +98,18 @@ void ContactRosterItem::applyPresence(const String& resource, boost::shared_ptr< onDataChanged(); } +const std::vector<String> ContactRosterItem::getGroups() const { + return groups_; +} + +/** Only used so a contact can know about the groups it's in*/ +void ContactRosterItem::addGroup(const String& group) { + groups_.push_back(group); +} +void ContactRosterItem::removeGroup(const String& group) { + groups_.erase(std::find(groups_.begin(), groups_.end(), group)); +} + } diff --git a/Swiften/Roster/ContactRosterItem.h b/Swiften/Roster/ContactRosterItem.h index 21f6024..707dd42 100644 --- a/Swiften/Roster/ContactRosterItem.h +++ b/Swiften/Roster/ContactRosterItem.h @@ -36,6 +36,10 @@ class ContactRosterItem : public RosterItem { void applyPresence(const String& resource, boost::shared_ptr<Presence> presence); void clearPresence(); void calculateShownPresence(); + const std::vector<String> getGroups() const; + /** Only used so a contact can know about the groups it's in*/ + void addGroup(const String& group); + void removeGroup(const String& group); private: JID jid_; JID displayJID_; @@ -44,6 +48,7 @@ class ContactRosterItem : public RosterItem { std::map<String, boost::shared_ptr<Presence> > presences_; boost::shared_ptr<Presence> offlinePresence_; boost::shared_ptr<Presence> shownPresence_; + std::vector<String> groups_; }; } diff --git a/Swiften/Roster/Roster.cpp b/Swiften/Roster/Roster.cpp index f9f0dbb..68bac53 100644 --- a/Swiften/Roster/Roster.cpp +++ b/Swiften/Roster/Roster.cpp @@ -73,9 +73,18 @@ void Roster::addContact(const JID& jid, const JID& displayJID, const String& nam ContactRosterItem *item = new ContactRosterItem(jid, displayJID, name, group); item->setAvatarPath(avatarPath); group->addChild(item); + if (itemMap_[fullJIDMapping_ ? jid : jid.toBare()].size() > 0) { + foreach (String existingGroup, itemMap_[fullJIDMapping_ ? jid : jid.toBare()][0]->getGroups()) { + item->addGroup(existingGroup); + } + } itemMap_[fullJIDMapping_ ? jid : jid.toBare()].push_back(item); item->onDataChanged.connect(boost::bind(&Roster::handleDataChanged, this, item)); filterContact(item, group); + + foreach (ContactRosterItem* item, itemMap_[fullJIDMapping_ ? jid : jid.toBare()]) { + item->addGroup(groupName); + } } struct JIDEqualsTo { @@ -113,6 +122,9 @@ void Roster::removeContactFromGroup(const JID& jid, const String& groupName) { } it++; } + foreach (ContactRosterItem* item, itemMap_[fullJIDMapping_ ? jid : jid.toBare()]) { + item->removeGroup(groupName); + } } -- cgit v0.10.2-6-g49f6