diff options
Diffstat (limited to 'Swift')
43 files changed, 1039 insertions, 114 deletions
| diff --git a/Swift/Controllers/BlockListController.cpp b/Swift/Controllers/BlockListController.cpp new file mode 100644 index 0000000..e7bc45d --- /dev/null +++ b/Swift/Controllers/BlockListController.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2013 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <Swift/Controllers/BlockListController.h> + +#include <boost/bind.hpp> + +#include <Swiften/Client/ClientBlockListManager.h> + +#include <Swiften/Base/foreach.h> +#include <Swiften/Base/format.h> +#include <Swift/Controllers/Intl.h> +#include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h> +#include <Swift/Controllers/XMPPEvents/ErrorEvent.h> +#include <Swift/Controllers/UIInterfaces/BlockListEditorWidget.h> +#include <Swift/Controllers/XMPPEvents/EventController.h> + +namespace Swift { + +BlockListController::BlockListController(ClientBlockListManager* blockListManager, UIEventStream* uiEventStream, BlockListEditorWidgetFactory* blockListEditorWidgetFactory, EventController* eventController) : blockListManager_(blockListManager), blockListEditorWidgetFactory_(blockListEditorWidgetFactory), blockListEditorWidget_(0), eventController_(eventController), remainingRequests_(0) { +	uiEventStream->onUIEvent.connect(boost::bind(&BlockListController::handleUIEvent, this, _1)); +	blockListManager_->getBlockList()->onItemAdded.connect(boost::bind(&BlockListController::handleBlockListChanged, this)); +	blockListManager_->getBlockList()->onItemRemoved.connect(boost::bind(&BlockListController::handleBlockListChanged, this)); +} + +BlockListController::~BlockListController() { +	blockListManager_->getBlockList()->onItemAdded.disconnect(boost::bind(&BlockListController::handleBlockListChanged, this)); +	blockListManager_->getBlockList()->onItemRemoved.disconnect(boost::bind(&BlockListController::handleBlockListChanged, this)); +} + +void BlockListController::blockListDifferences(const std::vector<JID> &newBlockList, std::vector<JID> &jidsToUnblock, std::vector<JID> &jidsToBlock) const { +	foreach (const JID& jid, blockListBeforeEdit) { +		if (std::find(newBlockList.begin(), newBlockList.end(), jid) == newBlockList.end()) { +			jidsToUnblock.push_back(jid); +		} +	} + +	foreach (const JID& jid, newBlockList) { +		if (std::find(blockListBeforeEdit.begin(), blockListBeforeEdit.end(), jid) == blockListBeforeEdit.end()) { +			jidsToBlock.push_back(jid); +		} +	} +} + +void BlockListController::handleUIEvent(boost::shared_ptr<UIEvent> rawEvent) { +	// handle UI dialog +	boost::shared_ptr<RequestBlockListDialogUIEvent> requestDialogEvent = boost::dynamic_pointer_cast<RequestBlockListDialogUIEvent>(rawEvent); +	if (requestDialogEvent != NULL) { +		if (blockListEditorWidget_ == NULL) { +			blockListEditorWidget_ = blockListEditorWidgetFactory_->createBlockListEditorWidget(); +			blockListEditorWidget_->onSetNewBlockList.connect(boost::bind(&BlockListController::handleSetNewBlockList, this, _1)); +		} +		blockListBeforeEdit = blockListManager_->getBlockList()->getItems(); +		blockListEditorWidget_->setCurrentBlockList(blockListBeforeEdit); +		blockListEditorWidget_->show(); +		return; +	} + +	// handle block state change +	boost::shared_ptr<RequestChangeBlockStateUIEvent> changeStateEvent = boost::dynamic_pointer_cast<RequestChangeBlockStateUIEvent>(rawEvent); +	if (changeStateEvent != NULL) { +		if (changeStateEvent->getBlockState() == RequestChangeBlockStateUIEvent::Blocked) { +			GenericRequest<BlockPayload>::ref blockRequest = blockListManager_->createBlockJIDRequest(changeStateEvent->getContact()); +			blockRequest->onResponse.connect(boost::bind(&BlockListController::handleBlockResponse, this, blockRequest, _1, _2, std::vector<JID>(1, changeStateEvent->getContact()), false)); +			blockRequest->send(); +		} else if (changeStateEvent->getBlockState() == RequestChangeBlockStateUIEvent::Unblocked) { +			GenericRequest<UnblockPayload>::ref unblockRequest = blockListManager_->createUnblockJIDRequest(changeStateEvent->getContact()); +			unblockRequest->onResponse.connect(boost::bind(&BlockListController::handleUnblockResponse, this, unblockRequest, _1, _2, std::vector<JID>(1, changeStateEvent->getContact()), false)); +			unblockRequest->send(); +		} +		return; +	} +} + +void BlockListController::handleBlockResponse(GenericRequest<BlockPayload>::ref request, boost::shared_ptr<BlockPayload>, ErrorPayload::ref error, const std::vector<JID>& jids, bool originEditor) { +	if (error) { +		std::string errorMessage; +		// FIXME: Handle reporting of list of JIDs in a translatable way. +		errorMessage = str(format(QT_TRANSLATE_NOOP("", "Failed to block %1%.")) % jids.at(0).toString()); +		if (!error->getText().empty()) { +			errorMessage = str(format(QT_TRANSLATE_NOOP("", "%1%: %2%.")) % errorMessage % error->getText()); +		} +		eventController_->handleIncomingEvent(boost::make_shared<ErrorEvent>(request->getReceiver(), errorMessage)); +	} +	if (originEditor) { +		remainingRequests_--; +		if (blockListEditorWidget_ && (remainingRequests_ == 0)) { +			blockListEditorWidget_->setBusy(false); +		} +	} +} + +void BlockListController::handleUnblockResponse(GenericRequest<UnblockPayload>::ref request, boost::shared_ptr<UnblockPayload>, ErrorPayload::ref error, const std::vector<JID>& jids, bool originEditor) { +	if (error) { +		std::string errorMessage; +		// FIXME: Handle reporting of list of JIDs in a translatable way. +		errorMessage = str(format(QT_TRANSLATE_NOOP("", "Failed to unblock %1%.")) % jids.at(0).toString()); +		if (!error->getText().empty()) { +			errorMessage = str(format(QT_TRANSLATE_NOOP("", "%1%: %2%.")) % errorMessage % error->getText()); +		} +		eventController_->handleIncomingEvent(boost::make_shared<ErrorEvent>(request->getReceiver(), errorMessage)); +	} +	if (originEditor) { +		remainingRequests_--; +		if (blockListEditorWidget_ && (remainingRequests_ == 0)) { +			blockListEditorWidget_->setBusy(false); +		} +	} +} + +void BlockListController::handleSetNewBlockList(const std::vector<JID> &newBlockList) { +	std::vector<JID> jidsToBlock; +	std::vector<JID> jidsToUnblock; + +	blockListDifferences(newBlockList, jidsToUnblock, jidsToBlock); + +	if (!jidsToBlock.empty()) { +		remainingRequests_++; +		GenericRequest<BlockPayload>::ref blockRequest = blockListManager_->createBlockJIDsRequest(jidsToBlock); +		blockRequest->onResponse.connect(boost::bind(&BlockListController::handleBlockResponse, this, blockRequest, _1, _2, jidsToBlock, true)); +		blockRequest->send(); +	} +	if (!jidsToUnblock.empty()) { +		remainingRequests_++; +		GenericRequest<UnblockPayload>::ref unblockRequest = blockListManager_->createUnblockJIDsRequest(jidsToUnblock); +		unblockRequest->onResponse.connect(boost::bind(&BlockListController::handleUnblockResponse, this, unblockRequest, _1, _2, jidsToUnblock, true)); +		unblockRequest->send(); +	} +	if (!jidsToBlock.empty() || jidsToUnblock.empty()) { +		assert(blockListEditorWidget_); +		blockListEditorWidget_->setBusy(true); +	} +} + +void BlockListController::handleBlockListChanged() { +	if (blockListEditorWidget_) { +		std::vector<JID> jidsToBlock; +		std::vector<JID> jidsToUnblock; + +		blockListDifferences(blockListEditorWidget_->getCurrentBlockList(), jidsToUnblock, jidsToBlock); +		blockListBeforeEdit = blockListManager_->getBlockList()->getItems(); + +		foreach (const JID& jid, jidsToBlock) { +			blockListBeforeEdit.push_back(jid); +		} + +		foreach (const JID& jid, jidsToUnblock) { +			blockListBeforeEdit.erase(std::remove(blockListBeforeEdit.begin(), blockListBeforeEdit.end(), jid), blockListBeforeEdit.end());; +		} + +		blockListEditorWidget_->setCurrentBlockList(blockListBeforeEdit); +	} +} + +} diff --git a/Swift/Controllers/BlockListController.h b/Swift/Controllers/BlockListController.h new file mode 100644 index 0000000..4c9caad --- /dev/null +++ b/Swift/Controllers/BlockListController.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <boost/shared_ptr.hpp> + +#include <Swiften/Queries/GenericRequest.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIInterfaces/BlockListEditorWidgetFactory.h> + +namespace Swift { + +class BlockPayload; +class UnblockPayload; +class ClientBlockListManager; +class EventController; + +class BlockListController { +public: +	BlockListController(ClientBlockListManager* blockListManager, UIEventStream* uiEventStream, BlockListEditorWidgetFactory* blockListEditorWidgetFactory, EventController* eventController); +	~BlockListController(); + +private: +	void blockListDifferences(const std::vector<JID> &newBlockList, std::vector<JID>& jidsToUnblock, std::vector<JID>& jidsToBlock) const; + +	void handleUIEvent(boost::shared_ptr<UIEvent> event); + +	void handleBlockResponse(GenericRequest<BlockPayload>::ref, boost::shared_ptr<BlockPayload>, ErrorPayload::ref error, const std::vector<JID>& jids, bool originEditor); +	void handleUnblockResponse(GenericRequest<UnblockPayload>::ref, boost::shared_ptr<UnblockPayload>, ErrorPayload::ref error, const std::vector<JID>& jids, bool originEditor); + +	void handleSetNewBlockList(const std::vector<JID>& newBlockList); + +	void handleBlockListChanged(); + +private: +	ClientBlockListManager* blockListManager_; +	BlockListEditorWidgetFactory* blockListEditorWidgetFactory_; +	BlockListEditorWidget* blockListEditorWidget_; +	EventController* eventController_; +	std::vector<JID> blockListBeforeEdit; +	int remainingRequests_; +}; + +} diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp index 3c0e933..f0de59c 100644 --- a/Swift/Controllers/Chat/ChatController.cpp +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -36,14 +36,15 @@  #include <Swift/Controllers/SettingConstants.h>  #include <Swift/Controllers/Highlighter.h>  #include <Swiften/Base/Log.h> +#include <Swiften/Client/ClientBlockListManager.h>  namespace Swift {  /**   * The controller does not gain ownership of the stanzaChannel, nor the factory.   */ -ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager) -	: ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry, highlightManager), eventStream_(eventStream), userWantsReceipts_(userWantsReceipts), settings_(settings) { +ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager) +	: ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry, highlightManager), eventStream_(eventStream), userWantsReceipts_(userWantsReceipts), settings_(settings), clientBlockListManager_(clientBlockListManager) {  	isInMUC_ = isInMUC;  	lastWasPresence_ = false;  	chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsProvider); @@ -145,6 +146,19 @@ void ChatController::setToJID(const JID& jid) {  	handleBareJIDCapsChanged(toJID_);  } +void ChatController::setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info) { +	ChatControllerBase::setAvailableServerFeatures(info); +	if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::BlockingCommandFeature)) { +		boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList(); + +		blockingOnStateChangedConnection_ = blockList->onStateChanged.connect(boost::bind(&ChatController::handleBlockingStateChanged, this)); +		blockingOnItemAddedConnection_ = blockList->onItemAdded.connect(boost::bind(&ChatController::handleBlockingItemAdded, this, _1)); +		blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&ChatController::handleBlockingItemRemoved, this, _1)); + +		handleBlockingStateChanged(); +	} +} +  bool ChatController::isIncomingMessageFromMe(boost::shared_ptr<Message>) {  	return false;  } @@ -216,6 +230,36 @@ void ChatController::checkForDisplayingDisplayReceiptsAlert() {  	}  } +void ChatController::handleBlockingStateChanged() { +	boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList(); +	if (blockList->getState() == BlockList::Available) { +		if (blockList->isBlocked(toJID_.toBare())) { +			chatWindow_->setAlert(QT_TRANSLATE_NOOP("", "You've currently blocked this contact. To continue your conversation you have to unblock the contact first.")); +			chatWindow_->setInputEnabled(false); +			chatWindow_->setBlockingState(ChatWindow::IsBlocked); +		} else { +			chatWindow_->setBlockingState(ChatWindow::IsUnblocked); +		} +	} +} + +void ChatController::handleBlockingItemAdded(const JID& jid) { +	if (jid == toJID_.toBare()) { +		chatWindow_->setAlert(QT_TRANSLATE_NOOP("", "You've currently blocked this contact. To continue your conversation you have to unblock the contact first.")); +		chatWindow_->setInputEnabled(false); +		chatWindow_->setBlockingState(ChatWindow::IsBlocked); +	} +} + +void ChatController::handleBlockingItemRemoved(const JID& jid) { +	if (jid == toJID_.toBare()) { +		// FIXME: Support for different types of alerts. +		chatWindow_->cancelAlert(); +		chatWindow_->setInputEnabled(true); +		chatWindow_->setBlockingState(ChatWindow::IsUnblocked); +	} +} +  void ChatController::postSendMessage(const std::string& body, boost::shared_ptr<Stanza> sentStanza) {  	boost::shared_ptr<Replace> replace = sentStanza->getPayload<Replace>();  	if (replace) { diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h index 6021ec1..4322240 100644 --- a/Swift/Controllers/Chat/ChatController.h +++ b/Swift/Controllers/Chat/ChatController.h @@ -23,12 +23,14 @@ namespace Swift {  	class SettingsProvider;  	class HistoryController;  	class HighlightManager; +	class ClientBlockListManager;  	class ChatController : public ChatControllerBase {  		public: -			ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager); +			ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager);  			virtual ~ChatController();  			virtual void setToJID(const JID& jid); +			virtual void setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info);  			virtual void setOnline(bool online);  			virtual void handleNewFileTransferController(FileTransferController* ftc);  			virtual void handleWhiteboardSessionRequest(bool senderIsSelf); @@ -67,6 +69,10 @@ namespace Swift {  			void handleSettingChanged(const std::string& settingPath);  			void checkForDisplayingDisplayReceiptsAlert(); +			void handleBlockingStateChanged(); +			void handleBlockingItemAdded(const JID&); +			void handleBlockingItemRemoved(const JID&); +  		private:  			NickResolver* nickResolver_;  			ChatStateNotifier* chatStateNotifier_; @@ -86,6 +92,11 @@ namespace Swift {  			std::map<std::string, FileTransferController*> ftControllers;  			SettingsProvider* settings_;  			std::string lastWbID_; + +			ClientBlockListManager* clientBlockListManager_; +			boost::bsignals::scoped_connection blockingOnStateChangedConnection_; +			boost::bsignals::scoped_connection blockingOnItemAddedConnection_; +			boost::bsignals::scoped_connection blockingOnItemRemovedConnection_;  	};  } diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h index baef9e6..84bd06a 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.h +++ b/Swift/Controllers/Chat/ChatControllerBase.h @@ -48,7 +48,7 @@ namespace Swift {  			virtual ~ChatControllerBase();  			void showChatWindow();  			void activateChatWindow(); -			void setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info); +			virtual void setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info);  			void handleIncomingMessage(boost::shared_ptr<MessageEvent> message);  			std::string addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight);  			void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight); diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index dba8565..d6010e9 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -43,6 +43,7 @@  #include <Swift/Controllers/SettingConstants.h>  #include <Swiften/Client/StanzaChannel.h>  #include <Swift/Controllers/WhiteboardManager.h> +#include <Swiften/Client/ClientBlockListManager.h>  namespace Swift { @@ -75,7 +76,8 @@ ChatsManager::ChatsManager(  		SettingsProvider* settings,  		HistoryController* historyController,  		WhiteboardManager* whiteboardManager, -		HighlightManager* highlightManager) : +		HighlightManager* highlightManager, +		ClientBlockListManager* clientBlockListManager) :  			jid_(jid),   			joinMUCWindowFactory_(joinMUCWindowFactory),   			useDelayForLatency_(useDelayForLatency),  @@ -88,7 +90,8 @@ ChatsManager::ChatsManager(  			settings_(settings),  			historyController_(historyController),  			whiteboardManager_(whiteboardManager), -			highlightManager_(highlightManager) { +			highlightManager_(highlightManager), +			clientBlockListManager_(clientBlockListManager) {  	timerFactory_ = timerFactory;  	eventController_ = eventController;  	stanzaChannel_ = stanzaChannel; @@ -523,7 +526,7 @@ ChatController* ChatsManager::getChatControllerOrFindAnother(const JID &contact)  ChatController* ChatsManager::createNewChatController(const JID& contact) {  	assert(chatControllers_.find(contact) == chatControllers_.end()); -	ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsProvider_, userWantsReceipts_, settings_, historyController_, mucRegistry_, highlightManager_); +	ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsProvider_, userWantsReceipts_, settings_, historyController_, mucRegistry_, highlightManager_, clientBlockListManager_);  	chatControllers_[contact] = controller;  	controller->setAvailableServerFeatures(serverDiscoInfo_);  	controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, contact, _1, false)); diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h index 55e62b9..4d7e9a8 100644 --- a/Swift/Controllers/Chat/ChatsManager.h +++ b/Swift/Controllers/Chat/ChatsManager.h @@ -51,10 +51,11 @@ namespace Swift {  	class WhiteboardManager;  	class HistoryController;  	class HighlightManager; +	class ClientBlockListManager;  	class ChatsManager {  		public: -			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* profileSettings, FileTransferOverview* ftOverview, XMPPRoster* roster, bool eagleMode, SettingsProvider* settings, HistoryController* historyController_, WhiteboardManager* whiteboardManager, HighlightManager* highlightManager); +			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* profileSettings, FileTransferOverview* ftOverview, XMPPRoster* roster, bool eagleMode, SettingsProvider* settings, HistoryController* historyController_, WhiteboardManager* whiteboardManager, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager);  			virtual ~ChatsManager();  			void setAvatarManager(AvatarManager* avatarManager);  			void setOnline(bool enabled); @@ -138,5 +139,6 @@ namespace Swift {  			HistoryController* historyController_;  			WhiteboardManager* whiteboardManager_;  			HighlightManager* highlightManager_; +			ClientBlockListManager* clientBlockListManager_;  	};  } diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index dd90d66..84a407c 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -10,48 +10,49 @@  #include <boost/bind.hpp> -#include "Swift/Controllers/Chat/ChatsManager.h" - -#include "Swift/Controllers/Chat/UnitTest/MockChatListWindow.h" -#include "Swift/Controllers/UIInterfaces/ChatWindow.h" -#include "Swift/Controllers/Settings/DummySettingsProvider.h" -#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" -#include "Swift/Controllers/UIInterfaces/ChatListWindowFactory.h" -#include "Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h" -#include "Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h" -#include "Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h" -#include "Swiften/Client/Client.h" -#include "Swiften/Disco/EntityCapsManager.h" -#include "Swiften/Disco/CapsProvider.h" -#include "Swiften/MUC/MUCManager.h" -#include "Swift/Controllers/Chat/ChatController.h" -#include "Swift/Controllers/XMPPEvents/EventController.h" -#include "Swift/Controllers/Chat/MUCController.h" -#include "Swiften/Presence/StanzaChannelPresenceSender.h" -#include "Swiften/Avatars/NullAvatarManager.h" -#include "Swiften/Avatars/AvatarMemoryStorage.h" -#include "Swiften/VCards/VCardManager.h" -#include "Swiften/VCards/VCardMemoryStorage.h" -#include "Swiften/Client/NickResolver.h" -#include "Swiften/Presence/DirectedPresenceSender.h" -#include "Swiften/Roster/XMPPRosterImpl.h" -#include "Swift/Controllers/UnitTest/MockChatWindow.h" -#include "Swiften/Client/DummyStanzaChannel.h" -#include "Swiften/Queries/DummyIQChannel.h" -#include "Swiften/Presence/PresenceOracle.h" -#include "Swiften/Jingle/JingleSessionManager.h" -#include "Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h" -#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h" -#include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" -#include "Swift/Controllers/UIEvents/UIEventStream.h" +#include <Swift/Controllers/Chat/ChatsManager.h> + +#include <Swift/Controllers/Chat/UnitTest/MockChatListWindow.h> +#include <Swift/Controllers/UIInterfaces/ChatWindow.h> +#include <Swift/Controllers/Settings/DummySettingsProvider.h> +#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> +#include <Swift/Controllers/UIInterfaces/ChatListWindowFactory.h> +#include <Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h> +#include <Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h> +#include <Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h> +#include <Swiften/Client/Client.h> +#include <Swiften/Disco/EntityCapsManager.h> +#include <Swiften/Disco/CapsProvider.h> +#include <Swiften/MUC/MUCManager.h> +#include <Swift/Controllers/Chat/ChatController.h> +#include <Swift/Controllers/XMPPEvents/EventController.h> +#include <Swift/Controllers/Chat/MUCController.h> +#include <Swiften/Presence/StanzaChannelPresenceSender.h> +#include <Swiften/Avatars/NullAvatarManager.h> +#include <Swiften/Avatars/AvatarMemoryStorage.h> +#include <Swiften/VCards/VCardManager.h> +#include <Swiften/VCards/VCardMemoryStorage.h> +#include <Swiften/Client/NickResolver.h> +#include <Swiften/Presence/DirectedPresenceSender.h> +#include <Swiften/Roster/XMPPRosterImpl.h> +#include <Swift/Controllers/UnitTest/MockChatWindow.h> +#include <Swiften/Client/DummyStanzaChannel.h> +#include <Swiften/Queries/DummyIQChannel.h> +#include <Swiften/Presence/PresenceOracle.h> +#include <Swiften/Jingle/JingleSessionManager.h> +#include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h> +#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h> +#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h>  #include <Swift/Controllers/ProfileSettingsProvider.h> -#include "Swift/Controllers/FileTransfer/FileTransferOverview.h" -#include "Swiften/Elements/DeliveryReceiptRequest.h" -#include "Swiften/Elements/DeliveryReceipt.h" +#include <Swift/Controllers/FileTransfer/FileTransferOverview.h> +#include <Swiften/Elements/DeliveryReceiptRequest.h> +#include <Swiften/Elements/DeliveryReceipt.h>  #include <Swiften/Base/Algorithm.h>  #include <Swift/Controllers/SettingConstants.h>  #include <Swift/Controllers/WhiteboardManager.h>  #include <Swiften/Whiteboard/WhiteboardSessionManager.h> +#include <Swiften/Client/ClientBlockListManager.h>  using namespace Swift; @@ -109,7 +110,8 @@ public:  		highlightManager_ = new HighlightManager(settings_);  		mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(chatListWindow_); -		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, xmppRoster_, false, settings_, NULL, wbManager_, highlightManager_); +		clientBlockListManager_ = new ClientBlockListManager(iqRouter_); +		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, xmppRoster_, false, settings_, NULL, wbManager_, highlightManager_, clientBlockListManager_);  		manager_->setAvatarManager(avatarManager_);  	} @@ -120,6 +122,7 @@ public:  		delete profileSettings_;  		delete avatarManager_;  		delete manager_; +		delete clientBlockListManager_;  		delete ftOverview_;  		delete ftManager_;  		delete wbSessionManager_; @@ -484,6 +487,7 @@ private:  	WhiteboardSessionManager* wbSessionManager_;  	WhiteboardManager* wbManager_;  	HighlightManager* highlightManager_; +	ClientBlockListManager* clientBlockListManager_;  };  CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest); diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 0c9c09c..32d045d 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -87,6 +87,8 @@  #include <Swiften/Client/StanzaChannel.h>  #include <Swift/Controllers/HighlightManager.h>  #include <Swift/Controllers/HighlightEditorController.h> +#include <Swiften/Client/ClientBlockListManager.h> +#include <Swift/Controllers/BlockListController.h>  namespace Swift { @@ -130,6 +132,7 @@ MainController::MainController(  	historyViewController_ = NULL;  	eventWindowController_ = NULL;  	profileController_ = NULL; +	blockListController_ = NULL;  	showProfileController_ = NULL;  	contactEditController_ = NULL;  	userSearchControllerChat_ = NULL; @@ -321,11 +324,13 @@ void MainController::handleConnected() {  		client_->getFileTransferManager()->startListeningOnPort(randomPort);  		ftOverview_ = new FileTransferOverview(client_->getFileTransferManager());  		fileTransferListController_->setFileTransferOverview(ftOverview_); -		rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_, client_->getEntityCapsProvider(), ftOverview_); +		rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_, client_->getEntityCapsProvider(), ftOverview_, client_->getClientBlockListManager());  		rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));  		rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));  		rosterController_->getWindow()->onShowCertificateRequest.connect(boost::bind(&MainController::handleShowCertificateRequest, this)); +		blockListController_ = new BlockListController(client_->getClientBlockListManager(), uiEventStream_, uiFactory_, eventController_); +  		contactEditController_ = new ContactEditController(rosterController_, client_->getVCardManager(), uiFactory_, uiEventStream_);  		whiteboardManager_ = new WhiteboardManager(uiFactory_, uiEventStream_, client_->getNickResolver(), client_->getWhiteboardSessionManager()); @@ -337,9 +342,9 @@ void MainController::handleConnected() {  #ifdef SWIFT_EXPERIMENTAL_HISTORY  		historyController_ = new HistoryController(storages_->getHistoryStorage());  		historyViewController_ = new HistoryViewController(jid_, uiEventStream_, historyController_, client_->getNickResolver(), client_->getAvatarManager(), client_->getPresenceOracle(), uiFactory_); -		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_, profileSettings_, ftOverview_, client_->getRoster(), !settings_->getSetting(SettingConstants::REMEMBER_RECENT_CHATS), settings_, historyController_, whiteboardManager_, highlightManager_); +		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_, profileSettings_, ftOverview_, client_->getRoster(), !settings_->getSetting(SettingConstants::REMEMBER_RECENT_CHATS), settings_, historyController_, whiteboardManager_, highlightManager_, client_->getClientBlockListManager());  #else -		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_, profileSettings_, ftOverview_, client_->getRoster(), !settings_->getSetting(SettingConstants::REMEMBER_RECENT_CHATS), settings_, NULL, whiteboardManager_, highlightManager_); +		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_, profileSettings_, ftOverview_, client_->getRoster(), !settings_->getSetting(SettingConstants::REMEMBER_RECENT_CHATS), settings_, NULL, whiteboardManager_, highlightManager_, client_->getClientBlockListManager());  #endif  		client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1)); @@ -731,6 +736,10 @@ void MainController::handleServerDiscoInfoResponse(boost::shared_ptr<DiscoInfo>  	if (!error) {  		chatsManager_->setServerDiscoInfo(info);  		adHocManager_->setServerDiscoInfo(info); +		if (info->hasFeature(DiscoInfo::BlockingCommandFeature)) { +			rosterController_->getWindow()->setBlockingCommandAvailable(true); +			rosterController_->initBlockingCommand(); +		}  	}  } diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index 4f37e12..d60805a 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -74,6 +74,7 @@ namespace Swift {  	class WhiteboardManager;  	class HighlightManager;  	class HighlightEditorController; +	class BlockListController;  	class MainController {  		public: @@ -154,6 +155,7 @@ namespace Swift {  			HistoryViewController* historyViewController_;  			HistoryController* historyController_;  			FileTransferListController* fileTransferListController_; +			BlockListController* blockListController_;  			ChatsManager* chatsManager_;  			ProfileController* profileController_;  			ShowProfileController* showProfileController_; diff --git a/Swift/Controllers/Roster/ContactRosterItem.cpp b/Swift/Controllers/Roster/ContactRosterItem.cpp index f301552..bf85167 100644 --- a/Swift/Controllers/Roster/ContactRosterItem.cpp +++ b/Swift/Controllers/Roster/ContactRosterItem.cpp @@ -16,7 +16,7 @@  namespace Swift { -ContactRosterItem::ContactRosterItem(const JID& jid, const JID& displayJID, const std::string& name, GroupRosterItem* parent) : RosterItem(name, parent), jid_(jid), displayJID_(displayJID) { +ContactRosterItem::ContactRosterItem(const JID& jid, const JID& displayJID, const std::string& name, GroupRosterItem* parent) : RosterItem(name, parent), jid_(jid), displayJID_(displayJID), blockState_(BlockingNotSupported) {  }  ContactRosterItem::~ContactRosterItem() { @@ -134,6 +134,14 @@ bool ContactRosterItem::supportsFeature(const Feature feature) const {  	return features_.find(feature) != features_.end();  } +void ContactRosterItem::setBlockState(BlockState state) { +	blockState_ = state; +} + +ContactRosterItem::BlockState ContactRosterItem::blockState() const { +	return blockState_; +} +  } diff --git a/Swift/Controllers/Roster/ContactRosterItem.h b/Swift/Controllers/Roster/ContactRosterItem.h index 247c606..fc65d6d 100644 --- a/Swift/Controllers/Roster/ContactRosterItem.h +++ b/Swift/Controllers/Roster/ContactRosterItem.h @@ -28,6 +28,12 @@ class ContactRosterItem : public RosterItem {  			FileTransferFeature,  			WhiteboardFeature  		}; + +		enum BlockState { +			BlockingNotSupported, +			IsBlocked, +			IsUnblocked +		};  	public:  		ContactRosterItem(const JID& jid, const JID& displayJID, const std::string& name, GroupRosterItem* parent); @@ -53,6 +59,9 @@ class ContactRosterItem : public RosterItem {  		void setSupportedFeatures(const std::set<Feature>& features);  		bool supportsFeature(Feature feature) const; +		void setBlockState(BlockState state); +		BlockState blockState() const; +  	private:  		JID jid_;  		JID displayJID_; @@ -63,6 +72,7 @@ class ContactRosterItem : public RosterItem {  		boost::shared_ptr<Presence> shownPresence_;  		std::vector<std::string> groups_;  		std::set<Feature> features_; +		BlockState blockState_;  };  } diff --git a/Swift/Controllers/Roster/Roster.cpp b/Swift/Controllers/Roster/Roster.cpp index b5c5998..3d0ca2e 100644 --- a/Swift/Controllers/Roster/Roster.cpp +++ b/Swift/Controllers/Roster/Roster.cpp @@ -22,7 +22,7 @@  namespace Swift { -Roster::Roster(bool sortByStatus, bool fullJIDMapping) { +Roster::Roster(bool sortByStatus, bool fullJIDMapping) : blockingSupported_(false) {  	sortByStatus_ = sortByStatus;  	fullJIDMapping_ = fullJIDMapping;  	root_ = new GroupRosterItem("Dummy-Root", NULL, sortByStatus_); @@ -71,6 +71,28 @@ void Roster::setAvailableFeatures(const JID& jid, const std::set<ContactRosterIt  	}  } +void Roster::setBlockedState(const std::vector<JID> &jids, ContactRosterItem::BlockState state) { +	if (!blockingSupported_ ) { +		foreach(ItemMap::value_type i, itemMap_) { +			foreach(ContactRosterItem* item, i.second) { +				item->setBlockState(ContactRosterItem::IsUnblocked); +			} +		} +	} + +	foreach(const JID& jid, jids) { +		ItemMap::const_iterator i = itemMap_.find(fullJIDMapping_ ? jid : jid.toBare()); +		if (i == itemMap_.end()) { +			continue; +		} +		foreach(ContactRosterItem* item, i->second) { +			item->setBlockState(state); +		} +	} + +	blockingSupported_ = true; +} +  void Roster::removeGroup(const std::string& group) {  	root_->removeGroupChild(group);  } @@ -87,6 +109,9 @@ void Roster::addContact(const JID& jid, const JID& displayJID, const std::string  	GroupRosterItem* group(getGroup(groupName));  	ContactRosterItem *item = new ContactRosterItem(jid, displayJID, name, group);	  	item->setAvatarPath(avatarPath); +	if (blockingSupported_) { +		item->setBlockState(ContactRosterItem::IsUnblocked); +	}  	group->addChild(item);  	ItemMap::iterator i = itemMap_.insert(std::make_pair(fullJIDMapping_ ? jid : jid.toBare(), std::vector<ContactRosterItem*>())).first;  	if (!i->second.empty()) { diff --git a/Swift/Controllers/Roster/Roster.h b/Swift/Controllers/Roster/Roster.h index 74547d6..91a152f 100644 --- a/Swift/Controllers/Roster/Roster.h +++ b/Swift/Controllers/Roster/Roster.h @@ -45,6 +45,7 @@ class Roster {  		boost::signal<void (RosterItem*)> onDataChanged;  		GroupRosterItem* getGroup(const std::string& groupName);  		void setAvailableFeatures(const JID& jid, const std::set<ContactRosterItem::Feature>& features); +		void setBlockedState(const std::vector<JID>& jids, ContactRosterItem::BlockState state);  	private:  		void handleDataChanged(RosterItem* item); @@ -58,6 +59,7 @@ class Roster {  		ItemMap itemMap_;  		bool fullJIDMapping_;  		bool sortByStatus_; +		bool blockingSupported_;  };  } diff --git a/Swift/Controllers/Roster/RosterController.cpp b/Swift/Controllers/Roster/RosterController.cpp index ec52993..d09ef4c 100644 --- a/Swift/Controllers/Roster/RosterController.cpp +++ b/Swift/Controllers/Roster/RosterController.cpp @@ -44,14 +44,15 @@  #include <Swiften/Disco/EntityCapsManager.h>  #include <Swiften/Jingle/JingleSessionManager.h>  #include <Swift/Controllers/SettingConstants.h> +#include <Swiften/Client/ClientBlockListManager.h>  namespace Swift {  /**   * The controller does not gain ownership of these parameters.   */ -RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsManager, FileTransferOverview* fileTransferOverview) - : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), nickManager_(nickManager), nickResolver_(nickResolver), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), ftOverview_(fileTransferOverview) { +RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsManager, FileTransferOverview* fileTransferOverview, ClientBlockListManager* clientBlockListManager) +	: myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), nickManager_(nickManager), nickResolver_(nickResolver), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), ftOverview_(fileTransferOverview), clientBlockListManager_(clientBlockListManager) {  	assert(fileTransferOverview);  	iqRouter_ = iqRouter;  	presenceOracle_ = presenceOracle; @@ -183,6 +184,20 @@ void RosterController::handleSettingChanged(const std::string& settingPath) {  	}  } +void RosterController::handleBlockingStateChanged() { +	if (clientBlockListManager_->getBlockList()->getState() == BlockList::Available) { +		roster_->setBlockedState(clientBlockListManager_->getBlockList()->getItems(), ContactRosterItem::IsBlocked); +	} +} + +void RosterController::handleBlockingItemAdded(const JID& jid) { +	roster_->setBlockedState(std::vector<JID>(1, jid), ContactRosterItem::IsBlocked); +} + +void RosterController::handleBlockingItemRemoved(const JID& jid) { +	roster_->setBlockedState(std::vector<JID>(1, jid), ContactRosterItem::IsUnblocked); +} +  void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) {  	if (boost::shared_ptr<AddContactUIEvent> addContactEvent = boost::dynamic_pointer_cast<AddContactUIEvent>(event)) {  		RosterItemPayload item; @@ -256,6 +271,18 @@ void RosterController::updateItem(const XMPPRosterItem& item) {  	request->send();  } +void RosterController::initBlockingCommand() { +	boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList(); + +	blockingOnStateChangedConnection_ = blockList->onStateChanged.connect(boost::bind(&RosterController::handleBlockingStateChanged, this)); +	blockingOnItemAddedConnection_ = blockList->onItemAdded.connect(boost::bind(&RosterController::handleBlockingItemAdded, this, _1)); +	blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&RosterController::handleBlockingItemRemoved, this, _1)); + +	if (blockList->getState() == BlockList::Available) { +		roster_->setBlockedState(blockList->getItems(), ContactRosterItem::IsBlocked); +	} +} +  void RosterController::handleRosterSetError(ErrorPayload::ref error, boost::shared_ptr<RosterPayload> rosterPayload) {  	if (!error) {  		return; diff --git a/Swift/Controllers/Roster/RosterController.h b/Swift/Controllers/Roster/RosterController.h index ec07574..06b551e 100644 --- a/Swift/Controllers/Roster/RosterController.h +++ b/Swift/Controllers/Roster/RosterController.h @@ -39,10 +39,11 @@ namespace Swift {  	class NickManager;  	class EntityCapsProvider;  	class FileTransferManager; -	 +	class ClientBlockListManager; +  	class RosterController {  		public: -			RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter_, SettingsProvider* settings, EntityCapsProvider* entityCapsProvider, FileTransferOverview* fileTransferOverview); +			RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter_, SettingsProvider* settings, EntityCapsProvider* entityCapsProvider, FileTransferOverview* fileTransferOverview, ClientBlockListManager* clientBlockListManager);  			~RosterController();  			void showRosterWindow();  			MainWindow* getWindow() {return mainWindow_;} @@ -57,6 +58,8 @@ namespace Swift {  			void setContactGroups(const JID& jid, const std::vector<std::string>& groups);  			void updateItem(const XMPPRosterItem&); +			void initBlockingCommand(); +  		private:  			void handleOnJIDAdded(const JID &jid);  			void handleRosterCleared(); @@ -76,6 +79,10 @@ namespace Swift {  			void handleOnCapsChanged(const JID& jid);  			void handleSettingChanged(const std::string& settingPath); +			void handleBlockingStateChanged(); +			void handleBlockingItemAdded(const JID& jid); +			void handleBlockingItemRemoved(const JID& jid); +  			JID myJID_;  			XMPPRoster* xmppRoster_;  			MainWindowFactory* mainWindowFactory_; @@ -94,7 +101,11 @@ namespace Swift {  			UIEventStream* uiEventStream_;  			EntityCapsProvider* entityCapsManager_;  			FileTransferOverview* ftOverview_; +			ClientBlockListManager* clientBlockListManager_; +			boost::bsignals::scoped_connection blockingOnStateChangedConnection_; +			boost::bsignals::scoped_connection blockingOnItemAddedConnection_; +			boost::bsignals::scoped_connection blockingOnItemRemovedConnection_;  			boost::bsignals::scoped_connection changeStatusConnection_;  			boost::bsignals::scoped_connection signOutConnection_;  			boost::bsignals::scoped_connection uiEventConnection_; diff --git a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp index e439c78..b0034e6 100644 --- a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp +++ b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp @@ -37,6 +37,7 @@  #include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h>  #include <Swiften/Base/Algorithm.h>  #include <Swiften/EventLoop/DummyEventLoop.h> +#include <Swiften/Client/ClientBlockListManager.h>  using namespace Swift; @@ -82,12 +83,14 @@ class RosterControllerTest : public CppUnit::TestFixture {  			ftManager_ = new DummyFileTransferManager();  			ftOverview_ = new FileTransferOverview(ftManager_); -			rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_, entityCapsManager_, ftOverview_); +			clientBlockListManager_ = new ClientBlockListManager(router_); +			rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_, entityCapsManager_, ftOverview_, clientBlockListManager_);  			mainWindow_ = mainWindowFactory_->last;  		}  		void tearDown() {  			delete rosterController_; +			delete clientBlockListManager_;  			delete ftManager_;  			delete jingleSessionManager_; @@ -337,6 +340,7 @@ class RosterControllerTest : public CppUnit::TestFixture {  		JingleSessionManager* jingleSessionManager_;  		FileTransferManager* ftManager_;  		FileTransferOverview* ftOverview_; +		ClientBlockListManager* clientBlockListManager_;  };  CPPUNIT_TEST_SUITE_REGISTRATION(RosterControllerTest); diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript index 26b9334..6f36a52 100644 --- a/Swift/Controllers/SConscript +++ b/Swift/Controllers/SConscript @@ -49,6 +49,7 @@ if env["SCONS_STAGE"] == "build" :  			"HistoryViewController.cpp",  			"HistoryController.cpp",  			"FileTransferListController.cpp", +			"BlockListController.cpp",  			"StatusTracker.cpp",  			"PresenceNotifier.cpp",  			"EventNotifier.cpp", diff --git a/Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h b/Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h new file mode 100644 index 0000000..d29cb4f --- /dev/null +++ b/Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2013 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIEvents/UIEvent.h> + +namespace Swift { + +class RequestBlockListDialogUIEvent : public UIEvent { +}; + +} diff --git a/Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h b/Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h new file mode 100644 index 0000000..9b7abcb --- /dev/null +++ b/Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIEvents/UIEvent.h> + +#include <Swiften/JID/JID.h> + +namespace Swift { + +class RequestChangeBlockStateUIEvent : public UIEvent { +	public: +		enum BlockState { +			Blocked, +			Unblocked +		}; + +	public: +		RequestChangeBlockStateUIEvent(BlockState newState, const JID& contact) : state_(newState), contact_(contact) {} + +		BlockState getBlockState() const { +			return state_; +		} + +		JID getContact() const { +			return contact_; +		} +	private: +		BlockState state_; +		JID contact_; +}; + +} diff --git a/Swift/Controllers/UIInterfaces/BlockListEditorWidget.h b/Swift/Controllers/UIInterfaces/BlockListEditorWidget.h new file mode 100644 index 0000000..60a1c11 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/BlockListEditorWidget.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2013 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <vector> + +#include <Swiften/JID/JID.h> +#include <Swiften/Base/boost_bsignals.h> + +namespace Swift { + +	class ClientBlockListManager; + +	class BlockListEditorWidget { +		public: +			virtual ~BlockListEditorWidget() {} + +			virtual void show() = 0; + +			virtual void setCurrentBlockList(const std::vector<JID>& blockedJIDs) = 0; +			virtual void setBusy(bool isBusy) = 0; + +			virtual std::vector<JID> getCurrentBlockList() const = 0; + +			boost::signal<void (const std::vector<JID>& /* blockedJID */)> onSetNewBlockList; +	}; + +} diff --git a/Swift/Controllers/UIInterfaces/BlockListEditorWidgetFactory.h b/Swift/Controllers/UIInterfaces/BlockListEditorWidgetFactory.h new file mode 100644 index 0000000..eb91ac1 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/BlockListEditorWidgetFactory.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2013 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +namespace Swift { + +	class BlockListEditorWidget; + +	class BlockListEditorWidgetFactory { +		public: +			virtual ~BlockListEditorWidgetFactory() {} + +			virtual BlockListEditorWidget* createBlockListEditorWidget() = 0; +	}; + +} diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h index d6b3656..b8e6722 100644 --- a/Swift/Controllers/UIInterfaces/ChatWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatWindow.h @@ -39,6 +39,7 @@ namespace Swift {  			enum RoomAction {ChangeSubject, Configure, Affiliations, Destroy, Invite};  			enum FileTransferState {WaitingForAccept, Negotiating, Transferring, Canceled, Finished, FTFailed};  			enum WhiteboardSessionState {WhiteboardAccepted, WhiteboardTerminated, WhiteboardRejected}; +			enum BlockingState {BlockingUnsupported, IsBlocked, IsUnblocked};  			ChatWindow() {}  			virtual ~ChatWindow() {} @@ -89,6 +90,7 @@ namespace Swift {  			virtual void setSubject(const std::string& subject) = 0;  			virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&) = 0;  			virtual void setAvailableRoomActions(const std::vector<RoomAction> &actions) = 0; +			virtual void setBlockingState(BlockingState state) = 0;  			/**  			 * Set an alert on the window.  			 * @param alertText Description of alert (required). diff --git a/Swift/Controllers/UIInterfaces/MainWindow.h b/Swift/Controllers/UIInterfaces/MainWindow.h index 2df2c10..3b10041 100644 --- a/Swift/Controllers/UIInterfaces/MainWindow.h +++ b/Swift/Controllers/UIInterfaces/MainWindow.h @@ -34,6 +34,7 @@ namespace Swift {  			/** Must be able to cope with NULL to clear the roster */  			virtual void setRosterModel(Roster* roster) = 0;  			virtual void setConnecting() = 0; +			virtual void setBlockingCommandAvailable(bool isAvailable) = 0;  			virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands) = 0;  			virtual void setStreamEncryptionStatus(bool tlsInPlaceAndValid) = 0;  			virtual void openCertificateDialog(const std::vector<Certificate::ref>& chain) = 0; diff --git a/Swift/Controllers/UIInterfaces/UIFactory.h b/Swift/Controllers/UIInterfaces/UIFactory.h index dcd1779..990dc98 100644 --- a/Swift/Controllers/UIInterfaces/UIFactory.h +++ b/Swift/Controllers/UIInterfaces/UIFactory.h @@ -22,6 +22,7 @@  #include <Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h>  #include <Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h>  #include <Swift/Controllers/UIInterfaces/HighlightEditorWidgetFactory.h> +#include <Swift/Controllers/UIInterfaces/BlockListEditorWidgetFactory.h>  namespace Swift {  	class UIFactory :  @@ -40,7 +41,8 @@ namespace Swift {  			public AdHocCommandWindowFactory,  			public FileTransferListWidgetFactory,  			public WhiteboardWindowFactory, -			public HighlightEditorWidgetFactory { +			public HighlightEditorWidgetFactory, +			public BlockListEditorWidgetFactory {  		public:  			virtual ~UIFactory() {}  	}; diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h index 84aaa04..8af65f7 100644 --- a/Swift/Controllers/UnitTest/MockChatWindow.h +++ b/Swift/Controllers/UnitTest/MockChatWindow.h @@ -60,6 +60,8 @@ namespace Swift {  			virtual void setAvailableRoomActions(const std::vector<RoomAction> &) {}  			virtual InviteToChatWindow* createInviteToChatWindow() {return NULL;} +			virtual void setBlockingState(BlockingState) {} +  			std::string name_;  			std::string lastMessageBody_;  			std::vector<SecurityLabelsCatalog::Item> labels_; diff --git a/Swift/Controllers/UnitTest/MockMainWindow.h b/Swift/Controllers/UnitTest/MockMainWindow.h index 19ab522..69a4e25 100644 --- a/Swift/Controllers/UnitTest/MockMainWindow.h +++ b/Swift/Controllers/UnitTest/MockMainWindow.h @@ -24,6 +24,7 @@ namespace Swift {  			virtual void setConnecting() {}  			virtual void setStreamEncryptionStatus(bool /*tlsInPlaceAndValid*/) {}  			virtual void openCertificateDialog(const std::vector<Certificate::ref>& /*chain*/) {} +			virtual void setBlockingCommandAvailable(bool /*isAvailable*/) {}  			Roster* roster;  	}; diff --git a/Swift/QtUI/QtBlockListEditorWindow.cpp b/Swift/QtUI/QtBlockListEditorWindow.cpp new file mode 100644 index 0000000..63e8d1f --- /dev/null +++ b/Swift/QtUI/QtBlockListEditorWindow.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2013 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <QtBlockListEditorWindow.h> +#include <ui_QtBlockListEditorWindow.h> + +#include <boost/bind.hpp> + +#include <QLineEdit> +#include <QMovie> +#include <QShortcut> +#include <QStyledItemDelegate> +#include <QValidator> + +#include <Swift/QtUI/QtUtilities.h> +#include <Swiften/Client/ClientBlockListManager.h> +#include <Swiften/Base/foreach.h> +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swiften/JID/JID.h> + +namespace Swift { + +class QtJIDValidator : public QValidator { +	public: +		QtJIDValidator(QObject* parent) : QValidator(parent) {} +		virtual ~QtJIDValidator() {} +		virtual QValidator::State validate(QString& input, int&) const { +			return JID(Q2PSTRING(input)).isValid() ? Acceptable : Intermediate; +		} +}; + +class QtJIDValidatedItemDelegate : public QItemDelegate { +	public: +		QtJIDValidatedItemDelegate(QObject* parent) : QItemDelegate(parent) {} +		virtual ~QtJIDValidatedItemDelegate() {} + +		virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const { +			QLineEdit *editor = new QLineEdit(parent); +			editor->setValidator(new QtJIDValidator(editor)); +			return editor; +		} + +		void setEditorData(QWidget *editor, const QModelIndex &index) const { +			QString value = index.model()->data(index, Qt::EditRole).toString(); + +			QLineEdit *lineEdit = static_cast<QLineEdit*>(editor); +			lineEdit->setText(value); +		} + +		void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { +			QLineEdit *lineEdit = static_cast<QLineEdit*>(editor); +			QString currentValue = lineEdit->text(); +			int pos = 0; +			if (lineEdit->validator()->validate(currentValue, pos) == QValidator::Acceptable) { +				model->setData(index, lineEdit->text(), Qt::EditRole); +			} else { +				model->setData(index, QString(), Qt::EditRole); +			} +		} + +		void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { +			editor->setGeometry(option.rect); +		} +}; + +QtBlockListEditorWindow::QtBlockListEditorWindow() : QWidget(), ui(new Ui::QtBlockListEditorWindow) { +	ui->setupUi(this); +	new QShortcut(QKeySequence::Close, this, SLOT(close())); +	ui->throbberLabel->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this)); + +	itemDelegate = new QtRemovableItemDelegate(style()); + +	connect(ui->savePushButton, SIGNAL(clicked()), SLOT(applyChanges())); + +	ui->blockListTreeWidget->setColumnCount(2); +	ui->blockListTreeWidget->header()->setStretchLastSection(false); +	int closeIconWidth = ui->blockListTreeWidget->fontMetrics().height(); +	ui->blockListTreeWidget->header()->resizeSection(1, closeIconWidth); + +#if QT_VERSION >= 0x050000 +	ui->blockListTreeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch); +#else +	ui->blockListTreeWidget->header()->setResizeMode(0, QHeaderView::Stretch); +#endif + +	ui->blockListTreeWidget->setHeaderHidden(true); +	ui->blockListTreeWidget->setRootIsDecorated(false); +	ui->blockListTreeWidget->setEditTriggers(QAbstractItemView::DoubleClicked); +	ui->blockListTreeWidget->setItemDelegateForColumn(0, new QtJIDValidatedItemDelegate(this)); +	ui->blockListTreeWidget->setItemDelegateForColumn(1, itemDelegate); +	connect(ui->blockListTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), SLOT(handleItemChanged(QTreeWidgetItem*,int))); + +	QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << ""); +	item->setFlags(item->flags() | Qt::ItemIsEditable); +	ui->blockListTreeWidget->addTopLevelItem(item); +} + +QtBlockListEditorWindow::~QtBlockListEditorWindow() { +} + +void QtBlockListEditorWindow::show() { +	QWidget::show(); +	QWidget::activateWindow(); +} + +void QtBlockListEditorWindow::handleItemChanged(QTreeWidgetItem *, int) { +	bool hasEmptyRow = false; +	QList<QTreeWidgetItem*> rows = ui->blockListTreeWidget->findItems("", Qt::MatchFixedString); +	foreach(QTreeWidgetItem* row, rows) { +		if (row->text(0).isEmpty()) { +			hasEmptyRow = true; +		} +	} + +	if (!hasEmptyRow) { +		QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << ""); +		item->setFlags(item->flags() | Qt::ItemIsEditable); +		ui->blockListTreeWidget->addTopLevelItem(item); +	} +} + +void QtBlockListEditorWindow::applyChanges() { +	onSetNewBlockList(getCurrentBlockList()); +} + +void Swift::QtBlockListEditorWindow::setCurrentBlockList(const std::vector<JID> &blockedJIDs) { +	ui->blockListTreeWidget->clear(); + +	foreach(const JID& jid, blockedJIDs) { +		QTreeWidgetItem* item = new QTreeWidgetItem(QStringList(P2QSTRING(jid.toString())) << ""); +		item->setFlags(item->flags() | Qt::ItemIsEditable); +		ui->blockListTreeWidget->addTopLevelItem(item); +	} +	handleItemChanged(0,0); +} + +void Swift::QtBlockListEditorWindow::setBusy(bool isBusy) { +	if (isBusy) { +		ui->throbberLabel->movie()->start(); +		ui->throbberLabel->show(); +	} else { +		ui->throbberLabel->movie()->stop(); +		ui->throbberLabel->hide(); +	} +} + +std::vector<JID> Swift::QtBlockListEditorWindow::getCurrentBlockList() const { +	std::vector<JID> futureBlockedJIDs; + +	for(int i=0; i < ui->blockListTreeWidget->topLevelItemCount(); ++i) { +		QTreeWidgetItem* row = ui->blockListTreeWidget->topLevelItem(i); +		if (!row->text(0).isEmpty()) { +			futureBlockedJIDs.push_back(JID(Q2PSTRING(row->text(0)))); +		} +	} +	return futureBlockedJIDs; +} + +} diff --git a/Swift/QtUI/QtBlockListEditorWindow.h b/Swift/QtUI/QtBlockListEditorWindow.h new file mode 100644 index 0000000..4b124a3 --- /dev/null +++ b/Swift/QtUI/QtBlockListEditorWindow.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIInterfaces/BlockListEditorWidget.h> +#include <Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h> + +#include <QWidget> +#include <QTreeWidgetItem> + +namespace Ui { +	class QtBlockListEditorWindow; +} + +namespace Swift { + +class QtBlockListEditorWindow : public QWidget, public BlockListEditorWidget { +	Q_OBJECT + +	public: +		QtBlockListEditorWindow(); +		virtual ~QtBlockListEditorWindow(); + +		virtual void show(); +		virtual void setCurrentBlockList(const std::vector<JID>& blockedJIDs); +		virtual void setBusy(bool isBusy); +		virtual std::vector<JID> getCurrentBlockList() const; + +	private slots: +		void handleItemChanged(QTreeWidgetItem*, int); +		void applyChanges(); + +	private: +		Ui::QtBlockListEditorWindow* ui; +		QtRemovableItemDelegate* itemDelegate; +}; + +} diff --git a/Swift/QtUI/QtBlockListEditorWindow.ui b/Swift/QtUI/QtBlockListEditorWindow.ui new file mode 100644 index 0000000..f71bbae --- /dev/null +++ b/Swift/QtUI/QtBlockListEditorWindow.ui @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtBlockListEditorWindow</class> + <widget class="QWidget" name="QtBlockListEditorWindow"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>348</width> +    <height>262</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Edit Block List</string> +  </property> +  <layout class="QVBoxLayout" name="verticalLayout"> +   <property name="spacing"> +    <number>5</number> +   </property> +   <property name="margin"> +    <number>5</number> +   </property> +   <item> +    <widget class="QTreeWidget" name="blockListTreeWidget"> +     <attribute name="headerVisible"> +      <bool>false</bool> +     </attribute> +     <column> +      <property name="text"> +       <string notr="true">1</string> +      </property> +     </column> +    </widget> +   </item> +   <item> +    <layout class="QHBoxLayout" name="horizontalLayout"> +     <property name="sizeConstraint"> +      <enum>QLayout::SetDefaultConstraint</enum> +     </property> +     <item> +      <spacer name="horizontalSpacer"> +       <property name="orientation"> +        <enum>Qt::Horizontal</enum> +       </property> +       <property name="sizeHint" stdset="0"> +        <size> +         <width>40</width> +         <height>20</height> +        </size> +       </property> +      </spacer> +     </item> +     <item> +      <widget class="QLabel" name="errorLabel"> +       <property name="text"> +        <string/> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QLabel" name="throbberLabel"> +       <property name="text"> +        <string/> +       </property> +       <property name="alignment"> +        <set>Qt::AlignCenter</set> +       </property> +       <property name="textInteractionFlags"> +        <set>Qt::NoTextInteraction</set> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QPushButton" name="savePushButton"> +       <property name="text"> +        <string>Save</string> +       </property> +       <property name="default"> +        <bool>true</bool> +       </property> +      </widget> +     </item> +    </layout> +   </item> +  </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 11e64ab..efdab99 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -24,6 +24,7 @@  #include <Swift/Controllers/UIEvents/UIEventStream.h>  #include <Swift/Controllers/UIEvents/SendFileUIEvent.h>  #include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h>  #include "QtChatWindowJSBridge.h"  #include "QtUtilities.h" @@ -47,6 +48,7 @@  #include <QTextEdit>  #include <QTime>  #include <QUrl> +#include <QToolButton>  #include <QPushButton>  #include <QFileDialog>  #include <QMenu> @@ -66,7 +68,7 @@ const QString QtChatWindow::ButtonFileTransferAcceptRequest = QString("filetrans  const QString QtChatWindow::ButtonMUCInvite = QString("mucinvite"); -QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), emoticons_(emoticons) { +QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), emoticons_(emoticons), blockingState_(BlockingUnsupported) {  	settings_ = settings;  	unreadCount_ = 0;  	idCounter_ = 0; @@ -105,21 +107,18 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	alertLabel_->setStyleSheet(alertStyleSheet_);  	alertWidget_->hide(); -	QBoxLayout* subjectLayout = new QBoxLayout(QBoxLayout::LeftToRight); +	subjectLayout_ = new QBoxLayout(QBoxLayout::LeftToRight);  	subject_ = new QLineEdit(this); -	subjectLayout->addWidget(subject_); +	subjectLayout_->addWidget(subject_);  	setSubject("");  	subject_->setReadOnly(true); -	actionButton_ = new QPushButton(this); +	QPushButton* actionButton_ = new QPushButton(this);  	actionButton_->setIcon(QIcon(":/icons/actions.png"));  	connect(actionButton_, SIGNAL(clicked()), this, SLOT(handleActionButtonClicked())); -	subjectLayout->addWidget(actionButton_); -  	subject_->hide(); -	actionButton_->hide(); -	layout->addLayout(subjectLayout); +	layout->addLayout(subjectLayout_);  	logRosterSplitter_ = new QSplitter(this);  	logRosterSplitter_->setAutoFillBackground(true); @@ -159,6 +158,12 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	correctingLabel_ = new QLabel(tr("Correcting"), this);  	inputBarLayout->addWidget(correctingLabel_);  	correctingLabel_->hide(); + +	// using an extra layout to work around Qt margin glitches on OS X +	QHBoxLayout* actionLayout = new QHBoxLayout(); +	actionLayout->addWidget(actionButton_); + +	inputBarLayout->addLayout(actionLayout);  	layout->addLayout(inputBarLayout);  	inputClearing_ = false; @@ -415,7 +420,6 @@ void QtChatWindow::convertToMUC() {  	setAcceptDrops(false);  	treeWidget_->show();  	subject_->show(); -	actionButton_->show();  }  void QtChatWindow::qAppFocusChanged(QWidget* /*old*/, QWidget* /*now*/) { @@ -504,8 +508,7 @@ QString QtChatWindow::linkimoticonify(const QString& message) const {  	return messageHTML;  } -QString QtChatWindow::getHighlightSpanStart(const HighlightAction& highlight) -{ +QString QtChatWindow::getHighlightSpanStart(const HighlightAction& highlight) {  	QString color = QtUtilities::htmlEscape(P2QSTRING(highlight.getTextColor()));  	QString background = QtUtilities::htmlEscape(P2QSTRING(highlight.getTextBackground()));  	if (color.isEmpty()) { @@ -922,15 +925,26 @@ void QtChatWindow::handleActionButtonClicked() {  	QAction* destroy = NULL;  	QAction* invite = NULL; -	foreach(ChatWindow::RoomAction availableAction, availableRoomActions_) -	{ -		switch(availableAction) +	QAction* block = NULL; +	QAction* unblock = NULL; + +	if (availableRoomActions_.empty()) { +		if (blockingState_ == IsBlocked) { +			unblock = contextMenu.addAction(tr("Unblock")); +		} else if (blockingState_ == IsUnblocked) { +			block = contextMenu.addAction(tr("Block")); +		} +	} else { +		foreach(ChatWindow::RoomAction availableAction, availableRoomActions_)  		{ -			case ChatWindow::ChangeSubject: changeSubject = contextMenu.addAction(tr("Change subject…")); break; -			case ChatWindow::Configure: configure = contextMenu.addAction(tr("Configure room…")); break; -			case ChatWindow::Affiliations: affiliations = contextMenu.addAction(tr("Edit affiliations…")); break; -			case ChatWindow::Destroy: destroy = contextMenu.addAction(tr("Destroy room")); break; -			case ChatWindow::Invite: invite = contextMenu.addAction(tr("Invite person to this room…")); break; +			switch(availableAction) +			{ +				case ChatWindow::ChangeSubject: changeSubject = contextMenu.addAction(tr("Change subject…")); break; +				case ChatWindow::Configure: configure = contextMenu.addAction(tr("Configure room…")); break; +				case ChatWindow::Affiliations: affiliations = contextMenu.addAction(tr("Edit affiliations…")); break; +				case ChatWindow::Destroy: destroy = contextMenu.addAction(tr("Destroy room")); break; +				case ChatWindow::Invite: invite = contextMenu.addAction(tr("Invite person to this room…")); break; +			}  		}  	} @@ -970,6 +984,12 @@ void QtChatWindow::handleActionButtonClicked() {  	else if (result == invite) {  		onInvitePersonToThisMUCRequest();  	} +	else if (result == block) { +		eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Blocked, JID(Q2PSTRING(contact_)))); +	} +	else if (result == unblock) { +		eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Unblocked, JID(Q2PSTRING(contact_)))); +	}  }  void QtChatWindow::handleAffiliationEditorAccepted() { @@ -981,11 +1001,14 @@ void QtChatWindow::setAffiliations(MUCOccupant::Affiliation affiliation, const s  	affiliationEditor_->setAffiliations(affiliation, jids);  } -void QtChatWindow::setAvailableRoomActions(const std::vector<RoomAction> &actions) -{ +void QtChatWindow::setAvailableRoomActions(const std::vector<RoomAction>& actions) {  	availableRoomActions_ = actions;  } +void QtChatWindow::setBlockingState(BlockingState state) { +	blockingState_ = state; +} +  void QtChatWindow::showRoomConfigurationForm(Form::ref form) {  	if (mucConfigurationWindow_) {  		delete mucConfigurationWindow_.data(); diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index 4abd456..3ccea31 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -132,7 +132,8 @@ namespace Swift {  			void showRoomConfigurationForm(Form::ref);  			void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true);  			void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&); -			void setAvailableRoomActions(const std::vector<RoomAction> &actions); +			void setAvailableRoomActions(const std::vector<RoomAction>& actions); +			void setBlockingState(BlockingState state);  			InviteToChatWindow* createInviteToChatWindow(); @@ -209,6 +210,7 @@ namespace Swift {  			QtChatTheme* theme_;  			QtTextEdit* input_;  			QWidget* midBar_;  +			QBoxLayout* subjectLayout_;  			QComboBox* labelsWidget_;  			QtOccupantListWidget* treeWidget_;  			QLabel* correctingLabel_; @@ -217,7 +219,6 @@ namespace Swift {  			QPushButton* alertButton_;  			TabComplete* completer_;  			QLineEdit* subject_; -			QPushButton* actionButton_;  			bool isCorrection_;  			bool previousMessageWasSelf_;  			PreviousMessageKind previousMessageKind_; @@ -240,5 +241,6 @@ namespace Swift {  			bool showEmoticons_;  			QPalette defaultLabelsPalette_;  			LabelModel* labelModel_; +			BlockingState blockingState_;  	};  } diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index 9749397..572b06f 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -33,6 +33,7 @@  #include <Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h>  #include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>  #include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h>  #include <Swift/QtUI/QtUISettingConstants.h>  #include <Swift/Controllers/SettingConstants.h>  #include <Swiften/Base/Platform.h> @@ -138,6 +139,10 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr  	connect(viewLogsAction, SIGNAL(triggered()), SLOT(handleViewLogsAction()));  	actionsMenu->addAction(viewLogsAction);  #endif +	openBlockingListEditor_ = new QAction(tr("Edit &Blocking List…"), this); +	connect(openBlockingListEditor_, SIGNAL(triggered()), SLOT(handleEditBlockingList())); +	actionsMenu->addAction(openBlockingListEditor_); +	openBlockingListEditor_->setVisible(false);  	addUserAction_ = new QAction(tr("&Add Contact…"), this);  	connect(addUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleAddUserActionTriggered(bool)));  	actionsMenu->addAction(addUserAction_); @@ -190,6 +195,10 @@ void QtMainWindow::handleShowCertificateInfo() {  	onShowCertificateRequest();  } +void QtMainWindow::handleEditBlockingList() { +	uiEventStream_->send(boost::make_shared<RequestBlockListDialogUIEvent>()); +} +  QtEventWindow* QtMainWindow::getEventWindow() {  	return eventWindow_;  } @@ -359,5 +368,9 @@ void QtMainWindow::setAvailableAdHocCommands(const std::vector<DiscoItems::Item>  	}  } +void QtMainWindow::setBlockingCommandAvailable(bool isAvailable) { +	openBlockingListEditor_->setVisible(isAvailable); +} +  } diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h index 99bc675..3e6e1d3 100644 --- a/Swift/QtUI/QtMainWindow.h +++ b/Swift/QtUI/QtMainWindow.h @@ -53,6 +53,7 @@ namespace Swift {  			QtChatListWindow* getChatListWindow();  			void setRosterModel(Roster* roster);  			void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands); +			void setBlockingCommandAvailable(bool isAvailable);  		private slots:  			void handleStatusChanged(StatusShow::Type showType, const QString &statusMessage);  			void handleSettingChanged(const std::string& settingPath); @@ -72,6 +73,7 @@ namespace Swift {  			void handleTabChanged(int index);  			void handleToggleRequestDeliveryReceipts(bool enabled);  			void handleShowCertificateInfo(); +			void handleEditBlockingList();  		private:  			SettingsProvider* settings_; @@ -85,6 +87,7 @@ namespace Swift {  			QAction* showOfflineAction_;  			QAction* compactRosterAction_;  			QAction* showEmoticonsAction_; +			QAction* openBlockingListEditor_;  			QAction* toggleRequestDeliveryReceipts_;  			QMenu* serverAdHocMenu_;  			QtTabWidget* tabs_; diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp index 7ec25df..0c7fbc2 100644 --- a/Swift/QtUI/QtUIFactory.cpp +++ b/Swift/QtUI/QtUIFactory.cpp @@ -4,34 +4,35 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "QtUIFactory.h" +#include <Swift/QtUI/QtUIFactory.h>  #include <QSplitter> -#include "QtXMLConsoleWidget.h" -#include "QtChatTabs.h" -#include "QtMainWindow.h" -#include "QtLoginWindow.h" -#include "QtSystemTray.h" -#include "QtSettingsProvider.h" -#include "QtMainWindow.h" -#include "QtChatWindow.h" -#include "QtJoinMUCWindow.h" -#include "QtChatWindowFactory.h" -#include "QtSwiftUtil.h" -#include "MUCSearch/QtMUCSearchWindow.h" -#include "UserSearch/QtUserSearchWindow.h" -#include "QtProfileWindow.h" -#include "QtContactEditWindow.h" -#include "QtAdHocCommandWindow.h" -#include "QtFileTransferListWidget.h" -#include <QtHighlightEditorWidget.h> -#include "Whiteboard/QtWhiteboardWindow.h" +#include <Swift/QtUI/QtXMLConsoleWidget.h> +#include <Swift/QtUI/QtChatTabs.h> +#include <Swift/QtUI/QtMainWindow.h> +#include <Swift/QtUI/QtLoginWindow.h> +#include <Swift/QtUI/QtSystemTray.h> +#include <Swift/QtUI/QtSettingsProvider.h> +#include <Swift/QtUI/QtMainWindow.h> +#include <Swift/QtUI/QtChatWindow.h> +#include <Swift/QtUI/QtJoinMUCWindow.h> +#include <Swift/QtUI/QtChatWindowFactory.h> +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/MUCSearch/QtMUCSearchWindow.h> +#include <Swift/QtUI/UserSearch/QtUserSearchWindow.h> +#include <Swift/QtUI/QtProfileWindow.h> +#include <Swift/QtUI/QtContactEditWindow.h> +#include <Swift/QtUI/QtAdHocCommandWindow.h> +#include <Swift/QtUI/QtFileTransferListWidget.h> +#include <Swift/QtUI/QtHighlightEditorWidget.h> +#include <Swift/QtUI/Whiteboard/QtWhiteboardWindow.h>  #include <Swift/Controllers/Settings/SettingsProviderHierachy.h>  #include <Swift/QtUI/QtUISettingConstants.h> -#include <QtHistoryWindow.h> +#include <Swift/QtUI/QtHistoryWindow.h>  #include <Swiften/Whiteboard/WhiteboardSession.h> -#include <QtSingleWindow.h> +#include <Swift/QtUI/QtSingleWindow.h> +#include <Swift/QtUI/QtBlockListEditorWindow.h>  namespace Swift { @@ -167,6 +168,10 @@ HighlightEditorWidget* QtUIFactory::createHighlightEditorWidget() {  	return new QtHighlightEditorWidget();  } +BlockListEditorWidget *QtUIFactory::createBlockListEditorWidget() { +	return new QtBlockListEditorWindow(); +} +  void QtUIFactory::createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) {  	new QtAdHocCommandWindow(command);  } diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h index a1baa82..662c78e 100644 --- a/Swift/QtUI/QtUIFactory.h +++ b/Swift/QtUI/QtUIFactory.h @@ -49,6 +49,7 @@ namespace Swift {  			virtual FileTransferListWidget* createFileTransferListWidget();  			virtual WhiteboardWindow* createWhiteboardWindow(boost::shared_ptr<WhiteboardSession> whiteboardSession);  			virtual HighlightEditorWidget* createHighlightEditorWidget(); +			virtual BlockListEditorWidget* createBlockListEditorWidget();  			virtual void createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command);  		private slots: diff --git a/Swift/QtUI/Roster/QtRosterWidget.cpp b/Swift/QtUI/Roster/QtRosterWidget.cpp index b783ff6..6bf3989 100644 --- a/Swift/QtUI/Roster/QtRosterWidget.cpp +++ b/Swift/QtUI/Roster/QtRosterWidget.cpp @@ -4,24 +4,25 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Roster/QtRosterWidget.h" +#include <Swift/QtUI/Roster/QtRosterWidget.h>  #include <QContextMenuEvent>  #include <QMenu>  #include <QInputDialog>  #include <QFileDialog> -#include "Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h" -#include "Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h" -#include "Swift/Controllers/UIEvents/RenameGroupUIEvent.h" -#include "Swift/Controllers/UIEvents/SendFileUIEvent.h" -#include "Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h" -#include "Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h" -#include "QtContactEditWindow.h" -#include "Swift/Controllers/Roster/ContactRosterItem.h" -#include "Swift/Controllers/Roster/GroupRosterItem.h" -#include "Swift/Controllers/UIEvents/UIEventStream.h" -#include "QtSwiftUtil.h" +#include <Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h> +#include <Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h> +#include <Swift/Controllers/UIEvents/RenameGroupUIEvent.h> +#include <Swift/Controllers/UIEvents/SendFileUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h> +#include <Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h> +#include <Swift/QtUI/QtContactEditWindow.h> +#include <Swift/Controllers/Roster/ContactRosterItem.h> +#include <Swift/Controllers/Roster/GroupRosterItem.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/QtUI/QtSwiftUtil.h>  namespace Swift { @@ -59,6 +60,17 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) {  		QAction* editContact = contextMenu.addAction(tr("Edit…"));  		QAction* removeContact = contextMenu.addAction(tr("Remove"));  		QAction* showProfileForContact = contextMenu.addAction(tr("Show Profile")); + +		QAction* unblockContact = NULL; +		if (contact->blockState() == ContactRosterItem::IsBlocked) { +			unblockContact = contextMenu.addAction(tr("Unblock")); +		} + +		QAction* blockContact = NULL; +		if (contact->blockState() == ContactRosterItem::IsUnblocked) { +			blockContact = contextMenu.addAction(tr("Block")); +		} +  #ifdef SWIFT_EXPERIMENTAL_FT  		QAction* sendFile = NULL;  		if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) { @@ -71,6 +83,7 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) {  			startWhiteboardChat = contextMenu.addAction(tr("Start Whiteboard Chat"));  		}  #endif +  		QAction* result = contextMenu.exec(event->globalPos());  		if (result == editContact) {  			eventStream_->send(boost::make_shared<RequestContactEditorUIEvent>(contact->getJID())); @@ -83,6 +96,12 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) {  		else if (result == showProfileForContact) {  			eventStream_->send(boost::make_shared<ShowProfileForRosterItemUIEvent>(contact->getJID()));  		} +		else if (unblockContact && result == unblockContact) { +			eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Unblocked, contact->getJID())); +		} +		else if (blockContact && result == blockContact) { +			eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Blocked, contact->getJID())); +		}  #ifdef SWIFT_EXPERIMENTAL_FT  		else if (sendFile && result == sendFile) {  			QString fileName = QFileDialog::getOpenFileName(this, tr("Send File"), "", tr("All Files (*);;")); diff --git a/Swift/QtUI/Roster/RosterModel.cpp b/Swift/QtUI/Roster/RosterModel.cpp index 2909c05..8f39f35 100644 --- a/Swift/QtUI/Roster/RosterModel.cpp +++ b/Swift/QtUI/Roster/RosterModel.cpp @@ -162,6 +162,10 @@ QString RosterModel::getStatusText(RosterItem* item) const {  QIcon RosterModel::getPresenceIcon(RosterItem* item) const {  	ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item);  	if (!contact) return QIcon(); +	if (contact->blockState() == ContactRosterItem::IsBlocked) { +		return QIcon(":/icons/stop.png"); +	} +  	QString iconString;  	switch (contact->getStatusShow()) {  		case StatusShow::Online: iconString = "online";break; diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 0d69d6b..0bcab5e 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -94,6 +94,7 @@ sources = [      "QtLoginWindow.cpp",      "QtMainWindow.cpp",      "QtProfileWindow.cpp", +    "QtBlockListEditorWindow.cpp",      "QtNameWidget.cpp",      "QtSettingsProvider.cpp",      "QtStatusWidget.cpp", @@ -273,6 +274,7 @@ myenv.Uic4("QtHistoryWindow.ui")  myenv.Uic4("QtConnectionSettings.ui")  myenv.Uic4("QtHighlightRuleWidget.ui")  myenv.Uic4("QtHighlightEditorWidget.ui") +myenv.Uic4("QtBlockListEditorWindow.ui")  myenv.Uic4("QtSpellCheckerWindow.ui")  myenv.Qrc("DefaultTheme.qrc")  myenv.Qrc("Swift.qrc") diff --git a/Swift/QtUI/Swift.qrc b/Swift/QtUI/Swift.qrc index f1b3140..eeef80d 100644 --- a/Swift/QtUI/Swift.qrc +++ b/Swift/QtUI/Swift.qrc @@ -41,5 +41,6 @@  		<file alias="icons/star-checked.png">../resources/icons/star-checked2.png</file>  		<file alias="icons/star-unchecked.png">../resources/icons/star-unchecked2.png</file>  		<file alias="icons/zzz.png">../resources/icons/zzz.png</file> +		<file alias="icons/stop.png">../resources/icons/stop.png</file>  	</qresource>  </RCC> diff --git a/Swift/resources/icons/stop.png b/Swift/resources/icons/stop.pngBinary files differ new file mode 100644 index 0000000..1574342 --- /dev/null +++ b/Swift/resources/icons/stop.png diff --git a/Swift/resources/icons/stop.svg b/Swift/resources/icons/stop.svg new file mode 100644 index 0000000..b6171ef --- /dev/null +++ b/Swift/resources/icons/stop.svg @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg +   xmlns:dc="http://purl.org/dc/elements/1.1/" +   xmlns:cc="http://creativecommons.org/ns#" +   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" +   xmlns:svg="http://www.w3.org/2000/svg" +   xmlns="http://www.w3.org/2000/svg" +   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" +   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" +   width="601" +   id="svg2" +   version="1.1" +   inkscape:version="0.48.2 r9819" +   height="601" +   sodipodi:docname="Blank_stop_sign_octagon.svg" +   inkscape:export-filename="/Users/tobias/Downloads/Blank_stop_sign_octagon.png" +   inkscape:export-xdpi="3.29" +   inkscape:export-ydpi="3.29"> +  <metadata +     id="metadata12"> +    <rdf:RDF> +      <cc:Work +         rdf:about=""> +        <dc:format>image/svg+xml</dc:format> +        <dc:type +           rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> +        <dc:title></dc:title> +      </cc:Work> +    </rdf:RDF> +  </metadata> +  <defs +     id="defs10" /> +  <sodipodi:namedview +     pagecolor="#ffffff" +     bordercolor="#666666" +     borderopacity="1" +     objecttolerance="10" +     gridtolerance="10" +     guidetolerance="10" +     inkscape:pageopacity="0" +     inkscape:pageshadow="2" +     inkscape:window-width="1440" +     inkscape:window-height="852" +     id="namedview8" +     showgrid="false" +     fit-margin-top="0" +     fit-margin-left="0" +     fit-margin-right="0" +     fit-margin-bottom="0" +     inkscape:zoom="0.63429569" +     inkscape:cx="102.64275" +     inkscape:cy="390.12926" +     inkscape:window-x="0" +     inkscape:window-y="0" +     inkscape:window-maximized="1" +     inkscape:current-layer="svg2" /> +  <path +     d="m 0.5,424.5 v -248 l 176,-176 h 248 l 176,176 v 248 l -176,176 h -248 z" +     id="path4" +     inkscape:connector-curvature="0" +     style="fill:#ffffff;stroke:#000000" /> +  <path +     d="M 17,417 V 182 L 183,16 H 418 L 584,182 V 417 L 418,583 H 183 z" +     id="path6" +     inkscape:connector-curvature="0" +     style="fill:#ff0000" /> +  <rect +     style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" +     id="rect2991" +     width="368.91312" +     height="96.169655" +     x="116.04344" +     y="249.42896" /> +</svg> diff --git a/Swift/resources/icons/stop.txt b/Swift/resources/icons/stop.txt new file mode 100644 index 0000000..9d4a1b1 --- /dev/null +++ b/Swift/resources/icons/stop.txt @@ -0,0 +1,2 @@ +License: Public Domain +Source:  https://commons.wikimedia.org/wiki/File:Blank_stop_sign_octagon.svg | 
 Swift
 Swift