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-afcfa9dd33cfb5e36edf7d8148a7f8b24c976741.zip swift-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; +	}; +} + | 
 Swift
 Swift