diff options
author | Remko Tronçon <git@el-tramo.be> | 2011-02-06 22:50:30 (GMT) |
---|---|---|
committer | Remko Tronçon <git@el-tramo.be> | 2011-02-07 15:22:48 (GMT) |
commit | afcfa9dd33cfb5e36edf7d8148a7f8b24c976741 (patch) | |
tree | c16d40dbb089a9bcf70fafc2d50def34a9984e58 | |
parent | 90a511ed523cfaf500dd27316b12e128e0c70ce3 (diff) | |
download | swift-contrib-afcfa9dd33cfb5e36edf7d8148a7f8b24c976741.zip swift-contrib-afcfa9dd33cfb5e36edf7d8148a7f8b24c976741.tar.bz2 |
Reworking contact editing.
Collapsed rename, group edit, and remove into one dialog.
Moved contact editing logic to controllers.
33 files changed, 663 insertions, 452 deletions
diff --git a/Swift/Controllers/ContactEditController.cpp b/Swift/Controllers/ContactEditController.cpp new file mode 100644 index 0000000..286fdeb --- /dev/null +++ b/Swift/Controllers/ContactEditController.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/ContactEditController.h> + +#include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h> +#include <Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h> +#include <Swift/Controllers/RosterController.h> + + +namespace Swift { + +ContactEditController::ContactEditController(RosterController* rosterController, ContactEditWindowFactory* contactEditWindowFactory, UIEventStream* uiEventStream) : rosterController(rosterController), contactEditWindowFactory(contactEditWindowFactory), uiEventStream(uiEventStream), contactEditWindow(NULL) { + uiEventStream->onUIEvent.connect(boost::bind(&ContactEditController::handleUIEvent, this, _1)); +} + +ContactEditController::~ContactEditController() { + if (contactEditWindow) { + contactEditWindow->onChangeContactRequest.disconnect(boost::bind(&ContactEditController::handleChangeContactRequest, this, _1, _2)); + contactEditWindow->onRemoveContactRequest.disconnect(boost::bind(&ContactEditController::handleRemoveContactRequest, this)); + delete contactEditWindow; + } + uiEventStream->onUIEvent.disconnect(boost::bind(&ContactEditController::handleUIEvent, this, _1)); +} + +void ContactEditController::handleUIEvent(UIEvent::ref event) { + RequestContactEditorUIEvent::ref editEvent = boost::dynamic_pointer_cast<RequestContactEditorUIEvent>(event); + if (!editEvent) { + return; + } + + if (!contactEditWindow) { + contactEditWindow = contactEditWindowFactory->createContactEditWindow(); + contactEditWindow->onRemoveContactRequest.connect(boost::bind(&ContactEditController::handleRemoveContactRequest, this)); + contactEditWindow->onChangeContactRequest.connect(boost::bind(&ContactEditController::handleChangeContactRequest, this, _1, _2)); + } + currentContact = rosterController->getItem(editEvent->getJID()); + assert(currentContact); + contactEditWindow->setContact(currentContact->getJID(), currentContact->getName(), currentContact->getGroups(), rosterController->getGroups()); + contactEditWindow->show(); +} + +void ContactEditController::setAvailable(bool b) { + if (contactEditWindow) { + contactEditWindow->setEnabled(b); + } +} + +void ContactEditController::handleRemoveContactRequest() { + assert(currentContact); + uiEventStream->send(boost::make_shared<RemoveRosterItemUIEvent>(currentContact->getJID())); + contactEditWindow->hide(); +} + +void ContactEditController::handleChangeContactRequest(const String& name, const std::vector<String>& groups) { + std::vector<String> oldGroupsVector = currentContact->getGroups(); + std::set<String> oldGroups(oldGroupsVector.begin(), oldGroupsVector.end()); + std::set<String> newGroups(groups.begin(), groups.end()); + if (oldGroups != newGroups || currentContact->getName() != name) { + XMPPRosterItem newContact(*currentContact); + newContact.setName(name); + newContact.setGroups(groups); + rosterController->updateItem(newContact); + } + contactEditWindow->hide(); +} + +} diff --git a/Swift/Controllers/ContactEditController.h b/Swift/Controllers/ContactEditController.h new file mode 100644 index 0000000..31917b1 --- /dev/null +++ b/Swift/Controllers/ContactEditController.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <vector> +#include <boost/optional.hpp> + +#include <Swiften/JID/JID.h> +#include <Swiften/Base/String.h> +#include <Swift/Controllers/UIEvents/UIEvent.h> +#include <Swiften/Roster/XMPPRosterItem.h> + +namespace Swift { + class UIEventStream; + class ContactEditWindowFactory; + class ContactEditWindow; + class RosterController; + + class ContactEditController { + public: + ContactEditController(RosterController* rosterController, ContactEditWindowFactory* contactEditWindowFactory, UIEventStream* uiEventStream); + ~ContactEditController(); + + void setAvailable(bool b); + + private: + void handleRemoveContactRequest(); + void handleChangeContactRequest(const String& name, const std::vector<String>& groups); + + private: + void handleUIEvent(UIEvent::ref event); + + private: + boost::optional<XMPPRosterItem> currentContact; + RosterController* rosterController; + ContactEditWindowFactory* contactEditWindowFactory; + UIEventStream* uiEventStream; + ContactEditWindow* contactEditWindow; + }; +} + diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 1b513cd..3e1b366 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -62,6 +62,7 @@ #include "Swift/Controllers/CertificateStorageTrustChecker.h" #include "Swiften/Network/NetworkFactories.h" #include <Swift/Controllers/ProfileController.h> +#include <Swift/Controllers/ContactEditController.h> namespace Swift { @@ -100,6 +101,7 @@ MainController::MainController( chatsManager_ = NULL; eventWindowController_ = NULL; profileController_ = NULL; + contactEditController_ = NULL; userSearchControllerChat_ = NULL; userSearchControllerAdd_ = NULL; quitRequested_ = false; @@ -172,6 +174,8 @@ MainController::~MainController() { void MainController::resetClient() { resetCurrentError(); resetPendingReconnects(); + delete contactEditController_; + contactEditController_ = NULL; delete profileController_; profileController_ = NULL; delete eventWindowController_; @@ -233,11 +237,12 @@ void MainController::handleConnected() { myStatusLooksOnline_ = true; if (freshLogin) { profileController_ = new ProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_); - rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_); rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2)); rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this)); + contactEditController_ = new ContactEditController(rosterController_, uiFactory_, uiEventStream_); + chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, settings_); client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1)); chatsManager_->setAvatarManager(client_->getAvatarManager()); @@ -268,6 +273,7 @@ void MainController::handleConnected() { rosterController_->setEnabled(true); profileController_->setAvailable(true); + contactEditController_->setAvailable(true); /* Send presence later to catch all the incoming presences. */ sendPresence(statusTracker_->getNextPresence()); /* Enable chats last of all, so rejoining MUCs has the right sent presence */ @@ -532,6 +538,9 @@ void MainController::setManagersOffline() { if (profileController_) { profileController_->setAvailable(false); } + if (contactEditController_) { + contactEditController_->setAvailable(false); + } } void MainController::handleServerDiscoInfoResponse(boost::shared_ptr<DiscoInfo> info, ErrorPayload::ref error) { diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index d6e54ef..09f17d2 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -43,6 +43,7 @@ namespace Swift { class MUCController; class Notifier; class ProfileController; + class ContactEditController; class TogglableNotifier; class PresenceNotifier; class EventNotifier; @@ -132,6 +133,7 @@ namespace Swift { XMLConsoleController* xmlConsoleController_; ChatsManager* chatsManager_; ProfileController* profileController_; + ContactEditController* contactEditController_; JID jid_; JID boundJID_; SystemTrayController* systemTrayController_; diff --git a/Swift/Controllers/RosterController.cpp b/Swift/Controllers/RosterController.cpp index 0144c97..be735cf 100644 --- a/Swift/Controllers/RosterController.cpp +++ b/Swift/Controllers/RosterController.cpp @@ -7,6 +7,7 @@ #include "Swift/Controllers/RosterController.h" #include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared.hpp> #include "Swiften/Base/foreach.h" #include "Swift/Controllers/UIInterfaces/MainWindow.h" @@ -27,10 +28,11 @@ #include "Swiften/Roster/SetName.h" #include "Swiften/Roster/OfflineRosterFilter.h" #include "Swiften/Roster/XMPPRoster.h" +#include "Swiften/Roster/XMPPRosterItem.h" #include "Swift/Controllers/UIEvents/AddContactUIEvent.h" #include "Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h" #include "Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h" -#include "Swift/Controllers/UIEvents/RegroupRosterItemUIEvent.h" +#include "Swift/Controllers/UIEvents/RenameGroupUIEvent.h" #include "Swift/Controllers/UIEvents/ToggleShowOfflineUIEvent.h" #include <Swiften/Client/NickManager.h> @@ -165,12 +167,10 @@ void RosterController::handleOnJIDUpdated(const JID& jid, const String& oldName, } void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) { - boost::shared_ptr<ToggleShowOfflineUIEvent> showOfflineEvent = boost::dynamic_pointer_cast<ToggleShowOfflineUIEvent>(event); - if (showOfflineEvent) { + if (boost::shared_ptr<ToggleShowOfflineUIEvent> showOfflineEvent = boost::dynamic_pointer_cast<ToggleShowOfflineUIEvent>(event)) { handleShowOfflineToggled(showOfflineEvent->getShow()); } - boost::shared_ptr<AddContactUIEvent> addContactEvent = boost::dynamic_pointer_cast<AddContactUIEvent>(event); - if (addContactEvent) { + else if (boost::shared_ptr<AddContactUIEvent> addContactEvent = boost::dynamic_pointer_cast<AddContactUIEvent>(event)) { RosterItemPayload item; item.setName(addContactEvent->getName()); item.setJID(addContactEvent->getJID()); @@ -180,10 +180,8 @@ void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) { request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); request->send(); subscriptionManager_->requestSubscription(addContactEvent->getJID()); - return; } - boost::shared_ptr<RemoveRosterItemUIEvent> removeEvent = boost::dynamic_pointer_cast<RemoveRosterItemUIEvent>(event); - if (removeEvent) { + else if (boost::shared_ptr<RemoveRosterItemUIEvent> removeEvent = boost::dynamic_pointer_cast<RemoveRosterItemUIEvent>(event)) { RosterItemPayload item(removeEvent->getJID(), "", RosterItemPayload::Remove); boost::shared_ptr<RosterPayload> roster(new RosterPayload()); roster->addItem(item); @@ -191,10 +189,8 @@ void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) { request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); request->send(); - return; } - boost::shared_ptr<RenameRosterItemUIEvent> renameEvent = boost::dynamic_pointer_cast<RenameRosterItemUIEvent>(event); - if (renameEvent) { + else if (boost::shared_ptr<RenameRosterItemUIEvent> renameEvent = boost::dynamic_pointer_cast<RenameRosterItemUIEvent>(event)) { JID contact(renameEvent->getJID()); RosterItemPayload item(contact, renameEvent->getNewName(), xmppRoster_->getSubscriptionStateForJID(contact)); item.setGroups(xmppRoster_->getGroupsForJID(contact)); @@ -203,34 +199,44 @@ void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) { SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); request->send(); - return; } - boost::shared_ptr<RegroupRosterItemUIEvent> regroupEvent = boost::dynamic_pointer_cast<RegroupRosterItemUIEvent>(event); - if (regroupEvent) { - JID contact(regroupEvent->getJID()); - RosterItemPayload item(contact, xmppRoster_->getNameForJID(contact), xmppRoster_->getSubscriptionStateForJID(contact)); - std::vector<String> newGroups; - const std::vector<String> addedGroups = regroupEvent->getAddedGroups(); - const std::vector<String> removedGroups = regroupEvent->getRemovedGroups(); - foreach (const String& oldGroup, xmppRoster_->getGroupsForJID(contact)) { - if (std::find(removedGroups.begin(), removedGroups.end(), oldGroup) == removedGroups.end() - && std::find(addedGroups.begin(), addedGroups.end(), oldGroup) == addedGroups.end()) { - newGroups.push_back(oldGroup); - } + else if (boost::shared_ptr<RenameGroupUIEvent> renameGroupEvent = boost::dynamic_pointer_cast<RenameGroupUIEvent>(event)) { + std::vector<XMPPRosterItem> items = xmppRoster_->getItems(); + String group = renameGroupEvent->getGroup(); + // FIXME: We should handle contacts groups specially to avoid clashes + if (group == "Contacts") { + group = ""; } - foreach (const String& newGroup, regroupEvent->getAddedGroups()) { - newGroups.push_back(newGroup); + foreach(XMPPRosterItem& item, items) { + std::vector<String> groups = item.getGroups(); + if ( (group.isEmpty() && groups.empty()) || std::find(groups.begin(), groups.end(), group) != groups.end()) { + groups.erase(std::remove(groups.begin(), groups.end(), group), groups.end()); + if (std::find(groups.begin(), groups.end(), renameGroupEvent->getNewName()) == groups.end()) { + groups.push_back(renameGroupEvent->getNewName()); + } + item.setGroups(groups); + updateItem(item); + } } - item.setGroups(newGroups); - boost::shared_ptr<RosterPayload> roster(new RosterPayload()); - roster->addItem(item); - SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); - request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); - request->send(); - return; } } +void RosterController::setContactGroups(const JID& jid, const std::vector<String>& groups) { + updateItem(XMPPRosterItem(jid, xmppRoster_->getNameForJID(jid), groups, xmppRoster_->getSubscriptionStateForJID(jid))); +} + +void RosterController::updateItem(const XMPPRosterItem& item) { + RosterItemPayload itemPayload(item.getJID(), item.getName(), item.getSubscription()); + itemPayload.setGroups(item.getGroups()); + + RosterPayload::ref roster = boost::make_shared<RosterPayload>(); + roster->addItem(itemPayload); + + SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); + request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); + request->send(); +} + void RosterController::handleRosterSetError(ErrorPayload::ref error, boost::shared_ptr<RosterPayload> rosterPayload) { if (!error) { return; @@ -281,4 +287,12 @@ void RosterController::handleAvatarChanged(const JID& jid) { } } +boost::optional<XMPPRosterItem> RosterController::getItem(const JID& jid) const { + return xmppRoster_->getItem(jid); +} + +std::set<String> RosterController::getGroups() const { + return xmppRoster_->getGroups(); +} + } diff --git a/Swift/Controllers/RosterController.h b/Swift/Controllers/RosterController.h index 9e22686..d8c2487 100644 --- a/Swift/Controllers/RosterController.h +++ b/Swift/Controllers/RosterController.h @@ -22,6 +22,7 @@ namespace Swift { class IQRouter; class Roster; class XMPPRoster; + class XMPPRosterItem; class MainWindow; class MainWindowFactory; class OfflineRosterFilter; @@ -45,6 +46,13 @@ namespace Swift { boost::signal<void ()> onSignOutRequest; void handleAvatarChanged(const JID& jid); void setEnabled(bool enabled); + + boost::optional<XMPPRosterItem> getItem(const JID&) const; + std::set<String> getGroups() const; + + void setContactGroups(const JID& jid, const std::vector<String>& groups); + void updateItem(const XMPPRosterItem&); + private: void handleOnJIDAdded(const JID &jid); void handleRosterCleared(); diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript index b149cd2..c8314de 100644 --- a/Swift/Controllers/SConscript +++ b/Swift/Controllers/SConscript @@ -30,6 +30,7 @@ if env["SCONS_STAGE"] == "build" : "DiscoServiceWalker.cpp", "MainController.cpp", "ProfileController.cpp", + "ContactEditController.cpp", "RosterController.cpp", "RosterGroupExpandinessPersister.cpp", "EventWindowController.cpp", diff --git a/Swift/Controllers/UIEvents/RegroupRosterItemUIEvent.h b/Swift/Controllers/UIEvents/RegroupRosterItemUIEvent.h deleted file mode 100644 index b8552b3..0000000 --- a/Swift/Controllers/UIEvents/RegroupRosterItemUIEvent.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2010 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#pragma once - -#include <boost/shared_ptr.hpp> -#include <vector> - -#include "Swift/Controllers/UIEvents/UIEvent.h" -#include "Swiften/MUC/MUCBookmark.h" - -namespace Swift { - /** - * An event for regrouping a roster item. - * This doesn't need to cover all groups, so it's valid to have the - * contact in several groups that are neither removedGroups or addedGroups. - */ - class RegroupRosterItemUIEvent : public UIEvent { - public: - RegroupRosterItemUIEvent(const JID& jid, const std::vector<String>& addedGroups, const std::vector<String>& removedGroups) : jid_(jid), addedGroups_(addedGroups), removedGroups_(removedGroups) {} - - const JID& getJID() const {return jid_;} - const std::vector<String>& getAddedGroups() const {return addedGroups_;} - const std::vector<String>& getRemovedGroups() const {return removedGroups_;} - - private: - JID jid_; - std::vector<String> addedGroups_; - std::vector<String> removedGroups_; - }; -} diff --git a/Swift/Controllers/UIEvents/RenameGroupUIEvent.h b/Swift/Controllers/UIEvents/RenameGroupUIEvent.h new file mode 100644 index 0000000..1825d77 --- /dev/null +++ b/Swift/Controllers/UIEvents/RenameGroupUIEvent.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIEvents/UIEvent.h> +#include <Swiften/Base/String.h> + +namespace Swift { + class RenameGroupUIEvent : public UIEvent { + public: + RenameGroupUIEvent(const String& group, const String& newName) : group(group), newName(newName) { + } + + const String& getGroup() const { + return group; + } + + const String& getNewName() const { + return newName; + } + + private: + String group; + String newName; + }; +} diff --git a/Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h b/Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h new file mode 100644 index 0000000..8d04525 --- /dev/null +++ b/Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/JID/JID.h> +#include <Swift/Controllers/UIEvents/UIEvent.h> + +namespace Swift { + class RequestContactEditorUIEvent : public UIEvent { + public: + typedef boost::shared_ptr<RequestContactEditorUIEvent> ref; + + RequestContactEditorUIEvent(const JID& jid) : jid(jid) { + } + + const JID& getJID() const { + return jid; + } + + private: + JID jid; + }; +} diff --git a/Swift/Controllers/UIInterfaces/ContactEditWindow.h b/Swift/Controllers/UIInterfaces/ContactEditWindow.h new file mode 100644 index 0000000..3feb718 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/ContactEditWindow.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/boost_bsignals.h> +#include <boost/shared_ptr.hpp> +#include <set> +#include <vector> + +#include <Swiften/Base/String.h> + +namespace Swift { + class JID; + + class ContactEditWindow { + public: + virtual ~ContactEditWindow() {}; + + virtual void setEnabled(bool b) = 0; + + virtual void setContact(const JID& jid, const String& name, const std::vector<String>& groups, const std::set<String>& allGroups) = 0; + + virtual void show() = 0; + virtual void hide() = 0; + + boost::signal<void ()> onRemoveContactRequest; + boost::signal<void (const String& /* name */, const std::vector<String>& /* groups */)> onChangeContactRequest; + }; +} diff --git a/Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h b/Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h new file mode 100644 index 0000000..8ad56c0 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIInterfaces/ContactEditWindow.h> + +namespace Swift { + class ContactEditWindowFactory { + public: + virtual ~ContactEditWindowFactory() {}; + + virtual ContactEditWindow* createContactEditWindow() = 0; + }; +} diff --git a/Swift/Controllers/UIInterfaces/UIFactory.h b/Swift/Controllers/UIInterfaces/UIFactory.h index 11623d7..9b36ac5 100644 --- a/Swift/Controllers/UIInterfaces/UIFactory.h +++ b/Swift/Controllers/UIInterfaces/UIFactory.h @@ -16,6 +16,7 @@ #include <Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h> #include <Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h> #include <Swift/Controllers/UIInterfaces/ProfileWindowFactory.h> +#include <Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h> namespace Swift { class UIFactory : @@ -28,7 +29,8 @@ namespace Swift { public XMLConsoleWidgetFactory, public UserSearchWindowFactory, public JoinMUCWindowFactory, - public ProfileWindowFactory { + public ProfileWindowFactory, + public ContactEditWindowFactory { public: virtual ~UIFactory() {} }; diff --git a/Swift/QtUI/ChatList/QtChatListWindow.h b/Swift/QtUI/ChatList/QtChatListWindow.h index b389474..3a3e95f 100644 --- a/Swift/QtUI/ChatList/QtChatListWindow.h +++ b/Swift/QtUI/ChatList/QtChatListWindow.h @@ -12,7 +12,6 @@ #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "Swift/QtUI/ChatList/ChatListModel.h" #include "Swift/QtUI/ChatList/ChatListDelegate.h" -#include "Swift/QtUI/ContextMenus/QtContextMenu.h" namespace Swift { @@ -34,13 +33,13 @@ namespace Swift { protected: void contextMenuEvent(QContextMenuEvent* event); + private: void setupContextMenus(); bool bookmarksEnabled_; UIEventStream* eventStream_; ChatListModel* model_; ChatListDelegate* delegate_; - QtContextMenu* contextMenu_; QMenu* mucMenu_; QMenu* emptyMenu_; ChatListItem* contextMenuItem_; diff --git a/Swift/QtUI/ContextMenus/QtContextMenu.cpp b/Swift/QtUI/ContextMenus/QtContextMenu.cpp deleted file mode 100644 index c74fb31..0000000 --- a/Swift/QtUI/ContextMenus/QtContextMenu.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2010 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#include "ContextMenus/QtContextMenu.h" - -namespace Swift { - -QtContextMenu::~QtContextMenu() { -} - -} diff --git a/Swift/QtUI/ContextMenus/QtContextMenu.h b/Swift/QtUI/ContextMenus/QtContextMenu.h deleted file mode 100644 index 9e73ef9..0000000 --- a/Swift/QtUI/ContextMenus/QtContextMenu.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2010 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#pragma once - -namespace Swift { - class RosterItem; - class QtContextMenu { - public: - virtual ~QtContextMenu(); - - virtual void show(RosterItem* item) = 0; - }; -} diff --git a/Swift/QtUI/ContextMenus/QtRosterContextMenu.cpp b/Swift/QtUI/ContextMenus/QtRosterContextMenu.cpp deleted file mode 100644 index 9bbbbca..0000000 --- a/Swift/QtUI/ContextMenus/QtRosterContextMenu.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2010 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#include "Swift/QtUI/ContextMenus/QtRosterContextMenu.h" - -#include <QInputDialog> -#include <QLineEdit> -#include <QMenu> -#include <QDebug> -#include <QDialog> -#include <QMessageBox> - -#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, QtTreeWidget* treeWidget) : eventStream_(eventStream), treeWidget_(treeWidget), item_(NULL) { -} - -void QtRosterContextMenu::show(RosterItem* item) { - ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); - item_ = item; - QMenu contextMenu; - if (contact) { - contextMenu.addAction("Rename", this, SLOT(handleRenameContact())); - contextMenu.addAction("Groups", this, SLOT(handleRegroupContact())); - contextMenu.addSeparator(); - contextMenu.addAction("Remove", this, SLOT(handleRemoveContact())); - } - GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item); - if (group) { - contextMenu.addAction("Rename", this, SLOT(handleRenameGroup())); - } - 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() { - ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item_); - assert(contact); - QMessageBox msgBox; - msgBox.setWindowTitle("Confirm contact deletion"); - msgBox.setText("Are you sure you want to delete this contact?"); - msgBox.setInformativeText(QString("This will remove the contact '%1' from all groups they may be in.").arg(P2QSTRING(contact->getJID().toString()))); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::Yes); - int ret = msgBox.exec(); - if (ret == QMessageBox::Yes) { - eventStream_->send(boost::shared_ptr<UIEvent>(new RemoveRosterItemUIEvent(contact->getJID()))); - } -} - -void QtRosterContextMenu::handleRenameContact() { - ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item_); - assert(contact); - bool ok; - QString newName = QInputDialog::getText(NULL, "Rename contact", "New name for " + P2QSTRING(item_->getDisplayName()), QLineEdit::Normal, P2QSTRING(item_->getDisplayName()), &ok); - if (ok) { - eventStream_->send(boost::shared_ptr<UIEvent>(new RenameRosterItemUIEvent(contact->getJID(), Q2PSTRING(newName)))); - } -} - -void QtRosterContextMenu::handleRenameGroup() { - GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item_); - assert(group); - bool ok; - QString newName = QInputDialog::getText(NULL, "Rename group", "New name for " + P2QSTRING(item_->getDisplayName()), QLineEdit::Normal, P2QSTRING(item_->getDisplayName()), &ok); - if (ok) { - std::vector<String> addedGroups; - std::vector<String> removedGroups; - addedGroups.push_back(Q2PSTRING(newName)); - removedGroups.push_back(group->getDisplayName()); - foreach (RosterItem* child, group->getChildren()) { - ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(child); - assert(contact); - boost::shared_ptr<RegroupRosterItemUIEvent> regroupItem(new RegroupRosterItemUIEvent(contact->getJID(), addedGroups, removedGroups)); - eventStream_->send(regroupItem); - } - } -} - -} diff --git a/Swift/QtUI/ContextMenus/QtRosterContextMenu.h b/Swift/QtUI/ContextMenus/QtRosterContextMenu.h deleted file mode 100644 index 0a88a8e..0000000 --- a/Swift/QtUI/ContextMenus/QtRosterContextMenu.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2010 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#pragma once - -#include <QObject> - -#include "Swift/QtUI/ContextMenus/QtContextMenu.h" -#include "Swift/QtUI/Roster/QtTreeWidget.h" -#include "Swift/Controllers/UIEvents/UIEventStream.h" - -namespace Swift { - class RosterItem; - class QtRosterContextMenu : public QObject, public QtContextMenu { - Q_OBJECT - public: - QtRosterContextMenu(UIEventStream* eventStream, QtTreeWidget* treeWidget); - void show(RosterItem* item); - - private slots: - void handleRemoveContact(); - void handleRenameContact(); - void handleRenameGroup(); - void handleRegroupContact(); - - private: - UIEventStream* eventStream_; - QtTreeWidget* treeWidget_; - RosterItem* item_; - }; -} diff --git a/Swift/QtUI/QtContactEditWindow.cpp b/Swift/QtUI/QtContactEditWindow.cpp new file mode 100644 index 0000000..6eb4316 --- /dev/null +++ b/Swift/QtUI/QtContactEditWindow.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "QtContactEditWindow.h" + +#include <algorithm> + +#include <QScrollArea> +#include <QBoxLayout> +#include <QLabel> +#include <QCheckBox> +#include <QLineEdit> +#include <QDialogButtonBox> +#include <QMessageBox> +#include <QPushButton> + +#include "Swift/QtUI/QtSwiftUtil.h" + +namespace Swift { + +QtContactEditWindow::QtContactEditWindow() : groups_(NULL) { + resize(300,300); + setWindowTitle("Edit contact"); + + QBoxLayout* layout = new QVBoxLayout(this); + setContentsMargins(0,0,0,0); + + jidLabel_ = new QLabel(this); + jidLabel_->setAlignment(Qt::AlignHCenter); + layout->addWidget(jidLabel_); + + QHBoxLayout* nameLayout = new QHBoxLayout(); + + QLabel* label = new QLabel("Name:", this); + nameLayout->addWidget(label); + name_ = new QLineEdit(this); + nameLayout->addWidget(name_); + layout->addLayout(nameLayout); + + layout->addWidget(new QLabel("Groups:", this)); + + groupsArea_ = new QScrollArea(this); + layout->addWidget(groupsArea_); + groupsArea_->setWidgetResizable(true); + groupsArea_->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + groupsArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + QHBoxLayout* buttonLayout = new QHBoxLayout(); + layout->addLayout(buttonLayout); + QPushButton* removeButton = new QPushButton("Remove contact", this); + connect(removeButton, SIGNAL(clicked()), this, SLOT(handleRemoveContact())); + buttonLayout->addWidget(removeButton); + QPushButton* okButton = new QPushButton("Ok", this); + connect(okButton, SIGNAL(clicked()), this, SLOT(handleUpdateContact())); + buttonLayout->addStretch(); + buttonLayout->addWidget(okButton); +} + +void QtContactEditWindow::setContact(const JID& jid, const String& name, const std::vector<String>& groups, const std::set<String>& allGroups) { + jid_ = jid; + + jidLabel_->setText("<b>" + P2QSTRING(jid.toString()) + "</b>"); + name_->setText(P2QSTRING(name)); + + delete groups_; + checkBoxes_.clear(); + groups_ = new QWidget(groupsArea_); + groupsArea_->setWidget(groups_); + QVBoxLayout* scrollLayout = new QVBoxLayout(groups_); + + foreach (String group, allGroups) { + QCheckBox* check = new QCheckBox(groups_); + check->setText(P2QSTRING(group)); + check->setCheckState(Qt::Unchecked); + checkBoxes_[group] = check; + scrollLayout->addWidget(check); + } + foreach (String group, groups) { + checkBoxes_[group]->setCheckState(Qt::Checked); + } + + QHBoxLayout* newGroupLayout = new QHBoxLayout(); + newGroup_ = new QCheckBox(groups_); + newGroup_->setText("New Group:"); + newGroup_->setCheckState(Qt::Unchecked); + newGroupLayout->addWidget(newGroup_); + newGroupName_ = new QLineEdit(groups_); + newGroupLayout->addWidget(newGroupName_); + scrollLayout->addLayout(newGroupLayout); + + scrollLayout->addItem(new QSpacerItem(20, 73, QSizePolicy::Minimum, QSizePolicy::Expanding)); +} + +void QtContactEditWindow::setEnabled(bool b) { + QWidget::setEnabled(b); +} + +void QtContactEditWindow::show() { + QWidget::show(); + QWidget::activateWindow(); +} + +void QtContactEditWindow::hide() { + QWidget::hide(); +} + +void QtContactEditWindow::handleRemoveContact() { + QMessageBox msgBox; + msgBox.setWindowTitle("Confirm contact deletion"); + msgBox.setText("Are you sure you want to delete this contact?"); + msgBox.setInformativeText(QString("This will remove the contact '%1' from all groups they may be in.").arg(P2QSTRING(jid_.toString()))); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + int ret = msgBox.exec(); + if (ret == QMessageBox::Yes) { + onRemoveContactRequest(); + } +} + +void QtContactEditWindow::handleUpdateContact() { + std::vector<String> groups; + foreach(const CheckBoxMap::value_type& group, checkBoxes_) { + if (group.second->checkState() == Qt::Checked) { + groups.push_back(group.first); + } + } + if (newGroup_->checkState() == Qt::Checked && !newGroupName_->text().isEmpty()) { + groups.push_back(Q2PSTRING(newGroupName_->text())); + } + onChangeContactRequest(Q2PSTRING(name_->text()), groups); +} + +} diff --git a/Swift/QtUI/QtContactEditWindow.h b/Swift/QtUI/QtContactEditWindow.h new file mode 100644 index 0000000..a731480 --- /dev/null +++ b/Swift/QtUI/QtContactEditWindow.h @@ -0,0 +1,52 @@ +/* + * 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 <QWidget> + +#include <Swift/Controllers/UIInterfaces/ContactEditWindow.h> +#include <Swiften/Base/String.h> +#include <Swiften/JID/JID.h> + +class QScrollArea; +class QLabel; +class QLineEdit; +class QCheckBox; + +namespace Swift { + class QtContactEditWindow : public QWidget, public ContactEditWindow { + Q_OBJECT + + public: + QtContactEditWindow(); + + virtual void setContact(const JID& jid, const String& name, const std::vector<String>& groups, const std::set<String>& allGroups); + + void setEnabled(bool); + void show(); + void hide(); + + private slots: + void handleRemoveContact(); + void handleUpdateContact(); + + private: + typedef std::map<String, QCheckBox*> CheckBoxMap; + JID jid_; + QLabel* jidLabel_; + CheckBoxMap checkBoxes_; + QLineEdit* name_; + QScrollArea* groupsArea_; + QWidget* groups_; + QCheckBox* newGroup_; + QLineEdit* newGroupName_; + }; +} + diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index ccf5a59..63ce32b 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -59,8 +59,6 @@ QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventS contactTabLayout->setContentsMargins(0, 0, 0, 0); treeWidget_ = new QtTreeWidget(uiEventStream_); - contextMenu_ = new QtRosterContextMenu(uiEventStream_, treeWidget_); - treeWidget_->setContextMenu(contextMenu_); contactTabLayout->addWidget(treeWidget_); tabs_->addTab(contactsTabWidget_, "&Contacts"); @@ -108,7 +106,6 @@ QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventS QtMainWindow::~QtMainWindow() { uiEventStream_->onUIEvent.disconnect(boost::bind(&QtMainWindow::handleUIEvent, this, _1)); - delete contextMenu_; } QtEventWindow* QtMainWindow::getEventWindow() { diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h index 67b89d8..66b0caf 100644 --- a/Swift/QtUI/QtMainWindow.h +++ b/Swift/QtUI/QtMainWindow.h @@ -4,8 +4,7 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#ifndef SWIFT_QtMainWindow_H -#define SWIFT_QtMainWindow_H +#pragma once #include <QWidget> #include <QMenu> @@ -13,7 +12,6 @@ #include "Swift/QtUI/QtRosterHeader.h" #include "Swift/QtUI/EventViewer/QtEventWindow.h" #include "Swift/QtUI/ChatList/QtChatListWindow.h" -#include "Swift/QtUI/ContextMenus/QtRosterContextMenu.h" #include <vector> @@ -73,10 +71,6 @@ namespace Swift { QtEventWindow* eventWindow_; QtChatListWindow* chatListWindow_; UIEventStream* uiEventStream_; - QtRosterContextMenu* contextMenu_; bool lastOfflineState_; }; } - -#endif - diff --git a/Swift/QtUI/QtSetGroupsDialog.cpp b/Swift/QtUI/QtSetGroupsDialog.cpp deleted file mode 100644 index d19a55d..0000000 --- a/Swift/QtUI/QtSetGroupsDialog.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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) { - //resize(300,300); - setWindowTitle("Edit contact"); - QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, this); - setContentsMargins(0,0,0,0); - QScrollArea* scrollArea = new QScrollArea(this); - layout->addWidget(scrollArea); - scrollArea->setWidgetResizable(true); - scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - QWidget* scroll = new QWidget(scrollArea); - scrollArea->setWidget(scroll); - QVBoxLayout* scrollLayout = new QVBoxLayout(scroll); - QLabel* label = new QLabel(scroll); - label->setText("Choose groups for " + P2QSTRING(contact->getDisplayName()) + " (" + P2QSTRING(contact->getJID().toString()) + ")"); - 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); - } - - QHBoxLayout* newGroupLayout = new QHBoxLayout(); - newGroup_ = new QCheckBox(scroll); - newGroup_->setText("New Group:"); - newGroup_->setCheckState(Qt::Unchecked); - newGroupLayout->addWidget(newGroup_); - newGroupName_ = new QLineEdit(scroll); - newGroupLayout->addWidget(newGroupName_); - scrollLayout->addLayout(newGroupLayout); - - scrollLayout->addItem(new QSpacerItem(20, 73, QSizePolicy::Minimum, QSizePolicy::Expanding)); - - 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 && pair.first != "Contacts") { - 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 deleted file mode 100644 index e8300f5..0000000 --- a/Swift/QtUI/QtSetGroupsDialog.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp index 35dc4ea..4832205 100644 --- a/Swift/QtUI/QtUIFactory.cpp +++ b/Swift/QtUI/QtUIFactory.cpp @@ -22,6 +22,7 @@ #include "MUCSearch/QtMUCSearchWindow.h" #include "UserSearch/QtUserSearchWindow.h" #include "QtProfileWindow.h" +#include "QtContactEditWindow.h" namespace Swift { @@ -94,4 +95,9 @@ ProfileWindow* QtUIFactory::createProfileWindow() { return new QtProfileWindow(); } +ContactEditWindow* QtUIFactory::createContactEditWindow() { + return new QtContactEditWindow(); +} + + } diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h index 199cebf..aa88fa3 100644 --- a/Swift/QtUI/QtUIFactory.h +++ b/Swift/QtUI/QtUIFactory.h @@ -36,6 +36,7 @@ namespace Swift { virtual UserSearchWindow* createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream); virtual JoinMUCWindow* createJoinMUCWindow(); virtual ProfileWindow* createProfileWindow(); + virtual ContactEditWindow* createContactEditWindow(); private slots: void handleLoginWindowGeometryChanged(); diff --git a/Swift/QtUI/Roster/QtTreeWidget.cpp b/Swift/QtUI/Roster/QtTreeWidget.cpp index cecb557..3c0b303 100644 --- a/Swift/QtUI/Roster/QtTreeWidget.cpp +++ b/Swift/QtUI/Roster/QtTreeWidget.cpp @@ -6,15 +6,19 @@ #include "Roster/QtTreeWidget.h" +#include <QMenu> +#include <QContextMenuEvent> +#include <QInputDialog> +#include <boost/smart_ptr/make_shared.hpp> + #include "Swiften/Base/Platform.h" #include "Swiften/Roster/ContactRosterItem.h" #include "Swiften/Roster/GroupRosterItem.h" #include "Swift/Controllers/UIEvents/UIEventStream.h" #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h" - -#include <qdebug.h> -#include <QMenu> -#include <QContextMenuEvent> +#include "Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h" +#include "Swift/Controllers/UIEvents/RenameGroupUIEvent.h" +#include "QtSwiftUtil.h" namespace Swift { @@ -25,7 +29,6 @@ QtTreeWidget::QtTreeWidget(UIEventStream* eventStream, QWidget* parent) : QTreeV delegate_ = new RosterDelegate(this); setItemDelegate(delegate_); setHeaderHidden(true); - contextMenu_ = NULL; #ifdef SWIFT_PLATFORM_MACOSX setAlternatingRowColors(true); #endif @@ -52,10 +55,6 @@ void QtTreeWidget::setRosterModel(Roster* roster) { expandAll(); } -void QtTreeWidget::setContextMenu(QtContextMenu* contextMenu) { - contextMenu_ = contextMenu; -} - QtTreeWidgetItem* QtTreeWidget::getRoot() { return treeRoot_; } @@ -77,13 +76,29 @@ void QtTreeWidget::handleItemActivated(const QModelIndex& index) { } void QtTreeWidget::contextMenuEvent(QContextMenuEvent* event) { - if (!contextMenu_) { + QModelIndex index = indexAt(event->pos()); + if (!index.isValid()) { return; } - QModelIndex index = indexAt(event->pos()); - RosterItem* item = index.isValid() ? static_cast<RosterItem*>(index.internalPointer()) : NULL; - if (item) { - contextMenu_->show(item); + RosterItem* item = static_cast<RosterItem*>(index.internalPointer()); + QMenu contextMenu; + if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) { + QAction* editContact = contextMenu.addAction("Edit"); + QAction* result = contextMenu.exec(event->globalPos()); + if (result == editContact) { + eventStream_->send(boost::make_shared<RequestContactEditorUIEvent>(contact->getJID())); + } + } + else if (GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item)) { + QAction* renameGroup = contextMenu.addAction("Rename"); + QAction* result = contextMenu.exec(event->globalPos()); + if (result == renameGroup) { + bool ok; + QString newName = QInputDialog::getText(NULL, "Rename group", "New name for " + P2QSTRING(group->getDisplayName()), QLineEdit::Normal, P2QSTRING(group->getDisplayName()), &ok); + if (ok) { + eventStream_->send(boost::make_shared<RenameGroupUIEvent>(group->getDisplayName(), Q2PSTRING(newName))); + } + } } } diff --git a/Swift/QtUI/Roster/QtTreeWidget.h b/Swift/QtUI/Roster/QtTreeWidget.h index 838c453..ff11567 100644 --- a/Swift/QtUI/Roster/QtTreeWidget.h +++ b/Swift/QtUI/Roster/QtTreeWidget.h @@ -12,7 +12,6 @@ #include "Swift/QtUI/Roster/QtTreeWidget.h" #include "Swift/QtUI/Roster/RosterModel.h" #include "Swift/QtUI/Roster/RosterDelegate.h" -#include "Swift/QtUI/ContextMenus/QtContextMenu.h" namespace Swift { class UIEventStream; @@ -24,7 +23,6 @@ class QtTreeWidget : public QTreeView{ ~QtTreeWidget(); void show(); QtTreeWidgetItem* getRoot(); - void setContextMenu(QtContextMenu* contextMenu); void setRosterModel(Roster* roster); Roster* getRoster() {return roster_;} private slots: @@ -42,7 +40,6 @@ class QtTreeWidget : public QTreeView{ Roster* roster_; RosterDelegate* delegate_; QtTreeWidgetItem* treeRoot_; - QtContextMenu* contextMenu_; UIEventStream* eventStream_; }; diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index b16fb7d..d27dc8c 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -90,7 +90,7 @@ sources = [ "QtBookmarkDetailWindow.cpp", "QtAddBookmarkWindow.cpp", "QtEditBookmarkWindow.cpp", - "QtSetGroupsDialog.cpp", + "QtContactEditWindow.cpp", "ChatSnippet.cpp", "MessageSnippet.cpp", "SystemMessageSnippet.cpp", @@ -120,8 +120,6 @@ sources = [ "UserSearch/QtUserSearchWindow.cpp", "UserSearch/UserSearchModel.cpp", "UserSearch/UserSearchDelegate.cpp", - "ContextMenus/QtRosterContextMenu.cpp", - "ContextMenus/QtContextMenu.cpp", "QtSubscriptionRequestWindow.cpp", "QtRosterHeader.cpp", "QtWebView.cpp", diff --git a/Swiften/Roster/XMPPRoster.h b/Swiften/Roster/XMPPRoster.h index b88148d..676e8f9 100644 --- a/Swiften/Roster/XMPPRoster.h +++ b/Swiften/Roster/XMPPRoster.h @@ -6,12 +6,15 @@ #pragma once +#include <boost/optional.hpp> +#include <vector> +#include <set> +#include "Swiften/Base/boost_bsignals.h" + #include "Swiften/Base/String.h" #include "Swiften/JID/JID.h" #include "Swiften/Elements/RosterItemPayload.h" - -#include <vector> -#include "Swiften/Base/boost_bsignals.h" +#include <Swiften/Roster/XMPPRosterItem.h> namespace Swift { /** @@ -43,7 +46,22 @@ namespace Swift { /** * Returns the list of groups for the given JID. */ - virtual const std::vector<String>& getGroupsForJID(const JID& jid) = 0; + virtual std::vector<String> getGroupsForJID(const JID& jid) = 0; + + /** + * Retrieve the items in the roster. + */ + virtual std::vector<XMPPRosterItem> getItems() const = 0; + + /** + * Retrieve the item with the given JID. + */ + virtual boost::optional<XMPPRosterItem> getItem(const JID&) const = 0; + + /** + * Retrieve the list of (existing) groups. + */ + virtual std::set<String> getGroups() const = 0; public: /** diff --git a/Swiften/Roster/XMPPRosterImpl.cpp b/Swiften/Roster/XMPPRosterImpl.cpp index 762ae29..3e9e312 100644 --- a/Swiften/Roster/XMPPRosterImpl.cpp +++ b/Swiften/Roster/XMPPRosterImpl.cpp @@ -4,7 +4,8 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "Swiften/Roster/XMPPRosterImpl.h" +#include <Swiften/Roster/XMPPRosterImpl.h> +#include <Swiften/Base/foreach.h> namespace Swift { @@ -13,21 +14,15 @@ XMPPRosterImpl::XMPPRosterImpl() { void XMPPRosterImpl::addContact(const JID& jid, const String& name, const std::vector<String>& groups, RosterItemPayload::Subscription subscription) { JID bareJID(jid.toBare()); - bool exists = containsJID(bareJID); - String oldName = getNameForJID(bareJID); - std::vector<String> oldGroups = entries_[bareJID].groups; - if (exists) { - entries_.erase(bareJID); - } - XMPPRosterItem item; - item.groups = groups; - item.name = name; - item.jid = jid; - item.subscription = subscription; - entries_[bareJID] = item; - if (exists) { + std::map<JID, XMPPRosterItem>::iterator i = entries_.find(bareJID); + if (i != entries_.end()) { + String oldName = i->second.getName(); + std::vector<String> oldGroups = i->second.getGroups(); + i->second = XMPPRosterItem(jid, name, groups, subscription); onJIDUpdated(bareJID, oldName, oldGroups); - } else { + } + else { + entries_.insert(std::make_pair(bareJID, XMPPRosterItem(jid, name, groups, subscription))); onJIDAdded(bareJID); } } @@ -49,19 +44,58 @@ bool XMPPRosterImpl::containsJID(const JID& jid) { String XMPPRosterImpl::getNameForJID(const JID& jid) const { std::map<JID, XMPPRosterItem>::const_iterator i = entries_.find(jid.toBare()); if (i != entries_.end()) { - return i->second.name; + return i->second.getName(); } else { return ""; } } -const std::vector<String>& XMPPRosterImpl::getGroupsForJID(const JID& jid) { - return entries_[JID(jid.toBare())].groups; +std::vector<String> XMPPRosterImpl::getGroupsForJID(const JID& jid) { + std::map<JID, XMPPRosterItem>::iterator i = entries_.find(jid.toBare()); + if (i != entries_.end()) { + return i->second.getGroups(); + } + else { + return std::vector<String>(); + } } RosterItemPayload::Subscription XMPPRosterImpl::getSubscriptionStateForJID(const JID& jid) { - return entries_[JID(jid.toBare())].subscription; + std::map<JID, XMPPRosterItem>::iterator i = entries_.find(jid.toBare()); + if (i != entries_.end()) { + return i->second.getSubscription(); + } + else { + return RosterItemPayload::None; + } +} + +std::vector<XMPPRosterItem> XMPPRosterImpl::getItems() const { + std::vector<XMPPRosterItem> result; + foreach(const RosterMap::value_type& entry, entries_) { + result.push_back(entry.second); + } + return result; +} + +boost::optional<XMPPRosterItem> XMPPRosterImpl::getItem(const JID& jid) const { + std::map<JID, XMPPRosterItem>::const_iterator i = entries_.find(jid.toBare()); + if (i != entries_.end()) { + return i->second; + } + else { + return boost::optional<XMPPRosterItem>(); + } +} + +std::set<String> XMPPRosterImpl::getGroups() const { + std::set<String> result; + foreach(const RosterMap::value_type& entry, entries_) { + std::vector<String> groups = entry.second.getGroups(); + result.insert(groups.begin(), groups.end()); + } + return result; } } diff --git a/Swiften/Roster/XMPPRosterImpl.h b/Swiften/Roster/XMPPRosterImpl.h index c2d2458..f65683f 100644 --- a/Swiften/Roster/XMPPRosterImpl.h +++ b/Swiften/Roster/XMPPRosterImpl.h @@ -7,6 +7,7 @@ #pragma once #include <map> +#include <set> #include "Swiften/Roster/XMPPRoster.h" @@ -22,15 +23,14 @@ namespace Swift { bool containsJID(const JID& jid); RosterItemPayload::Subscription getSubscriptionStateForJID(const JID& jid); String getNameForJID(const JID& jid) const; - const std::vector<String>& getGroupsForJID(const JID& jid); + std::vector<String> getGroupsForJID(const JID& jid); + + virtual std::vector<XMPPRosterItem> getItems() const; + virtual boost::optional<XMPPRosterItem> getItem(const JID&) const; + virtual std::set<String> getGroups() const; private: - struct XMPPRosterItem { - JID jid; - String name; - std::vector<String> groups; - RosterItemPayload::Subscription subscription; - }; - std::map<JID, XMPPRosterItem> entries_; + typedef std::map<JID, XMPPRosterItem> RosterMap; + RosterMap entries_; }; } diff --git a/Swiften/Roster/XMPPRosterItem.h b/Swiften/Roster/XMPPRosterItem.h new file mode 100644 index 0000000..ceb7763 --- /dev/null +++ b/Swiften/Roster/XMPPRosterItem.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + + +#include <vector> + +#include <Swiften/Base/String.h> +#include <Swiften/JID/JID.h> +#include <Swiften/Elements/RosterItemPayload.h> + +namespace Swift { + class XMPPRosterItem { + public: + XMPPRosterItem(const JID& jid, const String& name, const std::vector<String>& groups, RosterItemPayload::Subscription subscription) : jid(jid), name(name), groups(groups), subscription(subscription) { + } + + const JID& getJID() const { + return jid; + } + + const String& getName() const { + return name; + } + + void setName(const String& name) { + this->name = name; + } + + const std::vector<String>& getGroups() const { + return groups; + } + + void setGroups(const std::vector<String>& groups) { + this->groups = groups; + } + + RosterItemPayload::Subscription getSubscription() const { + return subscription; + } + + private: + JID jid; + String name; + std::vector<String> groups; + RosterItemPayload::Subscription subscription; + }; +} + |