diff options
Diffstat (limited to 'Swift')
220 files changed, 6925 insertions, 490 deletions
| diff --git a/Swift/ChangeLog.md b/Swift/ChangeLog.md index 544dcfa..097de4d 100644 --- a/Swift/ChangeLog.md +++ b/Swift/ChangeLog.md @@ -1,3 +1,8 @@ +3.0-beta1 +--------- +- Allow toggling of a more compact roster display. +- Remember status settings and provide quick access to them with searching of recent selections in the status setter. +  2.1  ---  - Fixed potential crash when using proxies on Mac OS X. diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp index 16b22fe..0ffef0c 100644 --- a/Swift/Controllers/Chat/ChatController.cpp +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -32,7 +32,7 @@  #include <Swiften/Elements/DeliveryReceipt.h>  #include <Swiften/Elements/DeliveryReceiptRequest.h>  #include <Swift/Controllers/SettingConstants.h> - +#include <Swift/Controllers/Highlighter.h>  #include <Swiften/Base/Log.h>  namespace Swift { @@ -40,8 +40,8 @@ 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) -	: ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry), 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) +	: ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry, highlightManager), eventStream_(eventStream), userWantsReceipts_(userWantsReceipts), settings_(settings) {  	isInMUC_ = isInMUC;  	lastWasPresence_ = false;  	chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsProvider); @@ -174,8 +174,11 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> me  	}  } -void ChatController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) { +void ChatController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const HighlightAction& highlight) {  	eventController_->handleIncomingEvent(messageEvent); +	if (!messageEvent->getConcluded()) { +		highlighter_->handleHighlightAction(highlight); +	}  } @@ -211,9 +214,9 @@ void ChatController::postSendMessage(const std::string& body, boost::shared_ptr<  	boost::shared_ptr<Replace> replace = sentStanza->getPayload<Replace>();  	if (replace) {  		eraseIf(unackedStanzas_, PairSecondEquals<boost::shared_ptr<Stanza>, std::string>(myLastMessageUIID_)); -		replaceMessage(body, myLastMessageUIID_, boost::posix_time::microsec_clock::universal_time()); +		replaceMessage(body, myLastMessageUIID_, boost::posix_time::microsec_clock::universal_time(), HighlightAction());  	} else { -		myLastMessageUIID_ = addMessage(body, QT_TRANSLATE_NOOP("", "me"), true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel().getLabel() : boost::shared_ptr<SecurityLabel>(), std::string(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time()); +		myLastMessageUIID_ = addMessage(body, QT_TRANSLATE_NOOP("", "me"), true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel().getLabel() : boost::shared_ptr<SecurityLabel>(), std::string(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time(), HighlightAction());  	}  	if (stanzaChannel_->getStreamManagementEnabled() && !myLastMessageUIID_.empty() ) { diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h index 66ec37d..6021ec1 100644 --- a/Swift/Controllers/Chat/ChatController.h +++ b/Swift/Controllers/Chat/ChatController.h @@ -22,10 +22,11 @@ namespace Swift {  	class FileTransferController;  	class SettingsProvider;  	class HistoryController; +	class HighlightManager;  	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); +			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);  			virtual ~ChatController();  			virtual void setToJID(const JID& jid);  			virtual void setOnline(bool online); @@ -45,7 +46,7 @@ namespace Swift {  			bool isIncomingMessageFromMe(boost::shared_ptr<Message> message);  			void postSendMessage(const std::string &body, boost::shared_ptr<Stanza> sentStanza);  			void preHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent); -			void postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent); +			void postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const HighlightAction&);  			void preSendMessageRequest(boost::shared_ptr<Message>);  			std::string senderDisplayNameFromMessage(const JID& from);  			virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(boost::shared_ptr<Message>) const; diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp index 50709f7..ad7f76a 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.cpp +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -13,6 +13,7 @@  #include <boost/shared_ptr.hpp>  #include <boost/smart_ptr/make_shared.hpp>  #include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/numeric/conversion/cast.hpp>  #include <boost/algorithm/string.hpp>  #include <Swift/Controllers/Intl.h> @@ -30,15 +31,18 @@  #include <Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h>  #include <Swiften/Avatars/AvatarManager.h>  #include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h> +#include <Swift/Controllers/HighlightManager.h> +#include <Swift/Controllers/Highlighter.h>  namespace Swift { -ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory), entityCapsProvider_(entityCapsProvider), historyController_(historyController), mucRegistry_(mucRegistry) { +ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory), entityCapsProvider_(entityCapsProvider), historyController_(historyController), mucRegistry_(mucRegistry) {  	chatWindow_ = chatWindowFactory_->createChatWindow(toJID, eventStream);  	chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this));  	chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2));  	chatWindow_->onLogCleared.connect(boost::bind(&ChatControllerBase::handleLogCleared, this));  	entityCapsProvider_->onCapsChanged.connect(boost::bind(&ChatControllerBase::handleCapsChanged, this, _1)); +	highlighter_ = highlightManager->createHighlighter();  	setOnline(stanzaChannel->isAvailable() && iqRouter->isAvailable());  	createDayChangeTimer();  } @@ -62,7 +66,7 @@ void ChatControllerBase::createDayChangeTimer() {  		boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();  		boost::posix_time::ptime midnight(now.date() + boost::gregorian::days(1));  		long millisecondsUntilMidnight = (midnight - now).total_milliseconds(); -		dateChangeTimer_ = boost::shared_ptr<Timer>(timerFactory_->createTimer(millisecondsUntilMidnight)); +		dateChangeTimer_ = boost::shared_ptr<Timer>(timerFactory_->createTimer(boost::numeric_cast<int>(millisecondsUntilMidnight)));  		dateChangeTimer_->onTick.connect(boost::bind(&ChatControllerBase::handleDayChangeTick, this));  		dateChangeTimer_->start();  	} @@ -112,7 +116,7 @@ void ChatControllerBase::handleAllMessagesRead() {  }  int ChatControllerBase::getUnreadCount() { -	return targetedUnreadMessages_.size(); +	return boost::numeric_cast<int>(targetedUnreadMessages_.size());  }  void ChatControllerBase::handleSendMessageRequest(const std::string &body, bool isCorrectionMessage) { @@ -175,19 +179,19 @@ void ChatControllerBase::activateChatWindow() {  	chatWindow_->activate();  } -std::string ChatControllerBase::addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, const boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time) { +std::string ChatControllerBase::addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, const boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) {  	if (boost::starts_with(message, "/me ")) { -		return chatWindow_->addAction(String::getSplittedAtFirst(message, ' ').second, senderName, senderIsSelf, label, avatarPath, time); +		return chatWindow_->addAction(String::getSplittedAtFirst(message, ' ').second, senderName, senderIsSelf, label, avatarPath, time, highlight);  	} else { -		return chatWindow_->addMessage(message, senderName, senderIsSelf, label, avatarPath, time); +		return chatWindow_->addMessage(message, senderName, senderIsSelf, label, avatarPath, time, highlight);  	}  } -void ChatControllerBase::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { +void ChatControllerBase::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) {  	if (boost::starts_with(message, "/me ")) { -		chatWindow_->replaceWithAction(String::getSplittedAtFirst(message, ' ').second, id, time); +		chatWindow_->replaceWithAction(String::getSplittedAtFirst(message, ' ').second, id, time, highlight);  	} else { -		chatWindow_->replaceMessage(message, id, time); +		chatWindow_->replaceMessage(message, id, time, highlight);  	}  } @@ -205,6 +209,7 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m  	}  	boost::shared_ptr<Message> message = messageEvent->getStanza();  	std::string body = message->getBody(); +	HighlightAction highlight;  	if (message->isError()) {  		std::string errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't send message: %1%")) % getErrorMessage(message->getPayload<ErrorPayload>()));  		chatWindow_->addErrorMessage(errorMessage); @@ -243,6 +248,11 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m  		}  		onActivity(body); +		// Highlight +		if (!isIncomingMessageFromMe(message)) { +			 highlight = highlighter_->findAction(body, senderDisplayNameFromMessage(from)); +		} +   		boost::shared_ptr<Replace> replace = message->getPayload<Replace>();  		if (replace) {  			std::string body = message->getBody(); @@ -250,19 +260,19 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m  			std::map<JID, std::string>::iterator lastMessage;  			lastMessage = lastMessagesUIID_.find(from);  			if (lastMessage != lastMessagesUIID_.end()) { -				replaceMessage(body, lastMessagesUIID_[from], timeStamp); +				replaceMessage(body, lastMessagesUIID_[from], timeStamp, highlight);  			}  		}  		else { -			lastMessagesUIID_[from] = addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), label, std::string(avatarManager_->getAvatarPath(from).string()), timeStamp); +			lastMessagesUIID_[from] = addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), label, std::string(avatarManager_->getAvatarPath(from).string()), timeStamp, highlight);  		}  		logMessage(body, from, selfJID_, timeStamp, true);  	}  	chatWindow_->show(); -	chatWindow_->setUnreadMessageCount(unreadMessages_.size()); +	chatWindow_->setUnreadMessageCount(boost::numeric_cast<int>(unreadMessages_.size()));  	onUnreadCountChanged(); -	postHandleIncomingMessage(messageEvent); +	postHandleIncomingMessage(messageEvent, highlight);  }  std::string ChatControllerBase::getErrorMessage(boost::shared_ptr<ErrorPayload> error) { @@ -296,13 +306,14 @@ std::string ChatControllerBase::getErrorMessage(boost::shared_ptr<ErrorPayload>  			case ErrorPayload::UnexpectedRequest: return QT_TRANSLATE_NOOP("", "Unexpected request");  		}  	} +	assert(false);  	return defaultMessage;  }  void ChatControllerBase::handleGeneralMUCInvitation(MUCInviteEvent::ref event) {  	unreadMessages_.push_back(event);  	chatWindow_->show(); -	chatWindow_->setUnreadMessageCount(unreadMessages_.size()); +	chatWindow_->setUnreadMessageCount(boost::numeric_cast<int>(unreadMessages_.size()));  	onUnreadCountChanged();  	chatWindow_->addMUCInvitation(senderDisplayNameFromMessage(event->getInviter()), event->getRoomJID(), event->getReason(), event->getPassword(), event->getDirect());  	eventController_->handleIncomingEvent(event); diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h index b26af02..baef9e6 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.h +++ b/Swift/Controllers/Chat/ChatControllerBase.h @@ -29,6 +29,7 @@  #include "Swiften/Base/IDGenerator.h"  #include <Swift/Controllers/HistoryController.h>  #include <Swiften/MUC/MUCRegistry.h> +#include <Swift/Controllers/HighlightManager.h>  namespace Swift {  	class IQRouter; @@ -39,6 +40,8 @@ namespace Swift {  	class UIEventStream;  	class EventController;  	class EntityCapsProvider; +	class HighlightManager; +	class Highlighter;  	class ChatControllerBase : public boost::bsignals::trackable {  		public: @@ -47,11 +50,11 @@ namespace Swift {  			void activateChatWindow();  			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); -			void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time); +			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);  			virtual void setOnline(bool online);  			virtual void setEnabled(bool enabled); -			virtual void setToJID(const JID& jid) {toJID_ = jid;}; +			virtual void setToJID(const JID& jid) {toJID_ = jid;}  			/** Used for determining when something is recent.*/  			boost::signal<void (const std::string& /*activity*/)> onActivity;  			boost::signal<void ()> onUnreadCountChanged; @@ -60,20 +63,20 @@ namespace Swift {  			void handleCapsChanged(const JID& jid);  		protected: -			ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry); +			ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager);  			/**  			 * Pass the Message appended, and the stanza used to send it.  			 */ -			virtual void postSendMessage(const std::string&, boost::shared_ptr<Stanza>) {}; +			virtual void postSendMessage(const std::string&, boost::shared_ptr<Stanza>) {}  			virtual std::string senderDisplayNameFromMessage(const JID& from) = 0;  			virtual bool isIncomingMessageFromMe(boost::shared_ptr<Message>) = 0; -			virtual void preHandleIncomingMessage(boost::shared_ptr<MessageEvent>) {}; -			virtual void postHandleIncomingMessage(boost::shared_ptr<MessageEvent>) {}; -			virtual void preSendMessageRequest(boost::shared_ptr<Message>) {}; +			virtual void preHandleIncomingMessage(boost::shared_ptr<MessageEvent>) {} +			virtual void postHandleIncomingMessage(boost::shared_ptr<MessageEvent>, const HighlightAction&) {} +			virtual void preSendMessageRequest(boost::shared_ptr<Message>) {}  			virtual bool isFromContact(const JID& from);  			virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(boost::shared_ptr<Message>) const = 0; -			virtual void dayTicked() {}; +			virtual void dayTicked() {}  			virtual void handleBareJIDCapsChanged(const JID& jid) = 0;  			std::string getErrorMessage(boost::shared_ptr<ErrorPayload>);  			virtual void setContactIsReceivingPresence(bool /* isReceivingPresence */) {} @@ -116,5 +119,6 @@ namespace Swift {  			SecurityLabelsCatalog::Item lastLabel_;   			HistoryController* historyController_;  			MUCRegistry* mucRegistry_; +			Highlighter* highlighter_;  	};  } diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index 1e0e9c2..dba8565 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -74,7 +74,8 @@ ChatsManager::ChatsManager(  		bool eagleMode,  		SettingsProvider* settings,  		HistoryController* historyController, -		WhiteboardManager* whiteboardManager) : +		WhiteboardManager* whiteboardManager, +		HighlightManager* highlightManager) :  			jid_(jid),   			joinMUCWindowFactory_(joinMUCWindowFactory),   			useDelayForLatency_(useDelayForLatency),  @@ -86,7 +87,8 @@ ChatsManager::ChatsManager(  			eagleMode_(eagleMode),  			settings_(settings),  			historyController_(historyController), -			whiteboardManager_(whiteboardManager) { +			whiteboardManager_(whiteboardManager), +			highlightManager_(highlightManager) {  	timerFactory_ = timerFactory;  	eventController_ = eventController;  	stanzaChannel_ = stanzaChannel; @@ -521,7 +523,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_); +	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_);  	chatControllers_[contact] = controller;  	controller->setAvailableServerFeatures(serverDiscoInfo_);  	controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, contact, _1, false)); @@ -594,7 +596,7 @@ void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional  		if (createAsReservedIfNew) {  			muc->setCreateAsReservedIfNew();  		} -		MUCController* controller = new MUCController(jid_, muc, password, nick, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_, roster_, historyController_, mucRegistry_); +		MUCController* controller = new MUCController(jid_, muc, password, nick, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_, roster_, historyController_, mucRegistry_, highlightManager_);  		mucControllers_[mucJID] = controller;  		controller->setAvailableServerFeatures(serverDiscoInfo_);  		controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller)); diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h index 5b8b785..55e62b9 100644 --- a/Swift/Controllers/Chat/ChatsManager.h +++ b/Swift/Controllers/Chat/ChatsManager.h @@ -50,10 +50,11 @@ namespace Swift {  	class SettingsProvider;  	class WhiteboardManager;  	class HistoryController; +	class HighlightManager;  	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); +			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);  			virtual ~ChatsManager();  			void setAvatarManager(AvatarManager* avatarManager);  			void setOnline(bool enabled); @@ -136,5 +137,6 @@ namespace Swift {  			SettingsProvider* settings_;  			HistoryController* historyController_;  			WhiteboardManager* whiteboardManager_; +			HighlightManager* highlightManager_;  	};  } diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp index 50eee68..a61b5f0 100644 --- a/Swift/Controllers/Chat/MUCController.cpp +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -24,6 +24,7 @@  #include <Swift/Controllers/UIEvents/UIEventStream.h>  #include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>  #include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h> +#include <Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h>  #include <Swift/Controllers/Roster/GroupRosterItem.h>  #include <Swift/Controllers/Roster/ContactRosterItem.h>  #include <Swiften/Avatars/AvatarManager.h> @@ -35,6 +36,7 @@  #include <Swift/Controllers/Roster/SetPresence.h>  #include <Swiften/Disco/EntityCapsProvider.h>  #include <Swiften/Roster/XMPPRoster.h> +#include <Swift/Controllers/Highlighter.h>  #define MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS 60000 @@ -61,8 +63,9 @@ MUCController::MUCController (  		EntityCapsProvider* entityCapsProvider,  		XMPPRoster* roster,  		HistoryController* historyController, -		MUCRegistry* mucRegistry) : -			ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry), muc_(muc), nick_(nick), desiredNick_(nick), password_(password) { +		MUCRegistry* mucRegistry, +		HighlightManager* highlightManager) : +			ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry, highlightManager), muc_(muc), nick_(nick), desiredNick_(nick), password_(password) {  	parting_ = true;  	joined_ = false;  	lastWasPresence_ = false; @@ -98,6 +101,8 @@ MUCController::MUCController (  	muc_->onConfigurationFormReceived.connect(boost::bind(&MUCController::handleConfigurationFormReceived, this, _1));  	muc_->onRoleChangeFailed.connect(boost::bind(&MUCController::handleOccupantRoleChangeFailed, this, _1, _2, _3));  	muc_->onAffiliationListReceived.connect(boost::bind(&MUCController::handleAffiliationListReceived, this, _1, _2)); +	highlighter_->setMode(Highlighter::MUCMode); +	highlighter_->setNick(nick_);  	if (timerFactory) {  		loginCheckTimer_ = boost::shared_ptr<Timer>(timerFactory->createTimer(MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS));  		loginCheckTimer_->onTick.connect(boost::bind(&MUCController::handleJoinTimeoutTick, this)); @@ -146,6 +151,7 @@ void MUCController::handleWindowOccupantSelectionChanged(ContactRosterItem* item  		if (muc_->getOccupant(item->getJID().getResource()).getRealJID()) {  			actions.push_back(ChatWindow::AddContact);  		} +		actions.push_back(ChatWindow::ShowProfile);  	}  	chatWindow_->setAvailableOccupantActions(actions);  } @@ -164,6 +170,7 @@ void MUCController::handleActionRequestedOnOccupant(ChatWindow::OccupantAction a  		case ChatWindow::MakeParticipant: muc_->changeOccupantRole(mucJID, MUCOccupant::Participant);break;  		case ChatWindow::MakeVisitor: muc_->changeOccupantRole(mucJID, MUCOccupant::Visitor);break;  		case ChatWindow::AddContact: if (occupant.getRealJID()) events_->send(boost::make_shared<RequestAddUserDialogUIEvent>(realJID, occupant.getNick()));break; +		case ChatWindow::ShowProfile: events_->send(boost::make_shared<ShowProfileForRosterItemUIEvent>(mucJID));break;  	}  } @@ -228,6 +235,9 @@ void MUCController::receivedActivity() {  	}  } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wswitch-enum" +  void MUCController::handleJoinFailed(boost::shared_ptr<ErrorPayload> error) {  	receivedActivity();  	std::string errorMessage = QT_TRANSLATE_NOOP("", "Unable to enter this room"); @@ -270,16 +280,18 @@ void MUCController::handleJoinFailed(boost::shared_ptr<ErrorPayload> error) {  	chatWindow_->addErrorMessage(errorMessage);  	parting_ = true;  	if (!rejoinNick.empty()) { -		nick_ = rejoinNick; +		setNick(rejoinNick);  		rejoin();  	}  } +#pragma clang diagnostic pop +  void MUCController::handleJoinComplete(const std::string& nick) {  	receivedActivity();  	joined_ = true;  	std::string joinMessage = str(format(QT_TRANSLATE_NOOP("", "You have entered room %1% as %2%.")) % toJID_.toString() % nick); -	nick_ = nick; +	setNick(nick);  	chatWindow_->addSystemMessage(joinMessage);  #ifdef SWIFT_EXPERIMENTAL_HISTORY @@ -386,6 +398,7 @@ std::string MUCController::roleToFriendlyName(MUCOccupant::Role role) {  	case MUCOccupant::Visitor: return QT_TRANSLATE_NOOP("", "visitor");  	case MUCOccupant::NoRole: return "";  	} +	assert(false);  	return "";  } @@ -396,6 +409,7 @@ std::string MUCController::roleToSortName(MUCOccupant::Role role) {  	case MUCOccupant::Visitor: return "3";  	case MUCOccupant::NoRole: return "4";  	} +	assert(false);  	return "5";  } @@ -448,10 +462,13 @@ void MUCController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> mes  	}  } -void MUCController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) { +void MUCController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const HighlightAction& highlight) {  	boost::shared_ptr<Message> message = messageEvent->getStanza();  	if (joined_ && messageEvent->getStanza()->getFrom().getResource() != nick_ && messageTargetsMe(message) && !message->getPayload<Delay>()) {  		eventController_->handleIncomingEvent(messageEvent); +		if (!messageEvent->getConcluded()) { +			highlighter_->handleHighlightAction(highlight); +		}  	}  } @@ -487,7 +504,6 @@ std::string MUCController::roleToGroupName(MUCOccupant::Role role) {  	case MUCOccupant::Participant: result = QT_TRANSLATE_NOOP("", "Participants"); break;  	case MUCOccupant::Visitor: result = QT_TRANSLATE_NOOP("", "Visitors"); break;  	case MUCOccupant::NoRole: result = QT_TRANSLATE_NOOP("", "Occupants"); break; -	default: assert(false);  	}  	return result;  } @@ -504,7 +520,7 @@ void MUCController::setOnline(bool online) {  			if (loginCheckTimer_) {  				loginCheckTimer_->start();  			} -			nick_ = desiredNick_; +			setNick(desiredNick_);  			rejoin();  		}  	} @@ -611,7 +627,8 @@ void MUCController::appendToJoinParts(std::vector<NickJoinPart>& joinParts, cons  			switch (newEvent.type) {  				case Join: type = (type == Part) ? PartThenJoin : Join; break;  				case Part: type = (type == Join) ? JoinThenPart : Part; break; -				default: /*Nothing to see here */;break; +				case PartThenJoin: break; +				case JoinThenPart: break;  			}  			(*it).type = type;  			break; @@ -811,7 +828,7 @@ void MUCController::addRecentLogs() {  		bool senderIsSelf = nick_ == message.getFromJID().getResource();  		// the chatWindow uses utc timestamps -		addMessage(message.getMessage(), senderDisplayNameFromMessage(message.getFromJID()), senderIsSelf, boost::shared_ptr<SecurityLabel>(new SecurityLabel()), std::string(avatarManager_->getAvatarPath(message.getFromJID()).string()), message.getTime() - boost::posix_time::hours(message.getOffset())); +		addMessage(message.getMessage(), senderDisplayNameFromMessage(message.getFromJID()), senderIsSelf, boost::shared_ptr<SecurityLabel>(new SecurityLabel()), std::string(avatarManager_->getAvatarPath(message.getFromJID()).string()), message.getTime() - boost::posix_time::hours(message.getOffset()), HighlightAction());  	}  } @@ -840,4 +857,10 @@ void MUCController::checkDuplicates(boost::shared_ptr<Message> newMessage) {  	}  } +void MUCController::setNick(const std::string& nick) +{ +	nick_ = nick; +	highlighter_->setNick(nick_); +} +  } diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h index 63893d0..11fe0ff 100644 --- a/Swift/Controllers/Chat/MUCController.h +++ b/Swift/Controllers/Chat/MUCController.h @@ -33,18 +33,19 @@ namespace Swift {  	class TabComplete;  	class InviteToChatWindow;  	class XMPPRoster; +	class HighlightManager;  	enum JoinPart {Join, Part, JoinThenPart, PartThenJoin};  	struct NickJoinPart { -			NickJoinPart(const std::string& nick, JoinPart type) : nick(nick), type(type) {}; +			NickJoinPart(const std::string& nick, JoinPart type) : nick(nick), type(type) {}  			std::string nick;  			JoinPart type;  	};  	class MUCController : public ChatControllerBase {  		public: -			MUCController(const JID& self, MUC::ref muc, const boost::optional<std::string>& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* roster, HistoryController* historyController, MUCRegistry* mucRegistry); +			MUCController(const JID& self, MUC::ref muc, const boost::optional<std::string>& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* roster, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager);  			~MUCController();  			boost::signal<void ()> onUserLeft;  			boost::signal<void ()> onUserJoined; @@ -62,7 +63,7 @@ namespace Swift {  			std::string senderDisplayNameFromMessage(const JID& from);  			boost::optional<boost::posix_time::ptime> getMessageTimestamp(boost::shared_ptr<Message> message) const;  			void preHandleIncomingMessage(boost::shared_ptr<MessageEvent>); -			void postHandleIncomingMessage(boost::shared_ptr<MessageEvent>); +			void postHandleIncomingMessage(boost::shared_ptr<MessageEvent>, const HighlightAction&);  			void cancelReplaces();  			void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming); @@ -108,6 +109,7 @@ namespace Swift {  			void handleInviteToMUCWindowCompleted();  			void addRecentLogs();  			void checkDuplicates(boost::shared_ptr<Message> newMessage); +			void setNick(const std::string& nick);  		private:  			MUC::ref muc_; diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index 482b19c..dd90d66 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -106,14 +106,16 @@ public:  		avatarManager_ = new NullAvatarManager();  		wbSessionManager_ = new WhiteboardSessionManager(iqRouter_, stanzaChannel_, presenceOracle_, entityCapsManager_);  		wbManager_ = new WhiteboardManager(whiteboardWindowFactory_, uiEventStream_, nickResolver_, wbSessionManager_); +		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_); +		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_);  		manager_->setAvatarManager(avatarManager_); -	}; +	}  	void tearDown() { +		delete highlightManager_;  		//delete chatListWindowFactory  		delete profileSettings_;  		delete avatarManager_; @@ -481,6 +483,7 @@ private:  	FileTransferManager* ftManager_;  	WhiteboardSessionManager* wbSessionManager_;  	WhiteboardManager* wbManager_; +	HighlightManager* highlightManager_;  };  CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest); diff --git a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp index 4f37229..f1fcf79 100644 --- a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp @@ -26,6 +26,7 @@  #include "Swiften/Network/TimerFactory.h"  #include "Swiften/Elements/MUCUserPayload.h"  #include "Swiften/Disco/DummyEntityCapsProvider.h" +#include <Swift/Controllers/Settings/DummySettingsProvider.h>  using namespace Swift; @@ -62,12 +63,16 @@ public:  		window_ = new MockChatWindow();  		mucRegistry_ = new MUCRegistry();  		entityCapsProvider_ = new DummyEntityCapsProvider(); +		settings_ = new DummySettingsProvider(); +		highlightManager_ = new HighlightManager(settings_);  		muc_ = boost::make_shared<MUC>(stanzaChannel_, iqRouter_, directedPresenceSender_, mucJID_, mucRegistry_);  		mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(muc_->getJID(), uiEventStream_).Return(window_); -		controller_ = new MUCController (self_, muc_, boost::optional<std::string>(), nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_, entityCapsProvider_, NULL, NULL, mucRegistry_); -	}; +		controller_ = new MUCController (self_, muc_, boost::optional<std::string>(), nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_, entityCapsProvider_, NULL, NULL, mucRegistry_, highlightManager_); +	}  	void tearDown() { +		delete highlightManager_; +		delete settings_;  		delete entityCapsProvider_;  		delete controller_;  		delete eventController_; @@ -338,6 +343,8 @@ private:  	MockChatWindow* window_;  	MUCRegistry* mucRegistry_;  	DummyEntityCapsProvider* entityCapsProvider_; +	DummySettingsProvider* settings_; +	HighlightManager* highlightManager_;  };  CPPUNIT_TEST_SUITE_REGISTRATION(MUCControllerTest); diff --git a/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h b/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h index 5bbd490..5fa264d 100644 --- a/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h +++ b/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h @@ -12,12 +12,12 @@ namespace Swift {  	class MockChatListWindow : public ChatListWindow {  		public: -			MockChatListWindow() {}; -			virtual ~MockChatListWindow() {}; +			MockChatListWindow() {} +			virtual ~MockChatListWindow() {}  			void addMUCBookmark(const MUCBookmark& /*bookmark*/) {}  			void removeMUCBookmark(const MUCBookmark& /*bookmark*/) {} -			void addWhiteboardSession(const ChatListWindow::Chat& /*chat*/) {}; -			void removeWhiteboardSession(const JID& /*jid*/) {}; +			void addWhiteboardSession(const ChatListWindow::Chat& /*chat*/) {} +			void removeWhiteboardSession(const JID& /*jid*/) {}  			void setBookmarksEnabled(bool /*enabled*/) {}  			void setRecents(const std::list<ChatListWindow::Chat>& /*recents*/) {}  			void setUnreadCount(int /*unread*/) {} diff --git a/Swift/Controllers/DummySoundPlayer.h b/Swift/Controllers/DummySoundPlayer.h index 36dcb28..b91192c 100644 --- a/Swift/Controllers/DummySoundPlayer.h +++ b/Swift/Controllers/DummySoundPlayer.h @@ -11,6 +11,6 @@  namespace Swift {  	class DummySoundPlayer : public SoundPlayer {  		public: -			void playSound(SoundEffect sound) {}; +			void playSound(SoundEffect sound) {}  	};  } diff --git a/Swift/Controllers/DummySystemTray.h b/Swift/Controllers/DummySystemTray.h index 41da4cd..451588e 100644 --- a/Swift/Controllers/DummySystemTray.h +++ b/Swift/Controllers/DummySystemTray.h @@ -11,8 +11,8 @@  namespace Swift {  	class DummySystemTray : public SystemTray {  	public: -		void setUnreadMessages(bool some) {}; -		void setStatusType(StatusShow::Type type) {}; +		void setUnreadMessages(bool some) {} +		void setStatusType(StatusShow::Type type) {}  		void setConnecting() {}  	};  } diff --git a/Swift/Controllers/FileTransfer/FileTransferController.cpp b/Swift/Controllers/FileTransfer/FileTransferController.cpp index 3feaf49..69b5c89 100644 --- a/Swift/Controllers/FileTransfer/FileTransferController.cpp +++ b/Swift/Controllers/FileTransfer/FileTransferController.cpp @@ -10,6 +10,7 @@  #include <Swiften/FileTransfer/FileReadBytestream.h>  #include <Swiften/Base/boost_bsignals.h>  #include <boost/bind.hpp> +#include <boost/filesystem.hpp>  #include "Swift/Controllers/UIInterfaces/ChatWindow.h"  #include <Swiften/Base/Log.h>  #include <Swift/Controllers/Intl.h> @@ -138,7 +139,7 @@ void FileTransferController::handleFileTransferStateChange(FileTransfer::State s  		case FileTransfer::State::WaitingForStart:  			return;  	} -	std::cerr << "Unhandled FileTransfer::State!" << std::endl; +	assert(false);  }  void FileTransferController::handleProgressPercentageChange(int percentage) { diff --git a/Swift/Controllers/FileTransfer/FileTransferProgressInfo.cpp b/Swift/Controllers/FileTransfer/FileTransferProgressInfo.cpp index 6d19fa1..3081f71 100644 --- a/Swift/Controllers/FileTransfer/FileTransferProgressInfo.cpp +++ b/Swift/Controllers/FileTransfer/FileTransferProgressInfo.cpp @@ -6,6 +6,8 @@  #include "FileTransferProgressInfo.h" +#include <boost/numeric/conversion/cast.hpp> +  #include <Swiften/Base/Log.h>  namespace Swift { @@ -16,7 +18,7 @@ FileTransferProgressInfo::FileTransferProgressInfo(boost::uintmax_t completeByte  void FileTransferProgressInfo::setBytesProcessed(int processedBytes) {  	int oldPercentage = int(double(completedBytes) / double(completeBytes) * 100.0); -	completedBytes += processedBytes; +	completedBytes += boost::numeric_cast<boost::uintmax_t>(processedBytes);  	int newPercentage = int(double(completedBytes) / double(completeBytes) * 100.0);  	if (oldPercentage != newPercentage) {  		onProgressPercentage(newPercentage); diff --git a/Swift/Controllers/HighlightAction.cpp b/Swift/Controllers/HighlightAction.cpp new file mode 100644 index 0000000..d4d199d --- /dev/null +++ b/Swift/Controllers/HighlightAction.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <Swift/Controllers/HighlightAction.h> + +namespace Swift { + +void HighlightAction::setHighlightText(bool highlightText) +{ +	highlightText_ = highlightText; +	if (!highlightText_) { +		textColor_.clear(); +		textBackground_.clear(); +	} +} + +void HighlightAction::setPlaySound(bool playSound) +{ +	playSound_ = playSound; +	if (!playSound_) { +		soundFile_.clear(); +	} +} + +} diff --git a/Swift/Controllers/HighlightAction.h b/Swift/Controllers/HighlightAction.h new file mode 100644 index 0000000..bfbed74 --- /dev/null +++ b/Swift/Controllers/HighlightAction.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <string> + +namespace Swift { + +	class HighlightRule; + +	class HighlightAction { +		public: +			HighlightAction() : highlightText_(false), playSound_(false) {} + +			bool highlightText() const { return highlightText_; } +			void setHighlightText(bool highlightText); + +			const std::string& getTextColor() const { return textColor_; } +			void setTextColor(const std::string& textColor) { textColor_ = textColor; } + +			const std::string& getTextBackground() const { return textBackground_; } +			void setTextBackground(const std::string& textBackground) { textBackground_ = textBackground; } + +			bool playSound() const { return playSound_; } +			void setPlaySound(bool playSound); + +			const std::string& getSoundFile() const { return soundFile_; } +			void setSoundFile(const std::string& soundFile) { soundFile_ = soundFile; } + +			bool isEmpty() const { return !highlightText_ && !playSound_; } + +		private: +			bool highlightText_; +			std::string textColor_; +			std::string textBackground_; + +			bool playSound_; +			std::string soundFile_; +	}; + +} diff --git a/Swift/Controllers/HighlightEditorController.cpp b/Swift/Controllers/HighlightEditorController.cpp new file mode 100644 index 0000000..899e4bb --- /dev/null +++ b/Swift/Controllers/HighlightEditorController.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <boost/bind.hpp> + +#include <Swift/Controllers/HighlightEditorController.h> +#include <Swift/Controllers/UIInterfaces/HighlightEditorWidget.h> +#include <Swift/Controllers/UIInterfaces/HighlightEditorWidgetFactory.h> +#include <Swift/Controllers/UIEvents/RequestHighlightEditorUIEvent.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> + +namespace Swift { + +HighlightEditorController::HighlightEditorController(UIEventStream* uiEventStream, HighlightEditorWidgetFactory* highlightEditorWidgetFactory, HighlightManager* highlightManager) : highlightEditorWidgetFactory_(highlightEditorWidgetFactory), highlightEditorWidget_(NULL), highlightManager_(highlightManager) +{ +	uiEventStream->onUIEvent.connect(boost::bind(&HighlightEditorController::handleUIEvent, this, _1)); +} + +HighlightEditorController::~HighlightEditorController() +{ +	delete highlightEditorWidget_; +	highlightEditorWidget_ = NULL; +} + +void HighlightEditorController::handleUIEvent(boost::shared_ptr<UIEvent> rawEvent) +{ +	boost::shared_ptr<RequestHighlightEditorUIEvent> event = boost::dynamic_pointer_cast<RequestHighlightEditorUIEvent>(rawEvent); +	if (event) { +		if (!highlightEditorWidget_) { +			highlightEditorWidget_ = highlightEditorWidgetFactory_->createHighlightEditorWidget(); +			highlightEditorWidget_->setHighlightManager(highlightManager_); +		} +		highlightEditorWidget_->show(); +	} +} + +} diff --git a/Swift/Controllers/HighlightEditorController.h b/Swift/Controllers/HighlightEditorController.h new file mode 100644 index 0000000..3868251 --- /dev/null +++ b/Swift/Controllers/HighlightEditorController.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <boost/shared_ptr.hpp> + +#include <Swift/Controllers/UIEvents/UIEvent.h> + +namespace Swift { + +	class UIEventStream; + +	class HighlightEditorWidgetFactory; +	class HighlightEditorWidget; + +	class HighlightManager; + +	class HighlightEditorController { +		public: +			HighlightEditorController(UIEventStream* uiEventStream, HighlightEditorWidgetFactory* highlightEditorWidgetFactory, HighlightManager* highlightManager); +			~HighlightEditorController(); + +			HighlightManager* getHighlightManager() const { return highlightManager_; } + +		private: +			void handleUIEvent(boost::shared_ptr<UIEvent> event); + +		private: +			HighlightEditorWidgetFactory* highlightEditorWidgetFactory_; +			HighlightEditorWidget* highlightEditorWidget_; +			HighlightManager* highlightManager_; +	}; + +} diff --git a/Swift/Controllers/HighlightManager.cpp b/Swift/Controllers/HighlightManager.cpp new file mode 100644 index 0000000..7ab578e --- /dev/null +++ b/Swift/Controllers/HighlightManager.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cassert> + +#include <boost/algorithm/string.hpp> +#include <boost/regex.hpp> +#include <boost/bind.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <Swiften/Base/foreach.h> +#include <Swift/Controllers/HighlightManager.h> +#include <Swift/Controllers/Highlighter.h> +#include <Swift/Controllers/Settings/SettingsProvider.h> +#include <Swift/Controllers/SettingConstants.h> + +/* How does highlighting work? + * + * HighlightManager manages a list of if-then rules used to highlight messages. + * Rule is represented by HighlightRule. Action ("then" part) is HighlightAction. + * + * + * HighlightManager is also used as a factory for Highlighter objects. + * Each ChatControllerBase has its own Highlighter. + * Highligher may be customized by using setNick(), etc. + * + * ChatControllerBase passes incoming messages to Highlighter and gets HighlightAction in return + * (first matching rule is returned). + * If needed, HighlightAction is then passed back to Highlighter for further handling. + * This results in HighlightManager emiting onHighlight event, + * which is handled by SoundController to play sound notification + */ + +namespace Swift { + +HighlightManager::HighlightManager(SettingsProvider* settings) +	: settings_(settings) +	, storingSettings_(false) +{ +	loadSettings(); +	settings_->onSettingChanged.connect(boost::bind(&HighlightManager::handleSettingChanged, this, _1)); +} + +void HighlightManager::handleSettingChanged(const std::string& settingPath) +{ +	if (!storingSettings_ && SettingConstants::HIGHLIGHT_RULES.getKey() == settingPath) { +		loadSettings(); +	} +} + +void HighlightManager::loadSettings() +{ +	std::string highlightRules = settings_->getSetting(SettingConstants::HIGHLIGHT_RULES); +	if (highlightRules == "@") { +		rules_ = getDefaultRules(); +	} else { +		rules_ = rulesFromString(highlightRules); +	} +} + +std::string HighlightManager::rulesToString() const +{ +	std::string s; +	foreach (HighlightRule r, rules_) { +		s += r.toString() + '\f'; +	} +	if (s.size()) { +		s.erase(s.end() - 1); +	} +	return s; +} + +std::vector<HighlightRule> HighlightManager::rulesFromString(const std::string& rulesString) +{ +	std::vector<HighlightRule> rules; +	std::string s(rulesString); +	typedef boost::split_iterator<std::string::iterator> split_iterator; +	for (split_iterator it = boost::make_split_iterator(s, boost::first_finder("\f")); it != split_iterator(); ++it) { +		HighlightRule r = HighlightRule::fromString(boost::copy_range<std::string>(*it)); +		if (!r.isEmpty()) { +			rules.push_back(r); +		} +	} +	return rules; +} + +std::vector<HighlightRule> HighlightManager::getDefaultRules() +{ +	std::vector<HighlightRule> rules; +	HighlightRule r; +	r.setMatchChat(true); +	r.getAction().setPlaySound(true); +	rules.push_back(r); +	return rules; +} + +void HighlightManager::storeSettings() +{ +	storingSettings_ = true;	// don't reload settings while saving +	settings_->storeSetting(SettingConstants::HIGHLIGHT_RULES, rulesToString()); +	storingSettings_ = false; +} + +HighlightRule HighlightManager::getRule(int index) const +{ +	assert(index >= 0 && static_cast<size_t>(index) < rules_.size()); +	return rules_[static_cast<size_t>(index)]; +} + +void HighlightManager::setRule(int index, const HighlightRule& rule) +{ +	assert(index >= 0 && static_cast<size_t>(index) < rules_.size()); +	rules_[static_cast<size_t>(index)] = rule; +	storeSettings(); +} + +void HighlightManager::insertRule(int index, const HighlightRule& rule) +{ +	assert(index >= 0 && boost::numeric_cast<std::vector<std::string>::size_type>(index) <= rules_.size()); +	rules_.insert(rules_.begin() + index, rule); +	storeSettings(); +} + +void HighlightManager::removeRule(int index) +{ +	assert(index >= 0 && boost::numeric_cast<std::vector<std::string>::size_type>(index) < rules_.size()); +	rules_.erase(rules_.begin() + index); +	storeSettings(); +} + +Highlighter* HighlightManager::createHighlighter() +{ +	return new Highlighter(this); +} + +} diff --git a/Swift/Controllers/HighlightManager.h b/Swift/Controllers/HighlightManager.h new file mode 100644 index 0000000..d195d05 --- /dev/null +++ b/Swift/Controllers/HighlightManager.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <vector> +#include <string> + +#include <Swiften/Base/boost_bsignals.h> +#include <Swift/Controllers/HighlightRule.h> + +namespace Swift { + +	class SettingsProvider; +	class Highlighter; + +	class HighlightManager { +		public: +			HighlightManager(SettingsProvider* settings); + +			Highlighter* createHighlighter(); + +			const std::vector<HighlightRule>& getRules() const { return rules_; } +			HighlightRule getRule(int index) const; +			void setRule(int index, const HighlightRule& rule); +			void insertRule(int index, const HighlightRule& rule); +			void removeRule(int index); + +			boost::signal<void (const HighlightAction&)> onHighlight; + +		private: +			void handleSettingChanged(const std::string& settingPath); + +			std::string rulesToString() const; +			static std::vector<HighlightRule> rulesFromString(const std::string&); +			static std::vector<HighlightRule> getDefaultRules(); + +			SettingsProvider* settings_; +			bool storingSettings_; +			void storeSettings(); +			void loadSettings(); + +			std::vector<HighlightRule> rules_; +	}; + +} diff --git a/Swift/Controllers/HighlightRule.cpp b/Swift/Controllers/HighlightRule.cpp new file mode 100644 index 0000000..28f26cf --- /dev/null +++ b/Swift/Controllers/HighlightRule.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <algorithm> +#include <boost/algorithm/string.hpp> +#include <boost/lambda/lambda.hpp> + +#include <Swiften/Base/foreach.h> +#include <Swift/Controllers/HighlightRule.h> + +namespace Swift { + +HighlightRule::HighlightRule() +	: nickIsKeyword_(false) +	, matchCase_(false) +	, matchWholeWords_(false) +	, matchChat_(false) +	, matchMUC_(false) +{ +} + +boost::regex HighlightRule::regexFromString(const std::string & s) const +{ +	// escape regex special characters: ^.$| etc +	// these need to be escaped: [\^\$\|.........] +	// and then C++ requires '\' to be escaped, too.... +	static const boost::regex esc("([\\^\\.\\$\\|\\(\\)\\[\\]\\*\\+\\?\\/\\{\\}\\\\])"); +	// matched character should be prepended with '\' +	// replace matched special character with \\\1 +	// and escape once more for C++ rules... +	static const std::string rep("\\\\\\1"); +	std::string escaped = boost::regex_replace(s , esc, rep); + +	std::string word = matchWholeWords_ ? "\\b" : ""; +	boost::regex::flag_type flags = boost::regex::normal; +	if (!matchCase_) { +		flags |= boost::regex::icase; +	} +	return boost::regex(word + escaped + word, flags); +} + +void HighlightRule::updateRegex() const +{ +	keywordRegex_.clear(); +	foreach (const std::string & k, keywords_) { +		keywordRegex_.push_back(regexFromString(k)); +	} +	senderRegex_.clear(); +	foreach (const std::string & s, senders_) { +		senderRegex_.push_back(regexFromString(s)); +	} +} + +std::string HighlightRule::boolToString(bool b) +{ +	return b ? "1" : "0"; +} + +bool HighlightRule::boolFromString(const std::string& s) +{ +	return s == "1"; +} + +std::string HighlightRule::toString() const +{ +	std::vector<std::string> v; +	v.push_back(boost::join(senders_, "\t")); +	v.push_back(boost::join(keywords_, "\t")); +	v.push_back(boolToString(nickIsKeyword_)); +	v.push_back(boolToString(matchChat_)); +	v.push_back(boolToString(matchMUC_)); +	v.push_back(boolToString(matchCase_)); +	v.push_back(boolToString(matchWholeWords_)); +	v.push_back(boolToString(action_.highlightText())); +	v.push_back(action_.getTextColor()); +	v.push_back(action_.getTextBackground()); +	v.push_back(boolToString(action_.playSound())); +	v.push_back(action_.getSoundFile()); +	return boost::join(v, "\n"); +} + +HighlightRule HighlightRule::fromString(const std::string& s) +{ +	std::vector<std::string> v; +	boost::split(v, s, boost::is_any_of("\n")); + +	HighlightRule r; +	size_t i = 0; +	try { +		boost::split(r.senders_, v.at(i++), boost::is_any_of("\t")); +		r.senders_.erase(std::remove_if(r.senders_.begin(), r.senders_.end(), boost::lambda::_1 == ""), r.senders_.end()); +		boost::split(r.keywords_, v.at(i++), boost::is_any_of("\t")); +		r.keywords_.erase(std::remove_if(r.keywords_.begin(), r.keywords_.end(), boost::lambda::_1 == ""), r.keywords_.end()); +		r.nickIsKeyword_ = boolFromString(v.at(i++)); +		r.matchChat_ = boolFromString(v.at(i++)); +		r.matchMUC_ = boolFromString(v.at(i++)); +		r.matchCase_ = boolFromString(v.at(i++)); +		r.matchWholeWords_ = boolFromString(v.at(i++)); +		r.action_.setHighlightText(boolFromString(v.at(i++))); +		r.action_.setTextColor(v.at(i++)); +		r.action_.setTextBackground(v.at(i++)); +		r.action_.setPlaySound(boolFromString(v.at(i++))); +		r.action_.setSoundFile(v.at(i++)); +	} +	catch (std::out_of_range) { +		// this may happen if more properties are added to HighlightRule object, etc... +		// in such case, default values (set by default constructor) will be used +	} + +	r.updateRegex(); + +	return r; +} + +bool HighlightRule::isMatch(const std::string& body, const std::string& sender, const std::string& nick, MessageType messageType) const +{ +	if ((messageType == HighlightRule::ChatMessage && matchChat_) || (messageType == HighlightRule::MUCMessage && matchMUC_)) { + +		bool matchesKeyword = keywords_.empty() && (nick.empty() || !nickIsKeyword_); +		bool matchesSender = senders_.empty(); + +		foreach (const boost::regex & rx, keywordRegex_) { +			if (boost::regex_search(body, rx)) { +				matchesKeyword = true; +				break; +			} +		} + +		if (!matchesKeyword && nickIsKeyword_ && !nick.empty()) { +			if (boost::regex_search(body, regexFromString(nick))) { +				matchesKeyword = true; +			} +		} + +		foreach (const boost::regex & rx, senderRegex_) { +			if (boost::regex_search(sender, rx)) { +				matchesSender = true; +				break; +			} +		} + +		if (matchesKeyword && matchesSender) { +			return true; +		} +	} + +	return false; +} + +void HighlightRule::setSenders(const std::vector<std::string>& senders) +{ +	senders_ = senders; +	updateRegex(); +} + +void HighlightRule::setKeywords(const std::vector<std::string>& keywords) +{ +	keywords_ = keywords; +	updateRegex(); +} + +void HighlightRule::setNickIsKeyword(bool nickIsKeyword) +{ +	nickIsKeyword_ = nickIsKeyword; +	updateRegex(); +} + +void HighlightRule::setMatchCase(bool matchCase) +{ +	matchCase_ = matchCase; +	updateRegex(); +} + +void HighlightRule::setMatchWholeWords(bool matchWholeWords) +{ +	matchWholeWords_ = matchWholeWords; +	updateRegex(); +} + +void HighlightRule::setMatchChat(bool matchChat) +{ +	matchChat_ = matchChat; +	updateRegex(); +} + +void HighlightRule::setMatchMUC(bool matchMUC) +{ +	matchMUC_ = matchMUC; +	updateRegex(); +} + +bool HighlightRule::isEmpty() const +{ +	return senders_.empty() && keywords_.empty() && !nickIsKeyword_ && !matchChat_ && !matchMUC_ && action_.isEmpty(); +} + +} diff --git a/Swift/Controllers/HighlightRule.h b/Swift/Controllers/HighlightRule.h new file mode 100644 index 0000000..1abfa5a --- /dev/null +++ b/Swift/Controllers/HighlightRule.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <vector> +#include <string> + +#include <boost/regex.hpp> + +#include <Swift/Controllers/HighlightAction.h> + +namespace Swift { + +	class HighlightRule { +		public: +			HighlightRule(); + +			enum MessageType { ChatMessage, MUCMessage }; + +			bool isMatch(const std::string& body, const std::string& sender, const std::string& nick, MessageType) const; + +			const HighlightAction& getAction() const { return action_; } +			HighlightAction& getAction() { return action_; } + +			static HighlightRule fromString(const std::string&); +			std::string toString() const; + +			const std::vector<std::string>& getSenders() const { return senders_; } +			void setSenders(const std::vector<std::string>&); + +			const std::vector<std::string>& getKeywords() const { return keywords_; } +			void setKeywords(const std::vector<std::string>&); + +			bool getNickIsKeyword() const { return nickIsKeyword_; } +			void setNickIsKeyword(bool); + +			bool getMatchCase() const { return matchCase_; } +			void setMatchCase(bool); + +			bool getMatchWholeWords() const { return matchWholeWords_; } +			void setMatchWholeWords(bool); + +			bool getMatchChat() const { return matchChat_; } +			void setMatchChat(bool); + +			bool getMatchMUC() const { return matchMUC_; } +			void setMatchMUC(bool); + +			bool isEmpty() const; + +		private: +			static std::string boolToString(bool); +			static bool boolFromString(const std::string&); + +			std::vector<std::string> senders_; +			std::vector<std::string> keywords_; +			bool nickIsKeyword_; + +			mutable std::vector<boost::regex> senderRegex_; +			mutable std::vector<boost::regex> keywordRegex_; +			void updateRegex() const; +			boost::regex regexFromString(const std::string&) const; + +			bool matchCase_; +			bool matchWholeWords_; + +			bool matchChat_; +			bool matchMUC_; + +			HighlightAction action_; +	}; + +} diff --git a/Swift/Controllers/Highlighter.cpp b/Swift/Controllers/Highlighter.cpp new file mode 100644 index 0000000..754641a --- /dev/null +++ b/Swift/Controllers/Highlighter.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <Swiften/Base/foreach.h> +#include <Swift/Controllers/Highlighter.h> +#include <Swift/Controllers/HighlightManager.h> + +namespace Swift { + +Highlighter::Highlighter(HighlightManager* manager) +	: manager_(manager) +{ +	setMode(ChatMode); +} + +void Highlighter::setMode(Mode mode) +{ +	mode_ = mode; +	messageType_ = mode_ == ChatMode ? HighlightRule::ChatMessage : HighlightRule::MUCMessage; +} + +HighlightAction Highlighter::findAction(const std::string& body, const std::string& sender) const +{ +	foreach (const HighlightRule & r, manager_->getRules()) { +		if (r.isMatch(body, sender, nick_, messageType_)) { +			return r.getAction(); +		} +	} + +	return HighlightAction(); +} + +void Highlighter::handleHighlightAction(const HighlightAction& action) +{ +	manager_->onHighlight(action); +} + +} diff --git a/Swift/Controllers/Highlighter.h b/Swift/Controllers/Highlighter.h new file mode 100644 index 0000000..d026f29 --- /dev/null +++ b/Swift/Controllers/Highlighter.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <string> + +#include <Swift/Controllers/HighlightRule.h> + +namespace Swift { + +	class HighlightManager; + +	class Highlighter { +		public: +			Highlighter(HighlightManager* manager); + +			enum Mode { ChatMode, MUCMode }; +			void setMode(Mode mode); + +			void setNick(const std::string& nick) { nick_ = nick; } + +			HighlightAction findAction(const std::string& body, const std::string& sender) const; + +			void handleHighlightAction(const HighlightAction& action); + +		private: +			HighlightManager* manager_; +			Mode mode_; +			HighlightRule::MessageType messageType_; +			std::string nick_; +	}; + +} diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 28d890d..87ec94d 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -4,6 +4,12 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ +  #include <Swift/Controllers/MainController.h>  #include <boost/bind.hpp> @@ -69,6 +75,7 @@  #include "Swift/Controllers/Storages/CertificateStorageTrustChecker.h"  #include "Swiften/Network/NetworkFactories.h"  #include <Swift/Controllers/ProfileController.h> +#include <Swift/Controllers/ShowProfileController.h>  #include <Swift/Controllers/ContactEditController.h>  #include <Swift/Controllers/XMPPURIController.h>  #include "Swift/Controllers/AdHocManager.h" @@ -78,6 +85,8 @@  #include <Swiften/Client/ClientXMLTracer.h>  #include <Swift/Controllers/SettingConstants.h>  #include <Swiften/Client/StanzaChannel.h> +#include <Swift/Controllers/HighlightManager.h> +#include <Swift/Controllers/HighlightEditorController.h>  namespace Swift { @@ -121,6 +130,7 @@ MainController::MainController(  	historyViewController_ = NULL;  	eventWindowController_ = NULL;  	profileController_ = NULL; +	showProfileController_ = NULL;  	contactEditController_ = NULL;  	userSearchControllerChat_ = NULL;  	userSearchControllerAdd_ = NULL; @@ -142,7 +152,11 @@ MainController::MainController(  	systemTrayController_ = new SystemTrayController(eventController_, systemTray);  	loginWindow_ = uiFactory_->createLoginWindow(uiEventStream_);  	loginWindow_->setShowNotificationToggle(!notifier->isExternallyConfigured()); -	soundEventController_ = new SoundEventController(eventController_, soundPlayer, settings); + +	highlightManager_ = new HighlightManager(settings_); +	highlightEditorController_ = new HighlightEditorController(uiEventStream_, uiFactory_, highlightManager_); + +	soundEventController_ = new SoundEventController(eventController_, soundPlayer, settings, highlightManager_);  	xmppURIController_ = new XMPPURIController(uriHandler_, uiEventStream_); @@ -202,6 +216,8 @@ MainController::~MainController() {  	eventController_->disconnectAll();  	resetClient(); +	delete highlightEditorController_; +	delete highlightManager_;  	delete fileTransferListController_;  	delete xmlConsoleController_;  	delete xmppURIController_; @@ -225,6 +241,8 @@ void MainController::resetClient() {  	contactEditController_ = NULL;  	delete profileController_;  	profileController_ = NULL; +	delete showProfileController_; +	showProfileController_ = NULL;  	delete eventWindowController_;  	eventWindowController_ = NULL;  	delete chatsManager_; @@ -297,7 +315,8 @@ void MainController::handleConnected() {  	myStatusLooksOnline_ = true;  	if (freshLogin) {  		profileController_ = new ProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_); -		srand(time(NULL)); +		showProfileController_ = new ShowProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_); +		srand(static_cast<unsigned int>(time(NULL)));  		int randomPort = 10000 + rand() % 10000;  		client_->getFileTransferManager()->startListeningOnPort(randomPort);  		ftOverview_ = new FileTransferOverview(client_->getFileTransferManager()); @@ -318,9 +337,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_); +		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_);  #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_); +		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_);  #endif  		client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1)); @@ -369,7 +388,9 @@ void MainController::handleConnected() {  	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 */ +	assert(chatsManager_);  	chatsManager_->setOnline(true);  } diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index 2e5bd05..4f37e12 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -43,6 +43,7 @@ namespace Swift {  	class MUCController;  	class Notifier;  	class ProfileController; +	class ShowProfileController;  	class ContactEditController;  	class TogglableNotifier;  	class PresenceNotifier; @@ -71,6 +72,8 @@ namespace Swift {  	class AdHocCommandWindowFactory;  	class FileTransferOverview;  	class WhiteboardManager; +	class HighlightManager; +	class HighlightEditorController;  	class MainController {  		public: @@ -153,6 +156,7 @@ namespace Swift {  			FileTransferListController* fileTransferListController_;  			ChatsManager* chatsManager_;  			ProfileController* profileController_; +			ShowProfileController* showProfileController_;  			ContactEditController* contactEditController_;  			JID jid_;  			JID boundJID_; @@ -176,5 +180,7 @@ namespace Swift {  			static const int SecondsToWaitBeforeForceQuitting;  			FileTransferOverview* ftOverview_;  			WhiteboardManager* whiteboardManager_; +			HighlightManager* highlightManager_; +			HighlightEditorController* highlightEditorController_;  	};  } diff --git a/Swift/Controllers/ProfileController.cpp b/Swift/Controllers/ProfileController.cpp index 101e283..e641988 100644 --- a/Swift/Controllers/ProfileController.cpp +++ b/Swift/Controllers/ProfileController.cpp @@ -37,6 +37,7 @@ void ProfileController::handleUIEvent(UIEvent::ref event) {  	if (!profileWindow) {  		profileWindow = profileWindowFactory->createProfileWindow(); +		profileWindow->setEditable(true);  		profileWindow->onVCardChangeRequest.connect(boost::bind(&ProfileController::handleVCardChangeRequest, this, _1));  		vcardManager->onOwnVCardChanged.connect(boost::bind(&ProfileController::handleOwnVCardChanged, this, _1));  	} diff --git a/Swift/Controllers/Roster/ContactRosterItem.cpp b/Swift/Controllers/Roster/ContactRosterItem.cpp index 8c388bf..5b1b6e0 100644 --- a/Swift/Controllers/Roster/ContactRosterItem.cpp +++ b/Swift/Controllers/Roster/ContactRosterItem.cpp @@ -24,12 +24,12 @@ StatusShow::Type ContactRosterItem::getStatusShow() const {  StatusShow::Type ContactRosterItem::getSimplifiedStatusShow() const {  	switch (shownPresence_ ? shownPresence_->getShow() : StatusShow::None) { -		case StatusShow::Online: return StatusShow::Online; break; -		case StatusShow::Away: return StatusShow::Away; break; -	 	case StatusShow::XA: return StatusShow::Away; break; -		case StatusShow::FFC: return StatusShow::Online; break; -		case StatusShow::DND: return StatusShow::DND; break; -		case StatusShow::None: return StatusShow::None; break; +		case StatusShow::Online: return StatusShow::Online; +		case StatusShow::Away: return StatusShow::Away; +	 	case StatusShow::XA: return StatusShow::Away; +		case StatusShow::FFC: return StatusShow::Online; +		case StatusShow::DND: return StatusShow::DND; +		case StatusShow::None: return StatusShow::None;  	}  	assert(false);  	return StatusShow::None; diff --git a/Swift/Controllers/Roster/LeastCommonSubsequence.h b/Swift/Controllers/Roster/LeastCommonSubsequence.h index dd3c95a..9d45679 100644 --- a/Swift/Controllers/Roster/LeastCommonSubsequence.h +++ b/Swift/Controllers/Roster/LeastCommonSubsequence.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2011 Remko Tronçon + * Copyright (c) 2011-2013 Remko Tronçon   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -7,6 +7,7 @@  #pragma once  #include <vector> +#include <boost/numeric/conversion/cast.hpp>  namespace Swift {  	using std::equal_to; @@ -14,8 +15,8 @@ namespace Swift {  	namespace Detail {  		template<typename XIt, typename YIt, typename Length, typename Predicate>  		void computeLeastCommonSubsequenceMatrix(XIt xBegin, XIt xEnd, YIt yBegin, YIt yEnd, std::vector<Length>& result) { -			size_t width = std::distance(xBegin, xEnd) + 1; -			size_t height = std::distance(yBegin, yEnd)  + 1; +			size_t width = static_cast<size_t>(std::distance(xBegin, xEnd) + 1); +			size_t height = static_cast<size_t>(std::distance(yBegin, yEnd)  + 1);  			result.resize(width * height);  			// Initialize first row & column @@ -30,7 +31,7 @@ namespace Swift {  			Predicate predicate;  			for (size_t i = 1; i < width; ++i) {  				for (size_t j = 1; j < height; ++j) { -					result[i + j*width] = (predicate(*(xBegin + i-1), *(yBegin + j-1)) ? result[(i-1) + (j-1)*width] + 1 : std::max(result[i + (j-1)*width], result[i-1 + (j*width)])); +					result[i + j*width] = predicate(*(xBegin + boost::numeric_cast<long long>(i)-1), *(yBegin + boost::numeric_cast<long long >(j)-1)) ? result[(i-1) + (j-1)*width] + 1 : std::max(result[i + (j-1)*width], result[i-1 + (j*width)]);  				}  			}  		} @@ -46,29 +47,29 @@ namespace Swift {  		typename std::vector<X>::const_iterator yBegin = y.begin();  		while (xBegin < x.end() && yBegin < y.end() && insertRemovePredicate(*xBegin, *yBegin)) {  			if (updatePredicate(*xBegin, *yBegin)) { -				updates.push_back(std::distance(x.begin(), xBegin)); -				postUpdates.push_back(std::distance(y.begin(), yBegin)); +				updates.push_back(static_cast<size_t>(std::distance(x.begin(), xBegin))); +				postUpdates.push_back(static_cast<size_t>(std::distance(y.begin(), yBegin)));  			}  			++xBegin;  			++yBegin;  		} -		size_t prefixLength = std::distance(x.begin(), xBegin); +		size_t prefixLength = static_cast<size_t>(std::distance(x.begin(), xBegin));  		// Find & handle common suffix (Optimization to reduce LCS matrix size)  		typename std::vector<X>::const_reverse_iterator xEnd = x.rbegin();  		typename std::vector<X>::const_reverse_iterator yEnd = y.rbegin();  		while (xEnd.base() > xBegin && yEnd.base() > yBegin && insertRemovePredicate(*xEnd, *yEnd)) {  			if (updatePredicate(*xEnd, *yEnd)) { -				updates.push_back(std::distance(x.begin(), xEnd.base()) - 1); -				postUpdates.push_back(std::distance(y.begin(), yEnd.base()) - 1); +				updates.push_back(static_cast<size_t>(std::distance(x.begin(), xEnd.base()) - 1)); +				postUpdates.push_back(static_cast<size_t>(std::distance(y.begin(), yEnd.base()) - 1));  			}  			++xEnd;  			++yEnd;  		}  		// Compute lengths -		size_t xLength = std::distance(xBegin, xEnd.base()); -		size_t yLength = std::distance(yBegin, yEnd.base()); +		size_t xLength = static_cast<size_t>(std::distance(xBegin, xEnd.base())); +		size_t yLength = static_cast<size_t>(std::distance(yBegin, yEnd.base()));  		// Compute LCS matrix  		std::vector<unsigned int> lcs; @@ -77,7 +78,7 @@ namespace Swift {  		// Process LCS matrix  		size_t i = xLength;  		size_t j = yLength; -		const size_t width = xLength + 1; +		size_t width = xLength + 1;  		while (true) {  			if (i > 0 && j > 0 && insertRemovePredicate(x[prefixLength + i-1], y[prefixLength + j-1])) {  				// x[i-1] same diff --git a/Swift/Controllers/Roster/Roster.cpp b/Swift/Controllers/Roster/Roster.cpp index 65cf4d2..b5c5998 100644 --- a/Swift/Controllers/Roster/Roster.cpp +++ b/Swift/Controllers/Roster/Roster.cpp @@ -198,13 +198,13 @@ void Roster::removeFilter(RosterFilter *filter) {  }  void Roster::filterContact(ContactRosterItem* contact, GroupRosterItem* group) { -	int oldDisplayedSize = group->getDisplayedChildren().size(); +	size_t oldDisplayedSize = group->getDisplayedChildren().size();  	bool hide = true;  	foreach (RosterFilter *filter, filters_) {  		hide &= (*filter)(contact);  	}  	group->setDisplayed(contact, filters_.empty() || !hide); -	int newDisplayedSize = group->getDisplayedChildren().size(); +	size_t newDisplayedSize = group->getDisplayedChildren().size();  	if (oldDisplayedSize == 0 && newDisplayedSize > 0) {  		onGroupAdded(group);  	} diff --git a/Swift/Controllers/Roster/Roster.h b/Swift/Controllers/Roster/Roster.h index 2fcfba5..74547d6 100644 --- a/Swift/Controllers/Roster/Roster.h +++ b/Swift/Controllers/Roster/Roster.h @@ -36,10 +36,10 @@ class Roster {  		void applyOnItems(const RosterItemOperation& operation);  		void applyOnAllItems(const RosterItemOperation& operation);  		void applyOnItem(const RosterItemOperation& operation, const JID& jid); -		void addFilter(RosterFilter *filter) {filters_.push_back(filter);filterAll();}; +		void addFilter(RosterFilter *filter) {filters_.push_back(filter);filterAll();}  		void removeFilter(RosterFilter *filter);  		GroupRosterItem* getRoot(); -		std::vector<RosterFilter*> getFilters() {return filters_;}; +		std::vector<RosterFilter*> getFilters() {return filters_;}  		boost::signal<void (GroupRosterItem*)> onChildrenChanged;  		boost::signal<void (GroupRosterItem*)> onGroupAdded;  		boost::signal<void (RosterItem*)> onDataChanged; diff --git a/Swift/Controllers/Roster/RosterController.h b/Swift/Controllers/Roster/RosterController.h index 5e40124..ec07574 100644 --- a/Swift/Controllers/Roster/RosterController.h +++ b/Swift/Controllers/Roster/RosterController.h @@ -45,7 +45,7 @@ namespace Swift {  			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();  			void showRosterWindow(); -			MainWindow* getWindow() {return mainWindow_;}; +			MainWindow* getWindow() {return mainWindow_;}  			boost::signal<void (StatusShow::Type, const std::string&)> onChangeStatusRequest;  			boost::signal<void ()> onSignOutRequest;  			void handleAvatarChanged(const JID& jid); diff --git a/Swift/Controllers/Roster/RosterItemOperation.h b/Swift/Controllers/Roster/RosterItemOperation.h index 691c8ef..f1dff8d 100644 --- a/Swift/Controllers/Roster/RosterItemOperation.h +++ b/Swift/Controllers/Roster/RosterItemOperation.h @@ -12,10 +12,10 @@ namespace Swift {  class RosterItemOperation {  	public: -		RosterItemOperation(bool requiresLookup = false, const JID& lookupJID = JID()) : requiresLookup_(requiresLookup), lookupJID_(lookupJID) {}; -		virtual ~RosterItemOperation() {}; -		bool requiresLookup() const {return requiresLookup_;}; -		const JID& lookupJID() const {return lookupJID_;};  +		RosterItemOperation(bool requiresLookup = false, const JID& lookupJID = JID()) : requiresLookup_(requiresLookup), lookupJID_(lookupJID) {} +		virtual ~RosterItemOperation() {} +		bool requiresLookup() const {return requiresLookup_;} +		const JID& lookupJID() const {return lookupJID_;}   		/**  		 * This is called when iterating over possible subjects, so must check it's  		 * applying to the right items - even if requiresLookup() is true an item diff --git a/Swift/Controllers/Roster/TableRoster.cpp b/Swift/Controllers/Roster/TableRoster.cpp index c00bf4f..eb036db 100644 --- a/Swift/Controllers/Roster/TableRoster.cpp +++ b/Swift/Controllers/Roster/TableRoster.cpp @@ -9,6 +9,7 @@  #include <boost/cast.hpp>  #include <cassert>  #include <algorithm> +#include <boost/numeric/conversion/cast.hpp>  #include <Swiften/Base/foreach.h>  #include <Swiften/Network/TimerFactory.h> @@ -132,13 +133,13 @@ void TableRoster::handleUpdateTimerTick() {  		computeIndexDiff<Item, ItemEquals, ItemNeedsUpdate >(sections[sectionUpdates[i]].items, newSections[sectionPostUpdates[i]].items, itemUpdates, itemPostUpdates, itemRemoves, itemInserts);  		size_t end = update.insertedRows.size();  		update.insertedRows.resize(update.insertedRows.size() + itemInserts.size()); -		std::transform(itemInserts.begin(), itemInserts.end(), update.insertedRows.begin() + end, CreateIndexForSection(sectionPostUpdates[i])); +		std::transform(itemInserts.begin(), itemInserts.end(), update.insertedRows.begin() + boost::numeric_cast<long long>(end), CreateIndexForSection(sectionPostUpdates[i]));  		end = update.deletedRows.size();  		update.deletedRows.resize(update.deletedRows.size() + itemRemoves.size()); -		std::transform(itemRemoves.begin(), itemRemoves.end(), update.deletedRows.begin() + end, CreateIndexForSection(sectionUpdates[i])); +		std::transform(itemRemoves.begin(), itemRemoves.end(), update.deletedRows.begin() + boost::numeric_cast<long long>(end), CreateIndexForSection(sectionUpdates[i]));  		end = update.updatedRows.size();  		update.updatedRows.resize(update.updatedRows.size() + itemUpdates.size()); -		std::transform(itemUpdates.begin(), itemUpdates.end(), update.updatedRows.begin() + end, CreateIndexForSection(sectionPostUpdates[i])); +		std::transform(itemUpdates.begin(), itemUpdates.end(), update.updatedRows.begin() + boost::numeric_cast<long long>(end), CreateIndexForSection(sectionPostUpdates[i]));  	}  	// Switch the old model with the new diff --git a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp index fbee894..e439c78 100644 --- a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp +++ b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp @@ -84,7 +84,7 @@ class RosterControllerTest : public CppUnit::TestFixture {  			ftOverview_ = new FileTransferOverview(ftManager_);  			rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_, entityCapsManager_, ftOverview_);  			mainWindow_ = mainWindowFactory_->last; -		}; +		}  		void tearDown() {  			delete rosterController_; @@ -105,7 +105,7 @@ class RosterControllerTest : public CppUnit::TestFixture {  			delete uiEventStream_;  			delete settings_;  			delete xmppRoster_; -		}; +		}  	GroupRosterItem* groupChild(size_t i) {  		return dynamic_cast<GroupRosterItem*>(CHILDREN[i]); @@ -133,7 +133,7 @@ class RosterControllerTest : public CppUnit::TestFixture {  		CPPUNIT_ASSERT(item2);  		CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item2->getStatusText()); -	}; +	}  	void testHighestPresence() {  		std::vector<std::string> groups; @@ -153,7 +153,7 @@ class RosterControllerTest : public CppUnit::TestFixture {  		ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]);  		CPPUNIT_ASSERT(item);  		CPPUNIT_ASSERT_EQUAL(highPresence->getStatus(), item->getStatusText()); -	}; +	}  	void testNotHighestPresence() {  		std::vector<std::string> groups; @@ -173,7 +173,7 @@ class RosterControllerTest : public CppUnit::TestFixture {  		ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]);  		CPPUNIT_ASSERT(item);  		CPPUNIT_ASSERT_EQUAL(highPresence->getStatus(), item->getStatusText()); -	}; +	}  	void testUnavailablePresence() {  		std::vector<std::string> groups; @@ -215,7 +215,7 @@ class RosterControllerTest : public CppUnit::TestFixture {  		CPPUNIT_ASSERT_EQUAL(lowPresenceOffline->getStatus(), high->getStatus());  		CPPUNIT_ASSERT_EQUAL(StatusShow::None, item->getStatusShow());  		CPPUNIT_ASSERT_EQUAL(lowPresenceOffline->getStatus(), item->getStatusText()); -	}; +	}  		void testAdd() {  			std::vector<std::string> groups; @@ -225,7 +225,7 @@ class RosterControllerTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(CHILDREN.size()));  			//CPPUNIT_ASSERT_EQUAL(std::string("Bob"), xmppRoster_->getNameForJID(JID("foo@bar.com"))); -		}; +		}   		void testAddSubscription() {  			std::vector<std::string> groups; @@ -242,7 +242,7 @@ class RosterControllerTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size()));  			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size())); -		}; +		}  		void testReceiveRename() {  			std::vector<std::string> groups; @@ -256,7 +256,7 @@ class RosterControllerTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size()));  			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));  			CPPUNIT_ASSERT_EQUAL(std::string("NewName"), groupChild(0)->getChildren()[0]->getDisplayName()); -		}; +		}  	void testReceiveRegroup() {  		std::vector<std::string> oldGroups; @@ -282,7 +282,7 @@ class RosterControllerTest : public CppUnit::TestFixture {  		CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));  		CPPUNIT_ASSERT_EQUAL(std::string("new name"), groupChild(0)->getChildren()[0]->getDisplayName());  		CPPUNIT_ASSERT_EQUAL(std::string("Best Group"), groupChild(0)->getDisplayName()); -	}; +	}  		void testSendRename() {  			JID jid("testling@wonderland.lit"); diff --git a/Swift/Controllers/Roster/UnitTest/TableRosterTest.cpp b/Swift/Controllers/Roster/UnitTest/TableRosterTest.cpp index e433b50..db8a2fd 100644 --- a/Swift/Controllers/Roster/UnitTest/TableRosterTest.cpp +++ b/Swift/Controllers/Roster/UnitTest/TableRosterTest.cpp @@ -6,6 +6,8 @@  #include <Swift/Controllers/Roster/TableRoster.h> +std::ostream& operator<<(std::ostream& os, const Swift::TableRoster::Index& i); +  std::ostream& operator<<(std::ostream& os, const Swift::TableRoster::Index& i) {  	os << "(" << i.section << ", " << i.row << ")";  	return os; diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript index 7cd017b..26b9334 100644 --- a/Swift/Controllers/SConscript +++ b/Swift/Controllers/SConscript @@ -30,6 +30,7 @@ if env["SCONS_STAGE"] == "build" :  			"Chat/UserSearchController.cpp",  			"MainController.cpp",  			"ProfileController.cpp", +			"ShowProfileController.cpp",  			"ContactEditController.cpp",  			"FileTransfer/FileTransferController.cpp",  			"FileTransfer/FileTransferOverview.cpp", @@ -74,7 +75,13 @@ if env["SCONS_STAGE"] == "build" :  			"XMPPURIController.cpp",  			"ChatMessageSummarizer.cpp",  			"SettingConstants.cpp", -			"WhiteboardManager.cpp" +			"WhiteboardManager.cpp", +			"StatusCache.cpp", +			"HighlightAction.cpp", +			"HighlightEditorController.cpp", +			"HighlightManager.cpp", +			"HighlightRule.cpp", +			"Highlighter.cpp"  		])  	env.Append(UNITTEST_SOURCES = [ @@ -89,4 +96,5 @@ if env["SCONS_STAGE"] == "build" :  			File("UnitTest/MockChatWindow.cpp"),  			File("UnitTest/ChatMessageSummarizerTest.cpp"),  			File("Settings/UnitTest/SettingsProviderHierachyTest.cpp"), +			File("UnitTest/HighlightRuleTest.cpp"),  		]) diff --git a/Swift/Controllers/SettingConstants.cpp b/Swift/Controllers/SettingConstants.cpp index 7ab4ac4..e430c77 100644 --- a/Swift/Controllers/SettingConstants.cpp +++ b/Swift/Controllers/SettingConstants.cpp @@ -19,4 +19,5 @@ const SettingsProvider::Setting<bool> SettingConstants::LOGIN_AUTOMATICALLY = Se  const SettingsProvider::Setting<bool> SettingConstants::SHOW_OFFLINE("showOffline", false);  const SettingsProvider::Setting<std::string> SettingConstants::EXPANDED_ROSTER_GROUPS("GroupExpandiness", "");  const SettingsProvider::Setting<bool> SettingConstants::PLAY_SOUNDS("playSounds", true); +const SettingsProvider::Setting<std::string> SettingConstants::HIGHLIGHT_RULES("highlightRules", "@");  } diff --git a/Swift/Controllers/SettingConstants.h b/Swift/Controllers/SettingConstants.h index ff1ed72..cc3af47 100644 --- a/Swift/Controllers/SettingConstants.h +++ b/Swift/Controllers/SettingConstants.h @@ -22,5 +22,6 @@ namespace Swift {  			static const SettingsProvider::Setting<bool> SHOW_OFFLINE;  			static const SettingsProvider::Setting<std::string> EXPANDED_ROSTER_GROUPS;  			static const SettingsProvider::Setting<bool> PLAY_SOUNDS; +			static const SettingsProvider::Setting<std::string> HIGHLIGHT_RULES;  	};  } diff --git a/Swift/Controllers/Settings/DummySettingsProvider.h b/Swift/Controllers/Settings/DummySettingsProvider.h index 1d6059f..0183dd3 100644 --- a/Swift/Controllers/Settings/DummySettingsProvider.h +++ b/Swift/Controllers/Settings/DummySettingsProvider.h @@ -17,25 +17,25 @@ class DummySettingsProvider : public SettingsProvider {  		virtual ~DummySettingsProvider() {}  		virtual std::string getSetting(const Setting<std::string>& setting) {  			return stringValues.find(setting.getKey()) != stringValues.end() ? stringValues[setting.getKey()] : setting.getDefaultValue(); -		}; +		}  		virtual void storeSetting(const Setting<std::string>& setting, const std::string& value) {  			stringValues[setting.getKey()] = value;  			onSettingChanged(setting.getKey()); -		}; +		}  		virtual bool getSetting(const Setting<bool>& setting) {  			return boolValues.find(setting.getKey()) != boolValues.end() ? boolValues[setting.getKey()] : setting.getDefaultValue(); -		}; +		}  		virtual void storeSetting(const Setting<bool>& setting, const bool& value) {  			boolValues[setting.getKey()] = value;  			onSettingChanged(setting.getKey()); -		}; +		}  		virtual int getSetting(const Setting<int>& setting) {  			return intValues.find(setting.getKey()) != intValues.end() ? intValues[setting.getKey()] : setting.getDefaultValue(); -		}; +		}  		virtual void storeSetting(const Setting<int>& setting, const int& value) {  			intValues[setting.getKey()] = value;  			onSettingChanged(setting.getKey()); -		}; +		}  		virtual std::vector<std::string> getAvailableProfiles() {return std::vector<std::string>();}  		virtual void createProfile(const std::string& ) {}  		virtual void removeProfile(const std::string& ) {} diff --git a/Swift/Controllers/Settings/UnitTest/SettingsProviderHierachyTest.cpp b/Swift/Controllers/Settings/UnitTest/SettingsProviderHierachyTest.cpp index aa4d14f..2b637a0 100644 --- a/Swift/Controllers/Settings/UnitTest/SettingsProviderHierachyTest.cpp +++ b/Swift/Controllers/Settings/UnitTest/SettingsProviderHierachyTest.cpp @@ -26,7 +26,7 @@ class SettingsProviderHierachyTest : public CppUnit::TestFixture {  	CPPUNIT_TEST_SUITE_END();  public: -	SettingsProviderHierachyTest() : setting1("somekey", 42) {}; +	SettingsProviderHierachyTest() : setting1("somekey", 42) {}  	void setUp() {  		bottom = new DummySettingsProvider(); diff --git a/Swift/Controllers/ShowProfileController.cpp b/Swift/Controllers/ShowProfileController.cpp new file mode 100644 index 0000000..ee0854b --- /dev/null +++ b/Swift/Controllers/ShowProfileController.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "ShowProfileController.h" + +#include <boost/bind.hpp> + +#include <Swiften/Base/foreach.h> +#include <Swiften/VCards/VCardManager.h> + +#include <Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIInterfaces/ProfileWindowFactory.h> + +namespace Swift { + +ShowProfileController::ShowProfileController(VCardManager* vcardManager, ProfileWindowFactory* profileWindowFactory, UIEventStream* uiEventStream) : vcardManager(vcardManager), profileWindowFactory(profileWindowFactory), uiEventStream(uiEventStream) { +	uiEventStream->onUIEvent.connect(boost::bind(&ShowProfileController::handleUIEvent, this, _1)); +	vcardManager->onVCardChanged.connect(boost::bind(&ShowProfileController::handleVCardChanged, this, _1, _2)); +} + +ShowProfileController::~ShowProfileController() { +	typedef std::pair<JID, ProfileWindow*> JIDProfileWindowPair; +	foreach(const JIDProfileWindowPair& jidWndPair, openedProfileWindows) { +		jidWndPair.second->onWindowClosed.disconnect(boost::bind(&ShowProfileController::handleProfileWindowClosed, this, _1)); +		delete jidWndPair.second; +	} + +	vcardManager->onVCardChanged.disconnect(boost::bind(&ShowProfileController::handleVCardChanged, this, _1, _2)); +	uiEventStream->onUIEvent.disconnect(boost::bind(&ShowProfileController::handleUIEvent, this, _1)); +} + +void ShowProfileController::handleUIEvent(UIEvent::ref event) { +	ShowProfileForRosterItemUIEvent::ref showProfileEvent = boost::dynamic_pointer_cast<ShowProfileForRosterItemUIEvent>(event); +	if (!showProfileEvent) { +		return; +	} + +	if (openedProfileWindows.find(showProfileEvent->getJID()) == openedProfileWindows.end()) { +		ProfileWindow* newProfileWindow = profileWindowFactory->createProfileWindow(); +		newProfileWindow->setJID(showProfileEvent->getJID()); +		newProfileWindow->onWindowClosed.connect(boost::bind(&ShowProfileController::handleProfileWindowClosed, this, _1)); +		openedProfileWindows[showProfileEvent->getJID()] = newProfileWindow; +		VCard::ref vcard = vcardManager->getVCardAndRequestWhenNeeded(showProfileEvent->getJID()); +		if (vcard) { +			newProfileWindow->setVCard(vcard); +		} else { +			newProfileWindow->setProcessing(true); +		} +		newProfileWindow->show(); +	} else { +		openedProfileWindows[showProfileEvent->getJID()]->show(); +	} +} + +void ShowProfileController::handleVCardChanged(const JID& jid, VCard::ref vcard) { +	if (openedProfileWindows.find(jid) == openedProfileWindows.end()) { +		return; +	} + +	ProfileWindow* profileWindow = openedProfileWindows[jid]; +	profileWindow->setVCard(vcard); +	profileWindow->setProcessing(false); +	profileWindow->show(); +} + +void ShowProfileController::handleProfileWindowClosed(const JID& profileJid) { +	ProfileWindow* profileWindow = openedProfileWindows[profileJid]; +	openedProfileWindows.erase(profileJid); +	delete profileWindow; +} + +} diff --git a/Swift/Controllers/ShowProfileController.h b/Swift/Controllers/ShowProfileController.h new file mode 100644 index 0000000..5646f5e --- /dev/null +++ b/Swift/Controllers/ShowProfileController.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/JID/JID.h> +#include <Swiften/Elements/VCard.h> + +#include <Swift/Controllers/UIEvents/UIEvent.h> + +namespace Swift { +	class VCardManager; +	class ProfileWindow; +	class ProfileWindowFactory; +	class UIEventStream; + +	class ShowProfileController { +		public: +			ShowProfileController(VCardManager* vcardManager, ProfileWindowFactory* profileWindowFactory, UIEventStream* uiEventStream); +			~ShowProfileController(); + +		private: +			void handleUIEvent(UIEvent::ref event); +			void handleVCardChanged(const JID&, VCard::ref); +			void handleProfileWindowClosed(const JID& profileJid); + +		private: +			VCardManager* vcardManager; +			ProfileWindowFactory* profileWindowFactory; +			UIEventStream* uiEventStream; +			std::map<JID, ProfileWindow*> openedProfileWindows; +	}; +} diff --git a/Swift/Controllers/SoundEventController.cpp b/Swift/Controllers/SoundEventController.cpp index d056990..a5171e2 100644 --- a/Swift/Controllers/SoundEventController.cpp +++ b/Swift/Controllers/SoundEventController.cpp @@ -12,22 +12,33 @@  #include <Swift/Controllers/SoundPlayer.h>  #include <Swift/Controllers/UIEvents/UIEventStream.h>  #include <Swift/Controllers/SettingConstants.h> +#include <Swift/Controllers/HighlightManager.h>  namespace Swift { -SoundEventController::SoundEventController(EventController* eventController, SoundPlayer* soundPlayer, SettingsProvider* settings) { +SoundEventController::SoundEventController(EventController* eventController, SoundPlayer* soundPlayer, SettingsProvider* settings, HighlightManager* highlightManager) {  	settings_ = settings;  	eventController_ = eventController;  	soundPlayer_ = soundPlayer;  	eventController_->onEventQueueEventAdded.connect(boost::bind(&SoundEventController::handleEventQueueEventAdded, this, _1)); +	highlightManager_ = highlightManager; +	highlightManager_->onHighlight.connect(boost::bind(&SoundEventController::handleHighlight, this, _1)); +  	settings_->onSettingChanged.connect(boost::bind(&SoundEventController::handleSettingChanged, this, _1));  	playSounds_ = settings->getSetting(SettingConstants::PLAY_SOUNDS);  } -void SoundEventController::handleEventQueueEventAdded(boost::shared_ptr<StanzaEvent> event) { -	if (playSounds_ && !event->getConcluded()) { -		soundPlayer_->playSound(SoundPlayer::MessageReceived); +void SoundEventController::handleEventQueueEventAdded(boost::shared_ptr<StanzaEvent> /*event*/) { +	// message received sound is now played via highlighting +	//if (playSounds_ && !event->getConcluded()) { +	//	soundPlayer_->playSound(SoundPlayer::MessageReceived); +	//} +} + +void SoundEventController::handleHighlight(const HighlightAction& action) { +	if (playSounds_ && action.playSound()) { +		soundPlayer_->playSound(SoundPlayer::MessageReceived, action.getSoundFile());  	}  } diff --git a/Swift/Controllers/SoundEventController.h b/Swift/Controllers/SoundEventController.h index c6dec6f..c9dcab4 100644 --- a/Swift/Controllers/SoundEventController.h +++ b/Swift/Controllers/SoundEventController.h @@ -10,21 +10,25 @@  #include <Swift/Controllers/XMPPEvents/StanzaEvent.h>  #include <Swift/Controllers/Settings/SettingsProvider.h> +#include <Swift/Controllers/HighlightAction.h>  namespace Swift {  	class EventController;  	class SoundPlayer; +	class HighlightManager;  	class SoundEventController {  		public: -			SoundEventController(EventController* eventController, SoundPlayer* soundPlayer, SettingsProvider* settings); +			SoundEventController(EventController* eventController, SoundPlayer* soundPlayer, SettingsProvider* settings, HighlightManager* highlightManager);  			void setPlaySounds(bool playSounds); -			bool getSoundEnabled() {return playSounds_;}; +			bool getSoundEnabled() {return playSounds_;}  		private:  			void handleSettingChanged(const std::string& settingPath);  			void handleEventQueueEventAdded(boost::shared_ptr<StanzaEvent> event); +			void handleHighlight(const HighlightAction& action);  			EventController* eventController_;  			SoundPlayer* soundPlayer_;  			bool playSounds_;  			SettingsProvider* settings_; +			HighlightManager* highlightManager_;  	};  } diff --git a/Swift/Controllers/SoundPlayer.h b/Swift/Controllers/SoundPlayer.h index b71d759..f18a2c0 100644 --- a/Swift/Controllers/SoundPlayer.h +++ b/Swift/Controllers/SoundPlayer.h @@ -6,11 +6,13 @@  #pragma once +#include <string> +  namespace Swift {  	class SoundPlayer {  		public: -			virtual ~SoundPlayer() {}; +			virtual ~SoundPlayer() {}  			enum SoundEffect{MessageReceived}; -			virtual void playSound(SoundEffect sound) = 0; +			virtual void playSound(SoundEffect sound, const std::string& soundResource) = 0;  	};  } diff --git a/Swift/Controllers/StatusCache.cpp b/Swift/Controllers/StatusCache.cpp new file mode 100644 index 0000000..1569e26 --- /dev/null +++ b/Swift/Controllers/StatusCache.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2012 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/StatusCache.h> + +#include <boost/algorithm/string.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/filesystem/fstream.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/lambda/bind.hpp> + +#include <Swiften/Base/foreach.h> +#include <Swiften/Base/ByteArray.h> +#include <SwifTools/Application/ApplicationPathProvider.h> + +namespace lambda = boost::lambda; + +namespace Swift { + +static const size_t MAX_ENTRIES = 200; + +StatusCache::StatusCache(ApplicationPathProvider* paths) { +	paths_ = paths; +	path_ = paths_->getDataDir() / "StatusCache"; +	loadRecents(); +} + +StatusCache::~StatusCache() { + +} + +std::vector<StatusCache::PreviousStatus> StatusCache::getMatches(const std::string& substring, size_t maxCount) const { +	std::vector<PreviousStatus> matches; +	foreach (const PreviousStatus& status, previousStatuses_) { +		if (substring.empty() || (boost::algorithm::ifind_first(status.first, substring) && substring != status.first)) { +			matches.push_back(status); +			if (matches.size() == maxCount) { +				break; +			} +		} +	} +	return matches; +} + +void StatusCache::addRecent(const std::string& text, StatusShow::Type type) { +	if (text.empty()) { +		return; +	} +	previousStatuses_.remove_if(lambda::bind(&PreviousStatus::first, lambda::_1) == text && lambda::bind(&PreviousStatus::second, lambda::_1) == type); +	previousStatuses_.push_front(PreviousStatus(text, type)); +	for (size_t i = previousStatuses_.size(); i > MAX_ENTRIES; i--) { +		previousStatuses_.pop_back(); +	} +	saveRecents(); +} + +void StatusCache::loadRecents() { +	try { +		if (boost::filesystem::exists(path_)) { +			ByteArray data; +			readByteArrayFromFile(data, path_.string()); +			std::string stringData = byteArrayToString(data); +			std::vector<std::string> lines; +			boost::split(lines, stringData, boost::is_any_of("\n")); +			foreach (const std::string& line, lines) { +				std::vector<std::string> bits; +				boost::split(bits, line, boost::is_any_of("\t")); +				if (bits.size() < 2) { +					continue; +				} +				StatusShow::Type type; +				type = static_cast<StatusShow::Type>(boost::lexical_cast<size_t>(bits[0])); +				previousStatuses_.push_back(PreviousStatus(boost::trim_copy(bits[1]), type)); +			} +		} +	} +	catch (const boost::filesystem::filesystem_error& e) { +		std::cerr << "ERROR: " << e.what() << std::endl; +	} +} + +void StatusCache::saveRecents() { +	try { +		if (!boost::filesystem::exists(path_.parent_path())) { +			boost::filesystem::create_directories(path_.parent_path()); +		} +		boost::filesystem::ofstream file(path_); +		foreach (const PreviousStatus& recent, previousStatuses_) { +			std::string message = recent.first; +			boost::replace_all(message, "\t", " "); +			file << recent.second << "\t" << message << std::endl; +		} +		file.close(); +	} +	catch (const boost::filesystem::filesystem_error& e) { +		std::cerr << "ERROR: " << e.what() << std::endl; +	} +} + +} + + + + diff --git a/Swift/Controllers/StatusCache.h b/Swift/Controllers/StatusCache.h new file mode 100644 index 0000000..35b3674 --- /dev/null +++ b/Swift/Controllers/StatusCache.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <string> +#include <utility> +#include <vector> +#include <list> +#include <iostream> + +#include <boost/filesystem/path.hpp> + +#include <Swiften/Elements/StatusShow.h> + +namespace Swift { +	class ApplicationPathProvider; +	class StatusCache { +		public: +			typedef std::pair<std::string, StatusShow::Type> PreviousStatus; +		public: +			StatusCache(ApplicationPathProvider* paths); +			~StatusCache(); + +			std::vector<PreviousStatus> getMatches(const std::string& substring, size_t maxCount) const; +			void addRecent(const std::string& text, StatusShow::Type type); +		private: +			void saveRecents(); +			void loadRecents(); +		private: +			boost::filesystem::path path_; +			std::list<PreviousStatus> previousStatuses_; +			ApplicationPathProvider* paths_; +	}; +} + + diff --git a/Swift/Controllers/StatusUtil.cpp b/Swift/Controllers/StatusUtil.cpp index fd1fea3..a72f340 100644 --- a/Swift/Controllers/StatusUtil.cpp +++ b/Swift/Controllers/StatusUtil.cpp @@ -6,6 +6,7 @@  #include <Swift/Controllers/StatusUtil.h> +#include <cassert>  #include <Swift/Controllers/Intl.h>  namespace Swift { @@ -19,6 +20,7 @@ std::string statusShowTypeToFriendlyName(StatusShow::Type type) {  		case StatusShow::DND: return QT_TRANSLATE_NOOP("", "Busy");  		case StatusShow::None: return QT_TRANSLATE_NOOP("", "Offline");  	} +	assert(false);  	return "";  } diff --git a/Swift/Controllers/Storages/CertificateFileStorage.cpp b/Swift/Controllers/Storages/CertificateFileStorage.cpp index a4a95c7..55016d5 100644 --- a/Swift/Controllers/Storages/CertificateFileStorage.cpp +++ b/Swift/Controllers/Storages/CertificateFileStorage.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -8,6 +8,7 @@  #include <iostream>  #include <boost/filesystem/fstream.hpp> +#include <boost/numeric/conversion/cast.hpp>  #include <Swiften/StringCodecs/SHA1.h>  #include <Swiften/StringCodecs/Hexify.h> @@ -50,7 +51,7 @@ void CertificateFileStorage::addCertificate(Certificate::ref certificate) {  	}  	boost::filesystem::ofstream file(certificatePath, boost::filesystem::ofstream::binary|boost::filesystem::ofstream::out);  	ByteArray data = certificate->toDER(); -	file.write(reinterpret_cast<const char*>(vecptr(data)), data.size()); +	file.write(reinterpret_cast<const char*>(vecptr(data)), boost::numeric_cast<std::streamsize>(data.size()));  	file.close();  } diff --git a/Swift/Controllers/SystemTray.h b/Swift/Controllers/SystemTray.h index 736b1fa..b71a783 100644 --- a/Swift/Controllers/SystemTray.h +++ b/Swift/Controllers/SystemTray.h @@ -11,7 +11,7 @@  namespace Swift {  	class SystemTray {  		public: -			virtual ~SystemTray(){}; +			virtual ~SystemTray(){}  			virtual void setUnreadMessages(bool some) = 0;  			virtual void setStatusType(StatusShow::Type type) = 0;  			virtual void setConnecting() = 0; diff --git a/Swift/Controllers/Translator.cpp b/Swift/Controllers/Translator.cpp index 82fc46e..b7766ca 100644 --- a/Swift/Controllers/Translator.cpp +++ b/Swift/Controllers/Translator.cpp @@ -10,7 +10,7 @@  namespace Swift { -struct DefaultTranslator : public Translator { +static struct DefaultTranslator : public Translator {  	virtual std::string translate(const std::string& text, const std::string&) {  		return text;  	} diff --git a/Swift/Controllers/UIEvents/AddContactUIEvent.h b/Swift/Controllers/UIEvents/AddContactUIEvent.h index d92d3af..6b70b76 100644 --- a/Swift/Controllers/UIEvents/AddContactUIEvent.h +++ b/Swift/Controllers/UIEvents/AddContactUIEvent.h @@ -14,15 +14,15 @@  namespace Swift {  	class AddContactUIEvent : public UIEvent {  		public: -			AddContactUIEvent(const JID& jid, const std::string& name, const std::set<std::string>& groups) : jid_(jid), name_(name), groups_(groups) {}; +			AddContactUIEvent(const JID& jid, const std::string& name, const std::set<std::string>& groups) : jid_(jid), name_(name), groups_(groups) {}  			const std::string& getName() const {  				return name_; -			}; +			}  			const JID& getJID() const {  				return jid_; -			}; +			}  			const std::set<std::string>& getGroups() const {  				return groups_; diff --git a/Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h b/Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h index 210da3e..df01d6c 100644 --- a/Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h +++ b/Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h @@ -14,7 +14,7 @@  namespace Swift {  	class AddMUCBookmarkUIEvent : public UIEvent {  		public: -			AddMUCBookmarkUIEvent(const MUCBookmark& bookmark) : bookmark(bookmark) {}; +			AddMUCBookmarkUIEvent(const MUCBookmark& bookmark) : bookmark(bookmark) {}  			const MUCBookmark& getBookmark() { return bookmark; }  		private: diff --git a/Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h b/Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h index 2b10f09..7723d89 100644 --- a/Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h +++ b/Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h @@ -14,10 +14,10 @@  namespace Swift {  	class EditMUCBookmarkUIEvent : public UIEvent {  		public: -			EditMUCBookmarkUIEvent(const MUCBookmark& oldBookmark, const MUCBookmark& newBookmark) : oldBookmark(oldBookmark) , newBookmark(newBookmark) {}; +			EditMUCBookmarkUIEvent(const MUCBookmark& oldBookmark, const MUCBookmark& newBookmark) : oldBookmark(oldBookmark) , newBookmark(newBookmark) {} -			const MUCBookmark& getOldBookmark() {return oldBookmark;}; -			const MUCBookmark& getNewBookmark() {return newBookmark;}; +			const MUCBookmark& getOldBookmark() {return oldBookmark;} +			const MUCBookmark& getNewBookmark() {return newBookmark;}  		private:  			MUCBookmark oldBookmark; diff --git a/Swift/Controllers/UIEvents/JoinMUCUIEvent.h b/Swift/Controllers/UIEvents/JoinMUCUIEvent.h index b3ff8c7..c1e6de7 100644 --- a/Swift/Controllers/UIEvents/JoinMUCUIEvent.h +++ b/Swift/Controllers/UIEvents/JoinMUCUIEvent.h @@ -18,7 +18,7 @@ namespace Swift {  	class JoinMUCUIEvent : public UIEvent {  		public:  			typedef boost::shared_ptr<JoinMUCUIEvent> ref; -			JoinMUCUIEvent(const JID& jid, const boost::optional<std::string>& password = boost::optional<std::string>(), const boost::optional<std::string>& nick = boost::optional<std::string>(), bool joinAutomaticallyInFuture = false, bool createAsReservedRoomIfNew = false) : jid_(jid), nick_(nick), joinAutomatically_(joinAutomaticallyInFuture), createAsReservedRoomIfNew_(createAsReservedRoomIfNew), password_(password) {}; +			JoinMUCUIEvent(const JID& jid, const boost::optional<std::string>& password = boost::optional<std::string>(), const boost::optional<std::string>& nick = boost::optional<std::string>(), bool joinAutomaticallyInFuture = false, bool createAsReservedRoomIfNew = false) : jid_(jid), nick_(nick), joinAutomatically_(joinAutomaticallyInFuture), createAsReservedRoomIfNew_(createAsReservedRoomIfNew), password_(password) {}  			const boost::optional<std::string>& getNick() const {return nick_;}  			const JID& getJID() const {return jid_;}  			bool getShouldJoinAutomatically() const {return joinAutomatically_;} diff --git a/Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h b/Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h index ea2e609..0df40f9 100644 --- a/Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h +++ b/Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h @@ -14,7 +14,7 @@  namespace Swift {  	class RemoveMUCBookmarkUIEvent : public UIEvent {  		public: -			RemoveMUCBookmarkUIEvent(const MUCBookmark& bookmark) : bookmark(bookmark) {}; +			RemoveMUCBookmarkUIEvent(const MUCBookmark& bookmark) : bookmark(bookmark) {}  			const MUCBookmark& getBookmark() { return bookmark; }  		private: diff --git a/Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h b/Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h index 7e5236a..617daf3 100644 --- a/Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h +++ b/Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h @@ -13,9 +13,9 @@ namespace Swift {  class RemoveRosterItemUIEvent : public UIEvent {  	public: -		RemoveRosterItemUIEvent(const JID& jid) : jid_(jid) {}; -		virtual ~RemoveRosterItemUIEvent() {}; -		JID getJID() {return jid_;}; +		RemoveRosterItemUIEvent(const JID& jid) : jid_(jid) {} +		virtual ~RemoveRosterItemUIEvent() {} +		JID getJID() {return jid_;}  	private:  		JID jid_; diff --git a/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h b/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h index c3b4b49..a0b51f2 100644 --- a/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h +++ b/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h @@ -13,7 +13,7 @@  namespace Swift {  	class RequestAdHocUIEvent : public UIEvent {  		public: -			RequestAdHocUIEvent(const DiscoItems::Item& command) : command_(command) {}; +			RequestAdHocUIEvent(const DiscoItems::Item& command) : command_(command) {}  			const DiscoItems::Item& getCommand() const {return command_;}  		private:  			DiscoItems::Item command_; diff --git a/Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h b/Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h index 5a071cf..7fe1926 100644 --- a/Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h +++ b/Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h @@ -15,11 +15,11 @@ namespace Swift {  	class RequestAddUserDialogUIEvent : public UIEvent {  		public: -			RequestAddUserDialogUIEvent(const JID& predefinedJID, const std::string& predefinedName) : preJID_(predefinedJID), preName_(predefinedName) {}; -			RequestAddUserDialogUIEvent() : preJID_(), preName_() {}; +			RequestAddUserDialogUIEvent(const JID& predefinedJID, const std::string& predefinedName) : preJID_(predefinedJID), preName_(predefinedName) {} +			RequestAddUserDialogUIEvent() : preJID_(), preName_() {} -			const JID& getPredefinedJID() const { return preJID_; }; -			const std::string& getPredefinedName() const { return preName_; }; +			const JID& getPredefinedJID() const { return preJID_; } +			const std::string& getPredefinedName() const { return preName_; }  		private:  			JID preJID_; diff --git a/Swift/Controllers/UIEvents/RequestChatUIEvent.h b/Swift/Controllers/UIEvents/RequestChatUIEvent.h index b1e86ed..4ef954f 100644 --- a/Swift/Controllers/UIEvents/RequestChatUIEvent.h +++ b/Swift/Controllers/UIEvents/RequestChatUIEvent.h @@ -13,7 +13,7 @@  namespace Swift {  	class RequestChatUIEvent : public UIEvent {  		public: -			RequestChatUIEvent(const JID& contact) : contact_(contact) {}; +			RequestChatUIEvent(const JID& contact) : contact_(contact) {}  			JID getContact() {return contact_;}  		private:  			JID contact_; diff --git a/Swift/Controllers/UIEvents/RequestHighlightEditorUIEvent.h b/Swift/Controllers/UIEvents/RequestHighlightEditorUIEvent.h new file mode 100644 index 0000000..42e22a2 --- /dev/null +++ b/Swift/Controllers/UIEvents/RequestHighlightEditorUIEvent.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * 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 RequestHighlightEditorUIEvent : public UIEvent { +	}; + +} diff --git a/Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h b/Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h index f5b995b..5c44da7 100644 --- a/Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h +++ b/Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h @@ -13,7 +13,7 @@  namespace Swift {  	class RequestWhiteboardUIEvent : public UIEvent {  	public: -		RequestWhiteboardUIEvent(const JID& contact) : contact_(contact) {}; +		RequestWhiteboardUIEvent(const JID& contact) : contact_(contact) {}  		const JID& getContact() const {return contact_;}  	private:  		JID contact_; diff --git a/Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h b/Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h new file mode 100644 index 0000000..4a603ea --- /dev/null +++ b/Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/JID/JID.h> +#include <Swift/Controllers/UIEvents/UIEvent.h> + +namespace Swift { + +class ShowProfileForRosterItemUIEvent : public UIEvent { +	public: +		typedef boost::shared_ptr<ShowProfileForRosterItemUIEvent> ref; +	public: +		ShowProfileForRosterItemUIEvent(const JID& jid) : jid_(jid) {} +		virtual ~ShowProfileForRosterItemUIEvent() {} +		JID getJID() const {return jid_;} +	private: +		JID jid_; +}; + +} diff --git a/Swift/Controllers/UIEvents/ShowWhiteboardUIEvent.h b/Swift/Controllers/UIEvents/ShowWhiteboardUIEvent.h index 265bf7d..bb72d9b 100644 --- a/Swift/Controllers/UIEvents/ShowWhiteboardUIEvent.h +++ b/Swift/Controllers/UIEvents/ShowWhiteboardUIEvent.h @@ -13,7 +13,7 @@  namespace Swift {  	class ShowWhiteboardUIEvent : public UIEvent {  	public: -		ShowWhiteboardUIEvent(const JID& contact) : contact_(contact) {}; +		ShowWhiteboardUIEvent(const JID& contact) : contact_(contact) {}  		const JID& getContact() const {return contact_;}  	private:  		JID contact_; diff --git a/Swift/Controllers/UIEvents/UIEventStream.h b/Swift/Controllers/UIEvents/UIEventStream.h index e174029..b1337a2 100644 --- a/Swift/Controllers/UIEvents/UIEventStream.h +++ b/Swift/Controllers/UIEvents/UIEventStream.h @@ -18,6 +18,6 @@ namespace Swift {  			void send(boost::shared_ptr<UIEvent> event) {  				onUIEvent(event); -			}; +			}  	};  } diff --git a/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h b/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h index f7a5d39..835defe 100644 --- a/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h +++ b/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h @@ -9,6 +9,6 @@  namespace Swift {  	class AdHocCommandWindow {  		public: -			virtual ~AdHocCommandWindow() {}; +			virtual ~AdHocCommandWindow() {}  	};  } diff --git a/Swift/Controllers/UIInterfaces/ChatListWindow.h b/Swift/Controllers/UIInterfaces/ChatListWindow.h index cb55bb3..6eb932f 100644 --- a/Swift/Controllers/UIInterfaces/ChatListWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatListWindow.h @@ -25,7 +25,7 @@ namespace Swift {  					bool operator==(const Chat& other) const {  						return jid.toBare() == other.jid.toBare()  								&& isMUC == other.isMUC; -					}; +					}  					void setUnreadCount(int unread) {  						unreadCount = unread;  					} diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h index 5db1a54..d6b3656 100644 --- a/Swift/Controllers/UIInterfaces/ChatWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatWindow.h @@ -17,6 +17,7 @@  #include <Swiften/Elements/ChatState.h>  #include <Swiften/Elements/Form.h>  #include <Swiften/Elements/MUCOccupant.h> +#include <Swift/Controllers/HighlightManager.h>  namespace Swift { @@ -34,26 +35,26 @@ namespace Swift {  			enum AckState {Pending, Received, Failed};  			enum ReceiptState {ReceiptRequested, ReceiptReceived};  			enum Tristate {Yes, No, Maybe}; -			enum OccupantAction {Kick, Ban, MakeModerator, MakeParticipant, MakeVisitor, AddContact}; +			enum OccupantAction {Kick, Ban, MakeModerator, MakeParticipant, MakeVisitor, AddContact, ShowProfile};  			enum RoomAction {ChangeSubject, Configure, Affiliations, Destroy, Invite};  			enum FileTransferState {WaitingForAccept, Negotiating, Transferring, Canceled, Finished, FTFailed};  			enum WhiteboardSessionState {WhiteboardAccepted, WhiteboardTerminated, WhiteboardRejected};  			ChatWindow() {} -			virtual ~ChatWindow() {}; +			virtual ~ChatWindow() {}  			/** Add message to window.  			 * @return id of added message (for acks).  			 */ -			virtual 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) = 0; +			virtual 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) = 0;  			/** Adds action to window.  			 * @return id of added message (for acks);  			 */ -			virtual std::string addAction(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) = 0; +			virtual std::string addAction(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) = 0;  			virtual void addSystemMessage(const std::string& message) = 0;  			virtual void addPresenceMessage(const std::string& message) = 0;  			virtual void addErrorMessage(const std::string& message) = 0; -			virtual void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) = 0; -			virtual void replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) = 0; +			virtual void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) = 0; +			virtual void replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) = 0;  			// File transfer related stuff  			virtual std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) = 0; diff --git a/Swift/Controllers/UIInterfaces/ChatWindowFactory.h b/Swift/Controllers/UIInterfaces/ChatWindowFactory.h index b7b4479..62e6621 100644 --- a/Swift/Controllers/UIInterfaces/ChatWindowFactory.h +++ b/Swift/Controllers/UIInterfaces/ChatWindowFactory.h @@ -14,7 +14,7 @@ namespace Swift {  	class UIEventStream;  	class ChatWindowFactory {  		public: -			virtual ~ChatWindowFactory() {}; +			virtual ~ChatWindowFactory() {}  			/**  			 * Transfers ownership of result.  			 */ diff --git a/Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h b/Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h index 8ad56c0..9d47aef 100644 --- a/Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h +++ b/Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h @@ -11,7 +11,7 @@  namespace Swift {  	class ContactEditWindowFactory {  		public: -			virtual ~ContactEditWindowFactory() {}; +			virtual ~ContactEditWindowFactory() {}  			virtual ContactEditWindow* createContactEditWindow() = 0;  	}; diff --git a/Swift/Controllers/UIInterfaces/EventWindow.h b/Swift/Controllers/UIInterfaces/EventWindow.h index 3ca2c82..b3af3d3 100644 --- a/Swift/Controllers/UIInterfaces/EventWindow.h +++ b/Swift/Controllers/UIInterfaces/EventWindow.h @@ -19,7 +19,7 @@ namespace Swift {  				return canDelete_;  			} -			virtual ~EventWindow() {};	 +			virtual ~EventWindow() {}	  			virtual void addEvent(boost::shared_ptr<StanzaEvent> event, bool active) = 0;  			virtual void removeEvent(boost::shared_ptr<StanzaEvent> event) = 0; diff --git a/Swift/Controllers/UIInterfaces/EventWindowFactory.h b/Swift/Controllers/UIInterfaces/EventWindowFactory.h index 1ff3ada..0b9c28e 100644 --- a/Swift/Controllers/UIInterfaces/EventWindowFactory.h +++ b/Swift/Controllers/UIInterfaces/EventWindowFactory.h @@ -11,7 +11,7 @@ namespace Swift {  	class EventWindowFactory {  		public: -			virtual ~EventWindowFactory() {}; +			virtual ~EventWindowFactory() {}  			/**  			 * Transfers ownership of result.  			 */ diff --git a/Swift/Controllers/UIInterfaces/HighlightEditorWidget.h b/Swift/Controllers/UIInterfaces/HighlightEditorWidget.h new file mode 100644 index 0000000..4745035 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/HighlightEditorWidget.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +namespace Swift { + +	class HighlightManager; + +	class HighlightEditorWidget { +		public: +			virtual ~HighlightEditorWidget() {} + +			virtual void show() = 0; + +			virtual void setHighlightManager(HighlightManager* highlightManager) = 0; +	}; + +} diff --git a/Swift/Controllers/UIInterfaces/HighlightEditorWidgetFactory.h b/Swift/Controllers/UIInterfaces/HighlightEditorWidgetFactory.h new file mode 100644 index 0000000..ade575b --- /dev/null +++ b/Swift/Controllers/UIInterfaces/HighlightEditorWidgetFactory.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +namespace Swift { + +	class HighlightEditorWidget; + +	class HighlightEditorWidgetFactory { +		public: +			virtual ~HighlightEditorWidgetFactory() {} + +			virtual HighlightEditorWidget* createHighlightEditorWidget() = 0; +	}; + +} diff --git a/Swift/Controllers/UIInterfaces/HistoryWindow.h b/Swift/Controllers/UIInterfaces/HistoryWindow.h index ffb0ad5..6d50f4b 100644 --- a/Swift/Controllers/UIInterfaces/HistoryWindow.h +++ b/Swift/Controllers/UIInterfaces/HistoryWindow.h @@ -11,7 +11,7 @@  namespace Swift {  	class HistoryWindow {  		public: -			virtual ~HistoryWindow() {}; +			virtual ~HistoryWindow() {}  			virtual void activate() = 0;  			virtual void setRosterModel(Roster*) = 0; diff --git a/Swift/Controllers/UIInterfaces/HistoryWindowFactory.h b/Swift/Controllers/UIInterfaces/HistoryWindowFactory.h index e91bc37..807fec5 100644 --- a/Swift/Controllers/UIInterfaces/HistoryWindowFactory.h +++ b/Swift/Controllers/UIInterfaces/HistoryWindowFactory.h @@ -12,7 +12,7 @@ namespace Swift {  	class UIEventStream;  	class HistoryWindowFactory {  		public: -			virtual ~HistoryWindowFactory() {}; +			virtual ~HistoryWindowFactory() {}  			virtual HistoryWindow* createHistoryWindow(UIEventStream* eventStream) = 0;  	};  } diff --git a/Swift/Controllers/UIInterfaces/InviteToChatWindow.h b/Swift/Controllers/UIInterfaces/InviteToChatWindow.h index 4070e01..db128c1 100644 --- a/Swift/Controllers/UIInterfaces/InviteToChatWindow.h +++ b/Swift/Controllers/UIInterfaces/InviteToChatWindow.h @@ -15,7 +15,7 @@  namespace Swift {  	class InviteToChatWindow {  		public: -			virtual ~InviteToChatWindow() {}; +			virtual ~InviteToChatWindow() {}  			virtual void setAutoCompletions(std::vector<std::pair<JID, std::string> > completions) = 0; diff --git a/Swift/Controllers/UIInterfaces/JoinMUCWindow.h b/Swift/Controllers/UIInterfaces/JoinMUCWindow.h index 4873c9b..56a9587 100644 --- a/Swift/Controllers/UIInterfaces/JoinMUCWindow.h +++ b/Swift/Controllers/UIInterfaces/JoinMUCWindow.h @@ -15,7 +15,7 @@  namespace Swift {  	class JoinMUCWindow {  		public: -			virtual ~JoinMUCWindow() {}; +			virtual ~JoinMUCWindow() {}  			virtual void setNick(const std::string& nick) = 0;  			virtual void setMUC(const std::string& nick) = 0; diff --git a/Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h b/Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h index cd8021b..494b418 100644 --- a/Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h +++ b/Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h @@ -12,7 +12,7 @@ namespace Swift {  	class UIEventStream;  	class JoinMUCWindowFactory {  		public: -			virtual ~JoinMUCWindowFactory() {}; +			virtual ~JoinMUCWindowFactory() {}  			virtual JoinMUCWindow* createJoinMUCWindow(UIEventStream* uiEventStream) = 0;  	}; diff --git a/Swift/Controllers/UIInterfaces/LoginWindow.h b/Swift/Controllers/UIInterfaces/LoginWindow.h index dbc77c4..e27c385 100644 --- a/Swift/Controllers/UIInterfaces/LoginWindow.h +++ b/Swift/Controllers/UIInterfaces/LoginWindow.h @@ -18,7 +18,7 @@ namespace Swift {  	class MainWindow;  	class LoginWindow {  		public: -			virtual ~LoginWindow() {}; +			virtual ~LoginWindow() {}  			virtual void selectUser(const std::string&) = 0;  			virtual void morphInto(MainWindow *mainWindow) = 0;  			virtual void loggedOut() = 0; diff --git a/Swift/Controllers/UIInterfaces/LoginWindowFactory.h b/Swift/Controllers/UIInterfaces/LoginWindowFactory.h index 1cead2a..7b8b7ec 100644 --- a/Swift/Controllers/UIInterfaces/LoginWindowFactory.h +++ b/Swift/Controllers/UIInterfaces/LoginWindowFactory.h @@ -16,7 +16,7 @@ namespace Swift {  	class LoginWindowFactory {  		public: -			virtual ~LoginWindowFactory() {}; +			virtual ~LoginWindowFactory() {}  			/**  			 * Transfers ownership of result. diff --git a/Swift/Controllers/UIInterfaces/MUCSearchWindow.h b/Swift/Controllers/UIInterfaces/MUCSearchWindow.h index 5814b06..43a61a1 100644 --- a/Swift/Controllers/UIInterfaces/MUCSearchWindow.h +++ b/Swift/Controllers/UIInterfaces/MUCSearchWindow.h @@ -19,7 +19,7 @@ namespace Swift {  	class MUCSearchWindow {  		public: -			virtual ~MUCSearchWindow() {}; +			virtual ~MUCSearchWindow() {}  			virtual void clearList() = 0;  			virtual void addService(const MUCService& service) = 0; diff --git a/Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h b/Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h index d334dff..46488eb 100644 --- a/Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h +++ b/Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h @@ -12,7 +12,7 @@ namespace Swift {  	class UIEventStream;  	class MUCSearchWindowFactory {  		public: -			virtual ~MUCSearchWindowFactory() {}; +			virtual ~MUCSearchWindowFactory() {}  			virtual MUCSearchWindow* createMUCSearchWindow() = 0;  	}; diff --git a/Swift/Controllers/UIInterfaces/MainWindow.h b/Swift/Controllers/UIInterfaces/MainWindow.h index 23328b4..2df2c10 100644 --- a/Swift/Controllers/UIInterfaces/MainWindow.h +++ b/Swift/Controllers/UIInterfaces/MainWindow.h @@ -20,7 +20,7 @@ namespace Swift {  	class MainWindow {  		public:  			MainWindow(bool candelete = true) : canDelete_(candelete) {} -			virtual ~MainWindow() {}; +			virtual ~MainWindow() {}  			bool canDelete() const {  				return canDelete_; diff --git a/Swift/Controllers/UIInterfaces/MainWindowFactory.h b/Swift/Controllers/UIInterfaces/MainWindowFactory.h index c5cdfef..6bd34b4 100644 --- a/Swift/Controllers/UIInterfaces/MainWindowFactory.h +++ b/Swift/Controllers/UIInterfaces/MainWindowFactory.h @@ -15,7 +15,7 @@ namespace Swift {  	class MainWindowFactory {  		public: -			virtual ~MainWindowFactory() {}; +			virtual ~MainWindowFactory() {}  			/**  			 * Transfers ownership of result.  			 */ diff --git a/Swift/Controllers/UIInterfaces/ProfileWindow.h b/Swift/Controllers/UIInterfaces/ProfileWindow.h index 5d5c754..0ce2ccf 100644 --- a/Swift/Controllers/UIInterfaces/ProfileWindow.h +++ b/Swift/Controllers/UIInterfaces/ProfileWindow.h @@ -12,19 +12,24 @@  #include <Swiften/Elements/VCard.h>  namespace Swift { +	class JID; +  	class ProfileWindow {  		public: -			virtual ~ProfileWindow() {}; +			virtual ~ProfileWindow() {} +			virtual void setJID(const JID& jid) = 0;  			virtual void setVCard(VCard::ref vcard) = 0;  			virtual void setEnabled(bool b) = 0;  			virtual void setProcessing(bool b) = 0;  			virtual void setError(const std::string&) = 0; +			virtual void setEditable(bool b) = 0;  			virtual void show() = 0;  			virtual void hide() = 0;  			boost::signal<void (VCard::ref)> onVCardChangeRequest; +			boost::signal<void (const JID&)> onWindowClosed;  	};  } diff --git a/Swift/Controllers/UIInterfaces/ProfileWindowFactory.h b/Swift/Controllers/UIInterfaces/ProfileWindowFactory.h index 022c3eb..45a340a 100644 --- a/Swift/Controllers/UIInterfaces/ProfileWindowFactory.h +++ b/Swift/Controllers/UIInterfaces/ProfileWindowFactory.h @@ -11,7 +11,7 @@  namespace Swift {  	class ProfileWindowFactory {  		public: -			virtual ~ProfileWindowFactory() {}; +			virtual ~ProfileWindowFactory() {}  			virtual ProfileWindow* createProfileWindow() = 0;  	}; diff --git a/Swift/Controllers/UIInterfaces/UIFactory.h b/Swift/Controllers/UIInterfaces/UIFactory.h index 6b4efd8..dcd1779 100644 --- a/Swift/Controllers/UIInterfaces/UIFactory.h +++ b/Swift/Controllers/UIInterfaces/UIFactory.h @@ -21,6 +21,7 @@  #include <Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h>  #include <Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h>  #include <Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h> +#include <Swift/Controllers/UIInterfaces/HighlightEditorWidgetFactory.h>  namespace Swift {  	class UIFactory :  @@ -38,7 +39,8 @@ namespace Swift {  			public ContactEditWindowFactory,  			public AdHocCommandWindowFactory,  			public FileTransferListWidgetFactory, -			public WhiteboardWindowFactory { +			public WhiteboardWindowFactory, +			public HighlightEditorWidgetFactory {  		public:  			virtual ~UIFactory() {}  	}; diff --git a/Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h b/Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h index 2a15806..331d6dd 100644 --- a/Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h +++ b/Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h @@ -14,7 +14,7 @@ namespace Swift {  	class UIEventStream;  	class UserSearchWindowFactory {  		public: -			virtual ~UserSearchWindowFactory() {}; +			virtual ~UserSearchWindowFactory() {}  			virtual UserSearchWindow* createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream, const std::set<std::string>& groups) = 0;  	}; diff --git a/Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h b/Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h index c2d2f6c..2be0f9c 100644 --- a/Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h +++ b/Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h @@ -12,7 +12,7 @@ namespace Swift {  	class WhiteboardWindowFactory {  	public : -		virtual ~WhiteboardWindowFactory() {}; +		virtual ~WhiteboardWindowFactory() {}  		virtual WhiteboardWindow* createWhiteboardWindow(boost::shared_ptr<WhiteboardSession> whiteboardSession) = 0;  	}; diff --git a/Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h b/Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h index e27fe2e..3cba597 100644 --- a/Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h +++ b/Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h @@ -12,7 +12,7 @@ namespace Swift {  	class UIEventStream;  	class XMLConsoleWidgetFactory {  		public: -			virtual ~XMLConsoleWidgetFactory() {}; +			virtual ~XMLConsoleWidgetFactory() {}  			virtual XMLConsoleWidget* createXMLConsoleWidget() = 0;  	}; diff --git a/Swift/Controllers/UnitTest/ChatMessageSummarizerTest.cpp b/Swift/Controllers/UnitTest/ChatMessageSummarizerTest.cpp index ee0ee9f..985352a 100644 --- a/Swift/Controllers/UnitTest/ChatMessageSummarizerTest.cpp +++ b/Swift/Controllers/UnitTest/ChatMessageSummarizerTest.cpp @@ -26,7 +26,7 @@ class ChatMessageSummarizerTest : public CppUnit::TestFixture {  	CPPUNIT_TEST_SUITE_END();  public: -	ChatMessageSummarizerTest() {}; +	ChatMessageSummarizerTest() {}  	void setUp() { diff --git a/Swift/Controllers/UnitTest/HighlightRuleTest.cpp b/Swift/Controllers/UnitTest/HighlightRuleTest.cpp new file mode 100644 index 0000000..ec81227 --- /dev/null +++ b/Swift/Controllers/UnitTest/HighlightRuleTest.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <vector> +#include <string> + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <Swift/Controllers/HighlightRule.h> + +using namespace Swift; + +class HighlightRuleTest : public CppUnit::TestFixture { +		CPPUNIT_TEST_SUITE(HighlightRuleTest); +		CPPUNIT_TEST(testEmptyRuleNeverMatches); +		CPPUNIT_TEST(testKeyword); +		CPPUNIT_TEST(testNickKeyword); +		CPPUNIT_TEST(testNickWithoutOtherKeywords); +		CPPUNIT_TEST(testSender); +		CPPUNIT_TEST(testSenderAndKeyword); +		CPPUNIT_TEST(testWholeWords); +		CPPUNIT_TEST(testCase); +		CPPUNIT_TEST(testWholeWordsAndCase); +		CPPUNIT_TEST(testMUC); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void setUp() { +			std::vector<std::string> keywords; +			keywords.push_back("keyword1"); +			keywords.push_back("KEYWORD2"); + +			std::vector<std::string>senders; +			senders.push_back("sender1"); +			senders.push_back("SENDER2"); + +			emptyRule = new HighlightRule(); + +			keywordRule = new HighlightRule(); +			keywordRule->setKeywords(keywords); + +			keywordChatRule = new HighlightRule(); +			keywordChatRule->setKeywords(keywords); +			keywordChatRule->setMatchChat(true); + +			keywordNickChatRule = new HighlightRule(); +			keywordNickChatRule->setKeywords(keywords); +			keywordNickChatRule->setNickIsKeyword(true); +			keywordNickChatRule->setMatchChat(true); + +			nickChatRule = new HighlightRule(); +			nickChatRule->setNickIsKeyword(true); +			nickChatRule->setMatchChat(true); + +			nickRule = new HighlightRule(); +			nickRule->setNickIsKeyword(true); + +			senderRule = new HighlightRule(); +			senderRule->setSenders(senders); + +			senderChatRule = new HighlightRule(); +			senderChatRule->setSenders(senders); +			senderChatRule->setMatchChat(true); + +			senderKeywordChatRule = new HighlightRule(); +			senderKeywordChatRule->setSenders(senders); +			senderKeywordChatRule->setKeywords(keywords); +			senderKeywordChatRule->setMatchChat(true); + +			senderKeywordNickChatRule = new HighlightRule(); +			senderKeywordNickChatRule->setSenders(senders); +			senderKeywordNickChatRule->setKeywords(keywords); +			senderKeywordNickChatRule->setNickIsKeyword(true); +			senderKeywordNickChatRule->setMatchChat(true); + +			senderKeywordNickWordChatRule = new HighlightRule(); +			senderKeywordNickWordChatRule->setSenders(senders); +			senderKeywordNickWordChatRule->setKeywords(keywords); +			senderKeywordNickWordChatRule->setNickIsKeyword(true); +			senderKeywordNickWordChatRule->setMatchWholeWords(true); +			senderKeywordNickWordChatRule->setMatchChat(true); + +			senderKeywordNickCaseChatRule = new HighlightRule(); +			senderKeywordNickCaseChatRule->setSenders(senders); +			senderKeywordNickCaseChatRule->setKeywords(keywords); +			senderKeywordNickCaseChatRule->setNickIsKeyword(true); +			senderKeywordNickCaseChatRule->setMatchCase(true); +			senderKeywordNickCaseChatRule->setMatchChat(true); + +			senderKeywordNickCaseWordChatRule = new HighlightRule(); +			senderKeywordNickCaseWordChatRule->setSenders(senders); +			senderKeywordNickCaseWordChatRule->setKeywords(keywords); +			senderKeywordNickCaseWordChatRule->setNickIsKeyword(true); +			senderKeywordNickCaseWordChatRule->setMatchCase(true); +			senderKeywordNickCaseWordChatRule->setMatchWholeWords(true); +			senderKeywordNickCaseWordChatRule->setMatchChat(true); + +			senderKeywordNickMUCRule = new HighlightRule(); +			senderKeywordNickMUCRule->setSenders(senders); +			senderKeywordNickMUCRule->setKeywords(keywords); +			senderKeywordNickMUCRule->setNickIsKeyword(true); +			senderKeywordNickMUCRule->setMatchMUC(true); +		} + +		void tearDown() { +			delete emptyRule; + +			delete keywordRule; +			delete keywordChatRule; +			delete keywordNickChatRule; +			delete nickChatRule; +			delete nickRule; + +			delete senderRule; +			delete senderChatRule; +			delete senderKeywordChatRule; +			delete senderKeywordNickChatRule; + +			delete senderKeywordNickWordChatRule; +			delete senderKeywordNickCaseChatRule; +			delete senderKeywordNickCaseWordChatRule; + +			delete senderKeywordNickMUCRule; +		} + +		void testEmptyRuleNeverMatches() { +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "from", "nick", HighlightRule::MUCMessage), false); + +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "from", "", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "from", "", HighlightRule::MUCMessage), false); + +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "", "nick", HighlightRule::MUCMessage), false); + +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "from", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "from", "nick", HighlightRule::MUCMessage), false); + +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "", "", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("body", "", "", HighlightRule::MUCMessage), false); + +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "from", "", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "from", "", HighlightRule::MUCMessage), false); + +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "", "nick", HighlightRule::MUCMessage), false); + +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "", "", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(emptyRule->isMatch("", "", "", HighlightRule::MUCMessage), false); +		} + +		void testKeyword() { +			CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("body", "from", "nick", HighlightRule::MUCMessage), false); + +			CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("keyword1", "from", "nick", HighlightRule::MUCMessage), false); +			CPPUNIT_ASSERT_EQUAL(keywordRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("body", "sender contains keyword1", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("abc keyword1 xyz", "from", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("abckeyword1xyz", "from", "nick", HighlightRule::ChatMessage), true); + +			CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("KEYword1", "from", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("abc KEYword1 xyz", "from", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("abcKEYword1xyz", "from", "nick", HighlightRule::ChatMessage), true); + +			CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("keyword2", "from", "nick", HighlightRule::ChatMessage), true); +		} + +		void testNickKeyword() { +			CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("body contains nick", "from", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("body contains nick", "from", "nick", HighlightRule::MUCMessage), false); +			CPPUNIT_ASSERT_EQUAL(keywordChatRule->isMatch("body contains nick", "from", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), true); + +			CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("body", "sender contains nick", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("body contains mixed-case NiCk", "sender", "nick", HighlightRule::ChatMessage), true); + +			CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("nickname", "from", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("NIckNAME", "from", "nick", HighlightRule::ChatMessage), true); + +			CPPUNIT_ASSERT_EQUAL(keywordNickChatRule->isMatch("body", "from", "", HighlightRule::ChatMessage), false); +		} + +		void testNickWithoutOtherKeywords() { +			CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("body contains nick", "from", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("body contains nick", "from", "nick", HighlightRule::MUCMessage), false); +			CPPUNIT_ASSERT_EQUAL(nickRule->isMatch("body contains nick", "from", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("body", "sender contains nick but it does't matter", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("body contains mixed-case NiCk", "from", "nick", HighlightRule::ChatMessage), true); + +			CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("nickname", "from", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("NIckNAME", "from", "nick", HighlightRule::ChatMessage), true); + +			// there are no keywords in this rule and empty nick is not treated as a keyword, so we don't check for keywords to get a match +			CPPUNIT_ASSERT_EQUAL(nickChatRule->isMatch("body", "from", "", HighlightRule::ChatMessage), true); +		} + +		void testSender() { +			CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "from", "nick", HighlightRule::MUCMessage), false); + +			CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "sender1", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "sender1", "nick", HighlightRule::MUCMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderRule->isMatch("body", "sender1", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(senderRule->isMatch("body contains sender1", "from", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "abc sender1 xyz", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "abcsender1xyz", "nick", HighlightRule::ChatMessage), true); + +			CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "SENDer1", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "abc SENDer1 xyz", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "abcSENDer1xyz", "nick", HighlightRule::ChatMessage), true); + +			CPPUNIT_ASSERT_EQUAL(senderChatRule->isMatch("body", "sender2", "nick", HighlightRule::ChatMessage), true); +		} + +		void testSenderAndKeyword() { +			CPPUNIT_ASSERT_EQUAL(senderKeywordChatRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordChatRule->isMatch("body", "sender1", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordChatRule->isMatch("keyword1", "sender1", "nick", HighlightRule::ChatMessage), true); +		} + +		void testWholeWords() { +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("body", "sender1", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("keyword1", "sender1", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("xkeyword1", "sender1", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("keyword1", "xsender1", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("body contains nick", "sender1", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("body contains xnick", "sender1", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("KEYword1", "SENDer1", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickWordChatRule->isMatch("body contains NiCk", "sender1", "nick", HighlightRule::ChatMessage), true); +		} + +		void testCase() { +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("body", "sender1", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("keyword1", "sender1", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("xkeyword1", "xsender1", "nick", HighlightRule::ChatMessage), true); + +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("body contains nick", "sender1", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("body contains xnick", "sender1", "nick", HighlightRule::ChatMessage), true); + +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("KEYword1", "SENDer1", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("keyword1", "SENDer1", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("KEYword1", "sender1", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseChatRule->isMatch("body contains NiCk", "sender1", "nick", HighlightRule::ChatMessage), false); +		} + +		void testWholeWordsAndCase() { +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("keyword1", "from", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("body", "sender1", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("keyword1", "sender1", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("xkeyword1", "sender1", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("keyword1", "xsender1", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("body contains nick", "sender1", "nick", HighlightRule::ChatMessage), true); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("body contains xnick", "sender1", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("KEYword1", "SENDer1", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("keyword1", "SENDer1", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("KEYword1", "sender1", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickCaseWordChatRule->isMatch("body contains NiCk", "sender1", "nick", HighlightRule::ChatMessage), false); +		} + +		void testMUC() { +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickMUCRule->isMatch("body", "from", "nick", HighlightRule::ChatMessage), false); + +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickMUCRule->isMatch("keyword1", "sender1", "nick", HighlightRule::ChatMessage), false); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickMUCRule->isMatch("keyword1", "sender1", "nick", HighlightRule::MUCMessage), true); +			CPPUNIT_ASSERT_EQUAL(senderKeywordNickMUCRule->isMatch("body contains nick", "sender1", "nick", HighlightRule::MUCMessage), true); +		} + +	private: +		HighlightRule* emptyRule; + +		HighlightRule* keywordRule; +		HighlightRule* keywordChatRule; +		HighlightRule* keywordNickChatRule; +		HighlightRule* nickChatRule; +		HighlightRule* nickRule; + +		HighlightRule* senderRule; +		HighlightRule* senderChatRule; +		HighlightRule* senderKeywordChatRule; +		HighlightRule* senderKeywordNickChatRule; + +		HighlightRule* senderKeywordNickWordChatRule; +		HighlightRule* senderKeywordNickCaseChatRule; +		HighlightRule* senderKeywordNickCaseWordChatRule; + +		HighlightRule* senderKeywordNickMUCRule; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(HighlightRuleTest); diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h index 998a4eb..84aaa04 100644 --- a/Swift/Controllers/UnitTest/MockChatWindow.h +++ b/Swift/Controllers/UnitTest/MockChatWindow.h @@ -11,53 +11,53 @@  namespace Swift {  	class MockChatWindow : public ChatWindow {  		public: -			MockChatWindow() : labelsEnabled_(false) {}; +			MockChatWindow() : labelsEnabled_(false) {}  			virtual ~MockChatWindow(); -			virtual 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&) {lastMessageBody_ = message; return "";}; -			virtual std::string addAction(const std::string& message, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime&) {lastMessageBody_ = message; return "";}; -			virtual void addSystemMessage(const std::string& /*message*/) {}; -			virtual void addErrorMessage(const std::string& /*message*/) {}; -			virtual void addPresenceMessage(const std::string& /*message*/) {}; +			virtual 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&, const HighlightAction&) {lastMessageBody_ = message; return "";} +			virtual std::string addAction(const std::string& message, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime&, const HighlightAction&) {lastMessageBody_ = message; return "";} +			virtual void addSystemMessage(const std::string& /*message*/) {} +			virtual void addErrorMessage(const std::string& /*message*/) {} +			virtual void addPresenceMessage(const std::string& /*message*/) {}  			// File transfer related stuff -			virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/,const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/) { return 0; }; -			virtual void setFileTransferProgress(std::string /*id*/, const int /*alreadyTransferedBytes*/) { }; -			virtual void setFileTransferStatus(std::string /*id*/, const FileTransferState /*state*/, const std::string& /*msg*/) { }; +			virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/,const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/) { return 0; } +			virtual void setFileTransferProgress(std::string /*id*/, const int /*alreadyTransferedBytes*/) { } +			virtual void setFileTransferStatus(std::string /*id*/, const FileTransferState /*state*/, const std::string& /*msg*/) { }  			virtual void setMessageReceiptState(const std::string &/* id */, ReceiptState /* state */) { } -			virtual void setContactChatState(ChatState::ChatStateType /*state*/) {}; -			virtual void setName(const std::string& name) {name_ = name;}; -			virtual void show() {}; -			virtual void activate() {}; -			virtual void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) {labels_ = labels;}; -			virtual void setSecurityLabelsEnabled(bool enabled) {labelsEnabled_ = enabled;}; -			virtual void setUnreadMessageCount(int /*count*/) {}; -			virtual void convertToMUC() {}; -			virtual void setSecurityLabelsError() {}; +			virtual void setContactChatState(ChatState::ChatStateType /*state*/) {} +			virtual void setName(const std::string& name) {name_ = name;} +			virtual void show() {} +			virtual void activate() {} +			virtual void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) {labels_ = labels;} +			virtual void setSecurityLabelsEnabled(bool enabled) {labelsEnabled_ = enabled;} +			virtual void setUnreadMessageCount(int /*count*/) {} +			virtual void convertToMUC() {} +			virtual void setSecurityLabelsError() {}  			virtual SecurityLabelsCatalog::Item getSelectedSecurityLabel() {return label_;} -			virtual void setInputEnabled(bool /*enabled*/) {}; -			virtual void setRosterModel(Roster* /*roster*/) {}; -			virtual void setTabComplete(TabComplete*) {}; -			virtual void replaceLastMessage(const std::string&) {}; -			virtual void replaceMessage(const std::string&, const std::string&, const boost::posix_time::ptime&) {}; -			virtual void replaceWithAction(const std::string& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/) {}; -			void setAckState(const std::string& /*id*/, AckState /*state*/) {}; -			virtual void flash() {}; -			virtual void setAlert(const std::string& /*alertText*/, const std::string& /*buttonText*/) {}; -			virtual void cancelAlert() {}; +			virtual void setInputEnabled(bool /*enabled*/) {} +			virtual void setRosterModel(Roster* /*roster*/) {} +			virtual void setTabComplete(TabComplete*) {} +			virtual void replaceLastMessage(const std::string&) {} +			virtual void replaceMessage(const std::string&, const std::string&, const boost::posix_time::ptime&, const HighlightAction&) {} +			virtual void replaceWithAction(const std::string& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction&) {} +			void setAckState(const std::string& /*id*/, AckState /*state*/) {} +			virtual void flash() {} +			virtual void setAlert(const std::string& /*alertText*/, const std::string& /*buttonText*/) {} +			virtual void cancelAlert() {}  			virtual void setCorrectionEnabled(Tristate /*enabled*/) {}  			void setAvailableOccupantActions(const std::vector<OccupantAction>&/* actions*/) {}  			void setSubject(const std::string& /*subject*/) {}  			virtual void showRoomConfigurationForm(Form::ref) {} -			virtual void addMUCInvitation(const std::string& /*senderName*/, const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool = true) {}; +			virtual void addMUCInvitation(const std::string& /*senderName*/, const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool = true) {} -			virtual std::string addWhiteboardRequest(bool) {return "";}; -			virtual void setWhiteboardSessionStatus(std::string, const ChatWindow::WhiteboardSessionState){}; +			virtual std::string addWhiteboardRequest(bool) {return "";} +			virtual void setWhiteboardSessionStatus(std::string, const ChatWindow::WhiteboardSessionState){}  			virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&) {} -			virtual void setAvailableRoomActions(const std::vector<RoomAction> &) {}; +			virtual void setAvailableRoomActions(const std::vector<RoomAction> &) {}  			virtual InviteToChatWindow* createInviteToChatWindow() {return NULL;}  			std::string name_; diff --git a/Swift/Controllers/UnitTest/MockMainWindow.h b/Swift/Controllers/UnitTest/MockMainWindow.h index be1a932..19ab522 100644 --- a/Swift/Controllers/UnitTest/MockMainWindow.h +++ b/Swift/Controllers/UnitTest/MockMainWindow.h @@ -12,16 +12,16 @@ namespace Swift {  	class Roster;  	class MockMainWindow : public MainWindow {  		public: -			MockMainWindow() : roster(NULL) {}; -			virtual ~MockMainWindow() {}; -			virtual void setRosterModel(Roster* roster) {this->roster = roster;}; -			virtual void setMyNick(const std::string& /*name*/) {};; -			virtual void setMyJID(const JID& /*jid*/) {};; -			virtual void setMyAvatarPath(const std::string& /*path*/) {}; -			virtual void setMyStatusText(const std::string& /*status*/) {}; -			virtual void setMyStatusType(StatusShow::Type /*type*/) {}; -			virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& /*commands*/) {}; -			virtual void setConnecting() {}; +			MockMainWindow() : roster(NULL) {} +			virtual ~MockMainWindow() {} +			virtual void setRosterModel(Roster* roster) {this->roster = roster;} +			virtual void setMyNick(const std::string& /*name*/) {} +			virtual void setMyJID(const JID& /*jid*/) {} +			virtual void setMyAvatarPath(const std::string& /*path*/) {} +			virtual void setMyStatusText(const std::string& /*status*/) {} +			virtual void setMyStatusType(StatusShow::Type /*type*/) {} +			virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& /*commands*/) {} +			virtual void setConnecting() {}  			virtual void setStreamEncryptionStatus(bool /*tlsInPlaceAndValid*/) {}  			virtual void openCertificateDialog(const std::vector<Certificate::ref>& /*chain*/) {}  			Roster* roster; diff --git a/Swift/Controllers/UnitTest/MockMainWindowFactory.h b/Swift/Controllers/UnitTest/MockMainWindowFactory.h index d130b39..279a6dd 100644 --- a/Swift/Controllers/UnitTest/MockMainWindowFactory.h +++ b/Swift/Controllers/UnitTest/MockMainWindowFactory.h @@ -13,14 +13,14 @@ namespace Swift {  	class MockMainWindowFactory : public MainWindowFactory {  		public: -			MockMainWindowFactory() : last(NULL) {}; +			MockMainWindowFactory() : last(NULL) {} -			virtual ~MockMainWindowFactory() {}; +			virtual ~MockMainWindowFactory() {}  			/**  			 * Transfers ownership of result.  			 */ -			virtual MainWindow* createMainWindow(UIEventStream*) {last = new MockMainWindow();return last;}; +			virtual MainWindow* createMainWindow(UIEventStream*) {last = new MockMainWindow();return last;}  			MockMainWindow* last;  	};  } diff --git a/Swift/Controllers/XMPPEvents/ErrorEvent.h b/Swift/Controllers/XMPPEvents/ErrorEvent.h index cbfc471..ac09de9 100644 --- a/Swift/Controllers/XMPPEvents/ErrorEvent.h +++ b/Swift/Controllers/XMPPEvents/ErrorEvent.h @@ -18,10 +18,10 @@  namespace Swift {  	class ErrorEvent : public StanzaEvent {  		public: -			ErrorEvent(const JID& jid, const std::string& text) : jid_(jid), text_(text){}; -			virtual ~ErrorEvent(){}; -			const JID& getJID() const {return jid_;}; -			const std::string& getText() const {return text_;}; +			ErrorEvent(const JID& jid, const std::string& text) : jid_(jid), text_(text){} +			virtual ~ErrorEvent(){} +			const JID& getJID() const {return jid_;} +			const std::string& getText() const {return text_;}  		private:  			JID jid_; diff --git a/Swift/Controllers/XMPPEvents/EventController.cpp b/Swift/Controllers/XMPPEvents/EventController.cpp index d84dfe3..8cb259b 100644 --- a/Swift/Controllers/XMPPEvents/EventController.cpp +++ b/Swift/Controllers/XMPPEvents/EventController.cpp @@ -7,6 +7,7 @@  #include <Swift/Controllers/XMPPEvents/EventController.h>  #include <boost/bind.hpp> +#include <boost/numeric/conversion/cast.hpp>  #include <algorithm>  #include <Swiften/Base/foreach.h> @@ -48,7 +49,7 @@ void EventController::handleIncomingEvent(boost::shared_ptr<StanzaEvent> sourceE  	if ((messageEvent && messageEvent->isReadable()) || subscriptionEvent || errorEvent || mucInviteEvent) {  		events_.push_back(sourceEvent);  		sourceEvent->onConclusion.connect(boost::bind(&EventController::handleEventConcluded, this, sourceEvent)); -		onEventQueueLengthChange(events_.size()); +		onEventQueueLengthChange(boost::numeric_cast<int>(events_.size()));  		onEventQueueEventAdded(sourceEvent);  		if (sourceEvent->getConcluded()) {  			handleEventConcluded(sourceEvent); @@ -59,7 +60,7 @@ void EventController::handleIncomingEvent(boost::shared_ptr<StanzaEvent> sourceE  void EventController::handleEventConcluded(boost::shared_ptr<StanzaEvent> event) {  	event->onConclusion.disconnect(boost::bind(&EventController::handleEventConcluded, this, event));  	events_.erase(std::remove(events_.begin(), events_.end(), event), events_.end()); -	onEventQueueLengthChange(events_.size()); +	onEventQueueLengthChange(boost::numeric_cast<int>(events_.size()));  }  void EventController::disconnectAll() { diff --git a/Swift/Controllers/XMPPEvents/MessageEvent.h b/Swift/Controllers/XMPPEvents/MessageEvent.h index 1093470..a9214f5 100644 --- a/Swift/Controllers/XMPPEvents/MessageEvent.h +++ b/Swift/Controllers/XMPPEvents/MessageEvent.h @@ -17,7 +17,7 @@ namespace Swift {  		public:  			typedef boost::shared_ptr<MessageEvent> ref; -			MessageEvent(boost::shared_ptr<Message> stanza) : stanza_(stanza), targetsMe_(true) {}; +			MessageEvent(boost::shared_ptr<Message> stanza) : stanza_(stanza), targetsMe_(true) {}  			boost::shared_ptr<Message> getStanza() {return stanza_;} diff --git a/Swift/Controllers/XMPPEvents/StanzaEvent.h b/Swift/Controllers/XMPPEvents/StanzaEvent.h index 321d23d..a15afc1 100644 --- a/Swift/Controllers/XMPPEvents/StanzaEvent.h +++ b/Swift/Controllers/XMPPEvents/StanzaEvent.h @@ -14,13 +14,13 @@  namespace Swift {  	class StanzaEvent {  		public: -			StanzaEvent() : time_(boost::posix_time::microsec_clock::universal_time()) {concluded_ = false;}; -			virtual ~StanzaEvent() {}; -			void conclude() {concluded_ = true; onConclusion();}; +			StanzaEvent() : time_(boost::posix_time::microsec_clock::universal_time()) {concluded_ = false;} +			virtual ~StanzaEvent() {} +			void conclude() {concluded_ = true; onConclusion();}  			/** Do not call this directly from outside the class.  			 * If you connect to this signal, you *must* disconnect from it manually. */  			boost::signal<void()> onConclusion; -			bool getConcluded() {return concluded_;}; +			bool getConcluded() {return concluded_;}  			boost::posix_time::ptime getTime() {return time_;}  		private:  			bool concluded_; diff --git a/Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h b/Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h index 1f7812e..fb7a05e 100644 --- a/Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h +++ b/Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h @@ -18,21 +18,21 @@  namespace Swift {  	class SubscriptionRequestEvent : public StanzaEvent {  		public: -			SubscriptionRequestEvent(const JID& jid, const std::string& reason) : jid_(jid), reason_(reason){}; -			virtual ~SubscriptionRequestEvent(){}; -			const JID& getJID() const {return jid_;}; -			const std::string& getReason() const {return reason_;}; +			SubscriptionRequestEvent(const JID& jid, const std::string& reason) : jid_(jid), reason_(reason){} +			virtual ~SubscriptionRequestEvent(){} +			const JID& getJID() const {return jid_;} +			const std::string& getReason() const {return reason_;}  			boost::signal<void()> onAccept;  			boost::signal<void()> onDecline;  			void accept() {  				onAccept();  				conclude(); -			}; +			}  			void decline() {  				onDecline();  				conclude(); -			}; +			}  			void defer() {  				conclude(); diff --git a/Swift/QtUI/ChatList/ChatListGroupItem.h b/Swift/QtUI/ChatList/ChatListGroupItem.h index a1e479f..17defea 100644 --- a/Swift/QtUI/ChatList/ChatListGroupItem.h +++ b/Swift/QtUI/ChatList/ChatListGroupItem.h @@ -13,14 +13,14 @@  namespace Swift {  	class ChatListGroupItem : public ChatListItem {  		public: -			ChatListGroupItem(const QString& name, ChatListGroupItem* parent, bool sorted = true) : ChatListItem(parent), name_(name), sorted_(sorted) {}; -			void addItem(ChatListItem* item) {items_.push_back(item); if (sorted_) {qStableSort(items_.begin(), items_.end(), pointerItemLessThan);}}; -			void remove(int index) {items_.removeAt(index);}; -			int rowCount() {return items_.size();}; -			ChatListItem* item(int i) {return items_[i];}; -			int row(ChatListItem* item) {return items_.indexOf(item);}; -			QVariant data(int role) const {return (role == Qt::DisplayRole) ? name_ : QVariant();}; -			void clear() {items_.clear();}; +			ChatListGroupItem(const QString& name, ChatListGroupItem* parent, bool sorted = true) : ChatListItem(parent), name_(name), sorted_(sorted) {} +			void addItem(ChatListItem* item) {items_.push_back(item); if (sorted_) {qStableSort(items_.begin(), items_.end(), pointerItemLessThan);}} +			void remove(int index) {items_.removeAt(index);} +			int rowCount() {return items_.size();} +			ChatListItem* item(int i) {return items_[i];} +			int row(ChatListItem* item) {return items_.indexOf(item);} +			QVariant data(int role) const {return (role == Qt::DisplayRole) ? name_ : QVariant();} +			void clear() {items_.clear();}  		private:  			static bool pointerItemLessThan(const ChatListItem* first, const ChatListItem* second) {  				QString myName = first->data(Qt::DisplayRole).toString().toLower(); diff --git a/Swift/QtUI/ChatList/ChatListItem.h b/Swift/QtUI/ChatList/ChatListItem.h index e7be614..28c0f9c 100644 --- a/Swift/QtUI/ChatList/ChatListItem.h +++ b/Swift/QtUI/ChatList/ChatListItem.h @@ -13,10 +13,10 @@ namespace Swift {  	class ChatListGroupItem;  	class ChatListItem {  		public: -			ChatListItem(ChatListGroupItem* parent) {parent_ = parent;}; +			ChatListItem(ChatListGroupItem* parent) {parent_ = parent;}  			virtual ~ChatListItem() {} -			ChatListGroupItem* parent() {return parent_;}; +			ChatListGroupItem* parent() {return parent_;}  			virtual QVariant data(int role) const = 0;  		private: diff --git a/Swift/QtUI/EventViewer/EventDelegate.cpp b/Swift/QtUI/EventViewer/EventDelegate.cpp index 9ecdd34..fdb1866 100644 --- a/Swift/QtUI/EventViewer/EventDelegate.cpp +++ b/Swift/QtUI/EventViewer/EventDelegate.cpp @@ -29,7 +29,6 @@ QSize EventDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIn  	case SubscriptionEventType: return subscriptionDelegate_.sizeHint(option, item);  	case ErrorEventType: return errorDelegate_.sizeHint(option, item);  	case MUCInviteEventType: return mucInviteDelegate_.sizeHint(option, item); -	default: return QStyledItemDelegate::sizeHint(option, index);  	}  } @@ -44,7 +43,6 @@ void EventDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,  	case SubscriptionEventType: subscriptionDelegate_.paint(painter, option, item);break;  	case ErrorEventType: errorDelegate_.paint(painter, option, item);break;  	case MUCInviteEventType: mucInviteDelegate_.paint(painter, option, item);break; -	default: QStyledItemDelegate::paint(painter, option, index);  	}  } diff --git a/Swift/QtUI/EventViewer/QtEvent.h b/Swift/QtUI/EventViewer/QtEvent.h index f5e3dee..11efd60 100644 --- a/Swift/QtUI/EventViewer/QtEvent.h +++ b/Swift/QtUI/EventViewer/QtEvent.h @@ -17,7 +17,7 @@ namespace Swift {  		public:  			QtEvent(boost::shared_ptr<StanzaEvent> event, bool active);  			QVariant data(int role); -			boost::shared_ptr<StanzaEvent> getEvent() { return event_; }; +			boost::shared_ptr<StanzaEvent> getEvent() { return event_; }  			enum EventRoles {  				SenderRole = Qt::UserRole diff --git a/Swift/QtUI/FreeDesktopNotifier.h b/Swift/QtUI/FreeDesktopNotifier.h index 4ba02b4..6da7621 100644 --- a/Swift/QtUI/FreeDesktopNotifier.h +++ b/Swift/QtUI/FreeDesktopNotifier.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010 -2012 Remko Tronçon   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -15,9 +15,8 @@ namespace Swift {  			FreeDesktopNotifier(const std::string& name);  			virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback); -			virtual void purgeCallbacks() { -#warning FIXME implement. -			}; +			virtual void purgeCallbacks() {} +  		private:  			std::string applicationName;  			QtCachedImageScaler imageScaler; diff --git a/Swift/QtUI/MessageSnippet.h b/Swift/QtUI/MessageSnippet.h index c7425e9..2a0ec6e 100644 --- a/Swift/QtUI/MessageSnippet.h +++ b/Swift/QtUI/MessageSnippet.h @@ -23,7 +23,7 @@ namespace Swift {  			QString getContinuationElementID() const {  				return "insert"; -			}; +			}  		private:  			QString content_; diff --git a/Swift/QtUI/QtAffiliationEditor.cpp b/Swift/QtUI/QtAffiliationEditor.cpp index ed03c23..0896b92 100644 --- a/Swift/QtUI/QtAffiliationEditor.cpp +++ b/Swift/QtUI/QtAffiliationEditor.cpp @@ -76,4 +76,4 @@ MUCOccupant::Affiliation QtAffiliationEditor::affiliationFromIndex(int affiliati  	}  } -}
\ No newline at end of file +} diff --git a/Swift/QtUI/QtAffiliationEditor.h b/Swift/QtUI/QtAffiliationEditor.h index 913b2cc..96536eb 100644 --- a/Swift/QtUI/QtAffiliationEditor.h +++ b/Swift/QtUI/QtAffiliationEditor.h @@ -34,4 +34,4 @@ namespace Swift {  			std::map<MUCOccupant::Affiliation, std::vector<JID> > affiliations_;  			std::vector<ChangePair> changes_;  	}; -}
\ No newline at end of file +} diff --git a/Swift/QtUI/QtAvatarWidget.cpp b/Swift/QtUI/QtAvatarWidget.cpp index f0bdf3c..ae9559a 100644 --- a/Swift/QtUI/QtAvatarWidget.cpp +++ b/Swift/QtUI/QtAvatarWidget.cpp @@ -68,6 +68,9 @@ void QtAvatarWidget::setAvatar(const ByteArray& data, const std::string& type) {  }  void QtAvatarWidget::mousePressEvent(QMouseEvent* event) { +	if (!editable) { +		return; +	}  	QMenu menu;  	QAction* selectPicture = new QAction(tr("Select picture ..."), this); diff --git a/Swift/QtUI/QtAvatarWidget.h b/Swift/QtUI/QtAvatarWidget.h index 8830d82..f4ac4cf 100644 --- a/Swift/QtUI/QtAvatarWidget.h +++ b/Swift/QtUI/QtAvatarWidget.h @@ -15,6 +15,7 @@ class QLabel;  namespace Swift {  	class QtAvatarWidget : public QWidget {  			Q_OBJECT +			Q_PROPERTY(bool editable READ isEditable WRITE setEditable)  		public:  			QtAvatarWidget(QWidget* parent); @@ -28,9 +29,18 @@ namespace Swift {  				return type;  			} +			void setEditable(bool b) { +				editable = b; +			} + +			bool isEditable() const { +				return editable; +			} +  			void mousePressEvent(QMouseEvent* event);  		private: +			bool editable;  			ByteArray data;  			std::string type;  			QLabel* label; diff --git a/Swift/QtUI/QtChatTabs.cpp b/Swift/QtUI/QtChatTabs.cpp index d3a5676..7d8f16e 100644 --- a/Swift/QtUI/QtChatTabs.cpp +++ b/Swift/QtUI/QtChatTabs.cpp @@ -237,7 +237,7 @@ void QtChatTabs::handleTabTitleUpdated(QWidget* widget) {  	switch (tabbable->getWidgetAlertState()) {  	case QtTabbable::WaitingActivity : tabTextColor = QColor(217, 20, 43); break;  	case QtTabbable::ImpendingActivity : tabTextColor = QColor(27, 171, 32); break; -	default : tabTextColor = QColor(); +	case QtTabbable::NoActivity : tabTextColor = QColor(); break;  	}  	tabs_->tabBar()->setTabTextColor(index, tabTextColor); diff --git a/Swift/QtUI/QtChatTheme.h b/Swift/QtUI/QtChatTheme.h index c6b02a0..f72a48b 100644 --- a/Swift/QtUI/QtChatTheme.h +++ b/Swift/QtUI/QtChatTheme.h @@ -13,20 +13,20 @@ namespace Swift {  	class QtChatTheme {  		public:  			QtChatTheme(const QString& themePath); -			QString getHeader() const {return fileContents_[Header];}; -			QString getFooter() const {return fileContents_[Footer];}; -			QString getContent() const {return fileContents_[Content];}; -			QString getStatus() const {return fileContents_[Status];}; -			QString getTopic() const {return fileContents_[Topic];}; -			QString getFileTransferRequest() const {return fileContents_[FileTransferRequest];}; -			QString getIncomingContent() const {return fileContents_[IncomingContent];}; -			QString getIncomingNextContent() const {return fileContents_[IncomingNextContent];}; -			QString getIncomingContext() const {return fileContents_[IncomingContext];}; -			QString getIncomingNextContext() const {return fileContents_[IncomingNextContext];}; -			QString getOutgoingContent() const {return fileContents_[OutgoingContent];}; -			QString getOutgoingNextContent() const {return fileContents_[OutgoingNextContent];}; -			QString getOutgoingContext() const {return fileContents_[OutgoingContext];}; -			QString getOutgoingNextContext() const {return fileContents_[OutgoingNextContext];}; +			QString getHeader() const {return fileContents_[Header];} +			QString getFooter() const {return fileContents_[Footer];} +			QString getContent() const {return fileContents_[Content];} +			QString getStatus() const {return fileContents_[Status];} +			QString getTopic() const {return fileContents_[Topic];} +			QString getFileTransferRequest() const {return fileContents_[FileTransferRequest];} +			QString getIncomingContent() const {return fileContents_[IncomingContent];} +			QString getIncomingNextContent() const {return fileContents_[IncomingNextContent];} +			QString getIncomingContext() const {return fileContents_[IncomingContext];} +			QString getIncomingNextContext() const {return fileContents_[IncomingNextContext];} +			QString getOutgoingContent() const {return fileContents_[OutgoingContent];} +			QString getOutgoingNextContent() const {return fileContents_[OutgoingNextContent];} +			QString getOutgoingContext() const {return fileContents_[OutgoingContext];} +			QString getOutgoingNextContext() const {return fileContents_[OutgoingNextContext];}  			QString getTemplate() const {return fileContents_[Template];}  			QString getMainCSS() const {return fileContents_[MainCSS];}  			QString getBase() const; diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp index 81820a3..967ebec 100644 --- a/Swift/QtUI/QtChatView.cpp +++ b/Swift/QtUI/QtChatView.cpp @@ -365,7 +365,7 @@ void QtChatView::resetView() {  	connect(webPage_->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)), this, SLOT(handleFrameSizeChanged()), Qt::UniqueConnection);  } -QWebElement findElementWithID(QWebElement document, QString elementName, QString id) { +static QWebElement findElementWithID(QWebElement document, QString elementName, QString id) {  	QWebElementCollection elements = document.findAll(elementName);  	foreach(QWebElement element, elements) {  		if (element.attribute("id") == id) { diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 28549f8..a53ca5d 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -481,8 +481,8 @@ void QtChatWindow::updateTitleWithUnreadCount() {  	emit titleUpdated();  } -std::string QtChatWindow::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) { -	return addMessage(linkimoticonify(P2QSTRING(message)), senderName, senderIsSelf, label, avatarPath, "", time); +std::string QtChatWindow::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) { +	return addMessage(linkimoticonify(P2QSTRING(message)), senderName, senderIsSelf, label, avatarPath, "", time, highlight);  }  QString QtChatWindow::linkimoticonify(const QString& message) const { @@ -502,7 +502,21 @@ QString QtChatWindow::linkimoticonify(const QString& message) const {  	return messageHTML;  } -std::string QtChatWindow::addMessage(const QString &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time) { +QString QtChatWindow::getHighlightSpanStart(const HighlightAction& highlight) +{ +	QString color = Qt::escape(P2QSTRING(highlight.getTextColor())); +	QString background = Qt::escape(P2QSTRING(highlight.getTextBackground())); +	if (color.isEmpty()) { +		color = "black"; +	} +	if (background.isEmpty()) { +		background = "yellow"; +	} + +	return QString("<span style=\"color: %1; background: %2\">").arg(color).arg(background); +} + +std::string QtChatWindow::addMessage(const QString &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time, const HighlightAction& highlight) {  	if (isWidgetSelected()) {  		onAllMessagesRead();  	} @@ -516,7 +530,9 @@ std::string QtChatWindow::addMessage(const QString &message, const std::string &  	QString messageHTML(message);  	QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";  	QString styleSpanEnd = style == "" ? "" : "</span>"; -	htmlString += "<span class='swift_inner_message'>" + styleSpanStart + messageHTML + styleSpanEnd + "</span>" ; +	QString highlightSpanStart = highlight.highlightText() ? getHighlightSpanStart(highlight) : ""; +	QString highlightSpanEnd = highlight.highlightText() ? "</span>" : ""; +	htmlString += "<span class='swift_inner_message'>" + styleSpanStart + highlightSpanStart + messageHTML + highlightSpanEnd + styleSpanEnd + "</span>" ;  	bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf);  	if (lastLineTracker_.getShouldMoveLastLine()) { @@ -572,10 +588,11 @@ int QtChatWindow::getCount() {  	return unreadCount_;  } -std::string QtChatWindow::addAction(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) { -	return addMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time); +std::string QtChatWindow::addAction(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) { +	return addMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time, highlight);  } +// FIXME: Move this to a different file  std::string formatSize(const boost::uintmax_t bytes) {  	static const char *siPrefix[] = {"k", "M", "G", "T", "P", "E", "Z", "Y", NULL};  	int power = 0; @@ -587,11 +604,11 @@ std::string formatSize(const boost::uintmax_t bytes) {  	return str( boost::format("%.1lf %sB") % engBytes % (power > 0 ? siPrefix[power-1] : "") );  } -QString encodeButtonArgument(const QString& str) { +static QString encodeButtonArgument(const QString& str) {  	return Qt::escape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str)))));  } -QString decodeButtonArgument(const QString& str) { +static QString decodeButtonArgument(const QString& str) {  	return P2QSTRING(byteArrayToString(Base64::decode(Q2PSTRING(str))));  } @@ -769,15 +786,15 @@ void QtChatWindow::addSystemMessage(const std::string& message) {  	previousMessageKind_ = PreviousMessageWasSystem;  } -void QtChatWindow::replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { -	replaceMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", id, time, "font-style:italic "); +void QtChatWindow::replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) { +	replaceMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", id, time, "font-style:italic ", highlight);  } -void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { -	replaceMessage(linkimoticonify(P2QSTRING(message)), id, time, ""); +void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) { +	replaceMessage(linkimoticonify(P2QSTRING(message)), id, time, "", highlight);  } -void QtChatWindow::replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style) { +void QtChatWindow::replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style, const HighlightAction& highlight) {  	if (!id.empty()) {  		if (isWidgetSelected()) {  			onAllMessagesRead(); @@ -787,7 +804,9 @@ void QtChatWindow::replaceMessage(const QString& message, const std::string& id,  		QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">";  		QString styleSpanEnd = style == "" ? "" : "</span>"; -		messageHTML = styleSpanStart + messageHTML + styleSpanEnd; +		QString highlightSpanStart = highlight.highlightText() ? getHighlightSpanStart(highlight) : ""; +		QString highlightSpanEnd = highlight.highlightText() ? "</span>" : ""; +		messageHTML = styleSpanStart + highlightSpanStart + messageHTML + highlightSpanEnd + styleSpanEnd;  		messageLog_->replaceMessage(messageHTML, P2QSTRING(id), B2QDATE(time));  	} diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index 3416b42..4abd456 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -37,6 +37,9 @@ namespace Swift {  	class QtChatWindowJSBridge;  	class SettingsProvider; +	// FIXME: Move this to a different file +	std::string formatSize(const boost::uintmax_t bytes); +  	class LabelModel : public QAbstractListModel {  		Q_OBJECT  		public: @@ -85,13 +88,13 @@ namespace Swift {  		public:  			QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons);  			~QtChatWindow(); -			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); -			std::string addAction(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); +			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); +			std::string addAction(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 addSystemMessage(const std::string& message);  			void addPresenceMessage(const std::string& message);  			void addErrorMessage(const std::string& errorMessage); -			void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time); -			void replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time); +			void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight); +			void replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight);  			// File transfer related stuff  			std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes);  			void setFileTransferProgress(std::string id, const int percentageDone); @@ -189,11 +192,12 @@ namespace Swift {  			void beginCorrection();  			void cancelCorrection();  			void handleSettingChanged(const std::string& setting); -			std::string addMessage(const QString& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time); -			void replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style); +			std::string addMessage(const QString& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time, const HighlightAction& highlight); +			void replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style, const HighlightAction& highlight);  			void handleOccupantSelectionChanged(RosterItem* item);  			bool appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const;  			QString linkimoticonify(const QString& message) const; +			QString getHighlightSpanStart(const HighlightAction& highlight);  			int unreadCount_;  			bool contactIsTyping_; diff --git a/Swift/QtUI/QtColorToolButton.cpp b/Swift/QtUI/QtColorToolButton.cpp new file mode 100644 index 0000000..1d379a3 --- /dev/null +++ b/Swift/QtUI/QtColorToolButton.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <QColorDialog> +#include <QPainter> + +#include <Swift/QtUI/QtColorToolButton.h> + +namespace Swift { + +QtColorToolButton::QtColorToolButton(QWidget* parent) : +	QToolButton(parent) +{ +	connect(this, SIGNAL(clicked()), SLOT(onClicked())); +	setColorIcon(Qt::transparent); +} + +void QtColorToolButton::setColor(const QColor& color) +{ +	if (color.isValid() != color_.isValid() || (color.isValid() && color != color_)) { +		color_ = color; +		setColorIcon(color_); +		emit colorChanged(color_); +	} +} + +void QtColorToolButton::onClicked() +{ +	QColor c = QColorDialog::getColor(color_, this); +	if (c.isValid()) { +		setColor(c); +	} +} + +void QtColorToolButton::setColorIcon(const QColor& color) +{ +	QPixmap pix(iconSize()); +	pix.fill(color.isValid() ? color : Qt::transparent); +	setIcon(pix); +} + +} diff --git a/Swift/QtUI/QtColorToolButton.h b/Swift/QtUI/QtColorToolButton.h new file mode 100644 index 0000000..33d195d --- /dev/null +++ b/Swift/QtUI/QtColorToolButton.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QToolButton> + +namespace Swift { + +	class QtColorToolButton : public QToolButton { +		Q_OBJECT +		Q_PROPERTY(QColor color READ getColor WRITE setColor NOTIFY colorChanged) +		public: +			explicit QtColorToolButton(QWidget* parent = NULL); +			void setColor(const QColor& color); +			const QColor& getColor() const { return color_; } + +		signals: +			void colorChanged(const QColor&); + +		private slots: +			void onClicked(); + +		private: +			void setColorIcon(const QColor& color); +			QColor color_; +	}; + +} diff --git a/Swift/QtUI/QtFileTransferListItemModel.cpp b/Swift/QtUI/QtFileTransferListItemModel.cpp index cf1de07..71961c2 100644 --- a/Swift/QtUI/QtFileTransferListItemModel.cpp +++ b/Swift/QtUI/QtFileTransferListItemModel.cpp @@ -9,14 +9,14 @@  #include <boost/bind.hpp>  #include <boost/cstdint.hpp> +#include "QtChatWindow.h" // for formatSize +  #include <Swiften/Base/boost_bsignals.h>  #include <Swift/Controllers/FileTransfer/FileTransferController.h>  #include <Swift/Controllers/FileTransfer/FileTransferOverview.h>  namespace Swift { -extern std::string formatSize(const boost::uintmax_t bytes); -  QtFileTransferListItemModel::QtFileTransferListItemModel(QObject *parent) : QAbstractItemModel(parent), fileTransferOverview(0) {  } diff --git a/Swift/QtUI/QtHighlightEditorWidget.cpp b/Swift/QtUI/QtHighlightEditorWidget.cpp new file mode 100644 index 0000000..7ff094e --- /dev/null +++ b/Swift/QtUI/QtHighlightEditorWidget.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cassert> + +#include <Swift/QtUI/QtHighlightEditorWidget.h> +#include <Swift/QtUI/QtHighlightRulesItemModel.h> + +namespace Swift { + +QtHighlightEditorWidget::QtHighlightEditorWidget(QWidget* parent) +	: QWidget(parent) +{ +	ui_.setupUi(this); + +	itemModel_ = new QtHighlightRulesItemModel(this); +	ui_.treeView->setModel(itemModel_); +	ui_.ruleWidget->setModel(itemModel_); + +	for (int i = 0; i < QtHighlightRulesItemModel::NumberOfColumns; ++i) { +		switch (i) { +			case QtHighlightRulesItemModel::ApplyTo: +			case QtHighlightRulesItemModel::Sender: +			case QtHighlightRulesItemModel::Keyword: +			case QtHighlightRulesItemModel::Action: +				ui_.treeView->showColumn(i); +				break; +			default: +				ui_.treeView->hideColumn(i); +				break; +		} +	} + +	setHighlightManager(NULL); // setup buttons for empty rules list + +	connect(ui_.treeView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), SLOT(onCurrentRowChanged(QModelIndex))); + +	connect(ui_.newButton, SIGNAL(clicked()), SLOT(onNewButtonClicked())); +	connect(ui_.deleteButton, SIGNAL(clicked()), SLOT(onDeleteButtonClicked())); + +	connect(ui_.moveUpButton, SIGNAL(clicked()), SLOT(onMoveUpButtonClicked())); +	connect(ui_.moveDownButton, SIGNAL(clicked()), SLOT(onMoveDownButtonClicked())); + +	connect(ui_.closeButton, SIGNAL(clicked()), SLOT(close())); + +	setWindowTitle(tr("Highlight Rules")); +} + +QtHighlightEditorWidget::~QtHighlightEditorWidget() +{ +} + +void QtHighlightEditorWidget::show() +{ +	if (itemModel_->rowCount(QModelIndex())) { +		selectRow(0); +	} +	QWidget::show(); +	QWidget::activateWindow(); +} + +void QtHighlightEditorWidget::setHighlightManager(HighlightManager* highlightManager) +{ +	itemModel_->setHighlightManager(highlightManager); +	ui_.newButton->setEnabled(highlightManager != NULL); + +	ui_.ruleWidget->setEnabled(false); +	ui_.deleteButton->setEnabled(false); +	ui_.moveUpButton->setEnabled(false); +	ui_.moveDownButton->setEnabled(false); +} + +void QtHighlightEditorWidget::closeEvent(QCloseEvent* event) { +	ui_.ruleWidget->save(); +	event->accept(); +} + +void QtHighlightEditorWidget::onNewButtonClicked() +{ +	int row = getSelectedRow() + 1; +	itemModel_->insertRow(row, QModelIndex()); +	selectRow(row); +} + +void QtHighlightEditorWidget::onDeleteButtonClicked() +{ +	int row = getSelectedRow(); +	assert(row >= 0); + +	itemModel_->removeRow(row, QModelIndex()); +	if (row == itemModel_->rowCount(QModelIndex())) { +		--row; +	} +	selectRow(row); +} + +void QtHighlightEditorWidget::onMoveUpButtonClicked() +{ +	int row = getSelectedRow(); +	assert(row > 0); + +	ui_.ruleWidget->save(); +	ui_.ruleWidget->setActiveIndex(QModelIndex()); +	itemModel_->swapRows(row, row - 1); +	selectRow(row - 1); +} + +void QtHighlightEditorWidget::onMoveDownButtonClicked() +{ +	int row = getSelectedRow(); +	assert(row < itemModel_->rowCount(QModelIndex()) - 1); + +	ui_.ruleWidget->save(); +	ui_.ruleWidget->setActiveIndex(QModelIndex()); +	if (itemModel_->swapRows(row, row + 1)) { +		selectRow(row + 1); +	} +} + +void QtHighlightEditorWidget::onCurrentRowChanged(const QModelIndex& index) +{ +	ui_.ruleWidget->save(); +	ui_.ruleWidget->setActiveIndex(index); + +	ui_.ruleWidget->setEnabled(index.isValid()); + +	ui_.deleteButton->setEnabled(index.isValid()); + +	ui_.moveUpButton->setEnabled(index.isValid() && index.row() != 0); +	ui_.moveDownButton->setEnabled(index.isValid() && index.row() != itemModel_->rowCount(QModelIndex()) - 1); +} + +void QtHighlightEditorWidget::selectRow(int row) +{ +	QModelIndex index = itemModel_->index(row, 0, QModelIndex()); +	ui_.treeView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); +} + +/** Return index of selected row or -1 if none is selected */ +int QtHighlightEditorWidget::getSelectedRow() const +{ +	QModelIndexList rows = ui_.treeView->selectionModel()->selectedRows(); +	return rows.isEmpty() ? -1 : rows[0].row(); +} + +} diff --git a/Swift/QtUI/QtHighlightEditorWidget.h b/Swift/QtUI/QtHighlightEditorWidget.h new file mode 100644 index 0000000..1293c87 --- /dev/null +++ b/Swift/QtUI/QtHighlightEditorWidget.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIInterfaces/HighlightEditorWidget.h> +#include <Swift/QtUI/ui_QtHighlightEditorWidget.h> + +namespace Swift { + +	class QtHighlightRulesItemModel; + +	class QtHighlightEditorWidget : public QWidget, public HighlightEditorWidget { +		Q_OBJECT + +		public: +			QtHighlightEditorWidget(QWidget* parent = NULL); +			virtual ~QtHighlightEditorWidget(); + +			void show(); + +			void setHighlightManager(HighlightManager* highlightManager); + +		private slots: +			void onNewButtonClicked(); +			void onDeleteButtonClicked(); +			void onMoveUpButtonClicked(); +			void onMoveDownButtonClicked(); +			void onCurrentRowChanged(const QModelIndex&); + +		private: +			virtual void closeEvent(QCloseEvent* event); + +			void selectRow(int row); +			int getSelectedRow() const; + +			Ui::QtHighlightEditorWidget ui_; +			QtHighlightRulesItemModel* itemModel_; +		}; + +} diff --git a/Swift/QtUI/QtHighlightEditorWidget.ui b/Swift/QtUI/QtHighlightEditorWidget.ui new file mode 100644 index 0000000..0f39168 --- /dev/null +++ b/Swift/QtUI/QtHighlightEditorWidget.ui @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtHighlightEditorWidget</class> + <widget class="QWidget" name="QtHighlightEditorWidget"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>419</width> +    <height>373</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Form</string> +  </property> +  <layout class="QHBoxLayout" name="horizontalLayout"> +   <item> +    <layout class="QVBoxLayout" name="verticalLayout_2"> +     <item> +      <widget class="QLabel" name="label"> +       <property name="text"> +        <string>Incoming messages are checked against the following rules. First rule that matches will be executed.</string> +       </property> +       <property name="wordWrap"> +        <bool>true</bool> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QTreeView" name="treeView"> +       <property name="rootIsDecorated"> +        <bool>false</bool> +       </property> +       <property name="itemsExpandable"> +        <bool>false</bool> +       </property> +      </widget> +     </item> +    </layout> +   </item> +   <item> +    <widget class="Swift::QtHighlightRuleWidget" name="ruleWidget" native="true"/> +   </item> +   <item> +    <layout class="QVBoxLayout" name="verticalLayout"> +     <item> +      <widget class="QPushButton" name="newButton"> +       <property name="text"> +        <string>New</string> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QPushButton" name="deleteButton"> +       <property name="text"> +        <string>Delete</string> +       </property> +      </widget> +     </item> +     <item> +      <spacer name="verticalSpacer_2"> +       <property name="orientation"> +        <enum>Qt::Vertical</enum> +       </property> +       <property name="sizeType"> +        <enum>QSizePolicy::Fixed</enum> +       </property> +       <property name="sizeHint" stdset="0"> +        <size> +         <width>20</width> +         <height>20</height> +        </size> +       </property> +      </spacer> +     </item> +     <item> +      <widget class="QPushButton" name="moveUpButton"> +       <property name="text"> +        <string>Move up</string> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QPushButton" name="moveDownButton"> +       <property name="text"> +        <string>Move down</string> +       </property> +      </widget> +     </item> +     <item> +      <spacer name="verticalSpacer_3"> +       <property name="orientation"> +        <enum>Qt::Vertical</enum> +       </property> +       <property name="sizeHint" stdset="0"> +        <size> +         <width>20</width> +         <height>40</height> +        </size> +       </property> +      </spacer> +     </item> +     <item> +      <widget class="QPushButton" name="closeButton"> +       <property name="text"> +        <string>Close</string> +       </property> +      </widget> +     </item> +    </layout> +   </item> +  </layout> + </widget> + <customwidgets> +  <customwidget> +   <class>Swift::QtHighlightRuleWidget</class> +   <extends>QWidget</extends> +   <header>QtHighlightRuleWidget.h</header> +   <container>1</container> +  </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtHighlightRuleWidget.cpp b/Swift/QtUI/QtHighlightRuleWidget.cpp new file mode 100644 index 0000000..9c0df5e --- /dev/null +++ b/Swift/QtUI/QtHighlightRuleWidget.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <QDataWidgetMapper> +#include <QStringListModel> +#include <QFileDialog> + +#include <Swift/QtUI/QtHighlightRuleWidget.h> +#include <Swift/QtUI/QtHighlightRulesItemModel.h> + +namespace Swift { + +QtHighlightRuleWidget::QtHighlightRuleWidget(QWidget* parent) +	: QWidget(parent) +{ +	ui_.setupUi(this); + +	QStringList applyToItems; +	for (int i = 0; i < QtHighlightRulesItemModel::ApplyToEOL; ++i) { +		applyToItems << QtHighlightRulesItemModel::getApplyToString(i); +	} +	QStringListModel * applyToModel = new QStringListModel(applyToItems, this); +	ui_.applyTo->setModel(applyToModel); + +	connect(ui_.highlightText, SIGNAL(toggled(bool)), SLOT(onHighlightTextToggled(bool))); +	connect(ui_.customColors, SIGNAL(toggled(bool)), SLOT(onCustomColorsToggled(bool))); +	connect(ui_.playSound, SIGNAL(toggled(bool)), SLOT(onPlaySoundToggled(bool))); +	connect(ui_.customSound, SIGNAL(toggled(bool)), SLOT(onCustomSoundToggled(bool))); +	connect(ui_.soundFileButton, SIGNAL(clicked()), SLOT(onSoundFileButtonClicked())); + +	mapper_ = new QDataWidgetMapper(this); +	hasValidIndex_ = false; +	model_ = NULL; +} + +QtHighlightRuleWidget::~QtHighlightRuleWidget() +{ +} + +/** Widget does not gain ownership over the model */ +void QtHighlightRuleWidget::setModel(QtHighlightRulesItemModel* model) +{ +	model_ = model; +	mapper_->setModel(model_); +} + +void QtHighlightRuleWidget::setActiveIndex(const QModelIndex& index) +{ +	if (index.isValid()) { +		if (!hasValidIndex_) { +			mapper_->addMapping(ui_.applyTo, QtHighlightRulesItemModel::ApplyTo, "currentIndex"); +			mapper_->addMapping(ui_.senders, QtHighlightRulesItemModel::Sender, "plainText"); +			mapper_->addMapping(ui_.keywords, QtHighlightRulesItemModel::Keyword, "plainText"); +			mapper_->addMapping(ui_.nickIsKeyword, QtHighlightRulesItemModel::NickIsKeyword); +			mapper_->addMapping(ui_.matchCase, QtHighlightRulesItemModel::MatchCase); +			mapper_->addMapping(ui_.matchWholeWords, QtHighlightRulesItemModel::MatchWholeWords); +			mapper_->addMapping(ui_.highlightText, QtHighlightRulesItemModel::HighlightText); +			mapper_->addMapping(ui_.foreground, QtHighlightRulesItemModel::TextColor, "color"); +			mapper_->addMapping(ui_.background, QtHighlightRulesItemModel::TextBackground, "color"); +			mapper_->addMapping(ui_.playSound, QtHighlightRulesItemModel::PlaySound); +			mapper_->addMapping(ui_.soundFile, QtHighlightRulesItemModel::SoundFile); +		} +		mapper_->setCurrentModelIndex(index); +		ui_.customColors->setChecked(ui_.foreground->getColor().isValid() || ui_.background->getColor().isValid()); +		ui_.customSound->setChecked(!ui_.soundFile->text().isEmpty()); +		ui_.applyTo->focusWidget(); +	} else { +		if (hasValidIndex_) { +			mapper_->clearMapping(); +		} +	} + +	hasValidIndex_ = index.isValid(); +} + +void QtHighlightRuleWidget::onCustomColorsToggled(bool enabled) +{ +	if (!enabled) { +		ui_.foreground->setColor(QColor()); +		ui_.background->setColor(QColor()); +	} +	ui_.foreground->setEnabled(enabled); +	ui_.background->setEnabled(enabled); +} + +void QtHighlightRuleWidget::onCustomSoundToggled(bool enabled) +{ +	if (enabled) { +		if (ui_.soundFile->text().isEmpty()) { +			onSoundFileButtonClicked(); +		} +	} else { +		ui_.soundFile->clear(); +	} +	ui_.soundFile->setEnabled(enabled); +	ui_.soundFileButton->setEnabled(enabled); +} + +void QtHighlightRuleWidget::onSoundFileButtonClicked() +{ +	QString s = QFileDialog::getOpenFileName(this, tr("Choose sound file"), QString(), tr("Sound files (*.wav)")); +	if (!s.isEmpty()) { +		ui_.soundFile->setText(s); +	} +} + +void QtHighlightRuleWidget::onHighlightTextToggled(bool enabled) +{ +	ui_.customColors->setEnabled(enabled); +} + +void QtHighlightRuleWidget::onPlaySoundToggled(bool enabled) +{ +	ui_.customSound->setEnabled(enabled); +} + +void QtHighlightRuleWidget::save() +{ +	if (hasValidIndex_) { +		mapper_->submit(); +	} +} + +void QtHighlightRuleWidget::revert() +{ +	if (hasValidIndex_) { +		mapper_->revert(); +	} +} + +} diff --git a/Swift/QtUI/QtHighlightRuleWidget.h b/Swift/QtUI/QtHighlightRuleWidget.h new file mode 100644 index 0000000..8a59a14 --- /dev/null +++ b/Swift/QtUI/QtHighlightRuleWidget.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QWidget> +#include <QModelIndex> + +#include <Swift/QtUI/ui_QtHighlightRuleWidget.h> + +class QDataWidgetMapper; + +namespace Swift { + +	class QtHighlightRulesItemModel; + +	class QtHighlightRuleWidget : public QWidget +	{ +		Q_OBJECT + +		public: +			explicit QtHighlightRuleWidget(QWidget* parent = NULL); +			~QtHighlightRuleWidget(); + +			void setModel(QtHighlightRulesItemModel* model); + +		public slots: +			void setActiveIndex(const QModelIndex&); +			void save(); +			void revert(); + +		private slots: +			void onHighlightTextToggled(bool); +			void onCustomColorsToggled(bool); +			void onPlaySoundToggled(bool); +			void onCustomSoundToggled(bool); +			void onSoundFileButtonClicked(); + +		private: +			QDataWidgetMapper * mapper_; +			QtHighlightRulesItemModel * model_; +			bool hasValidIndex_; +			Ui::QtHighlightRuleWidget ui_; +	}; + +} diff --git a/Swift/QtUI/QtHighlightRuleWidget.ui b/Swift/QtUI/QtHighlightRuleWidget.ui new file mode 100644 index 0000000..9c465f9 --- /dev/null +++ b/Swift/QtUI/QtHighlightRuleWidget.ui @@ -0,0 +1,260 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtHighlightRuleWidget</class> + <widget class="QWidget" name="QtHighlightRuleWidget"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>361</width> +    <height>524</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Form</string> +  </property> +  <layout class="QVBoxLayout" name="verticalLayout_2"> +   <item> +    <widget class="QGroupBox" name="groupBox"> +     <property name="title"> +      <string>Rule conditions</string> +     </property> +     <layout class="QFormLayout" name="formLayout"> +      <property name="fieldGrowthPolicy"> +       <enum>QFormLayout::ExpandingFieldsGrow</enum> +      </property> +      <item row="0" column="0" colspan="2"> +       <widget class="QLabel" name="label"> +        <property name="text"> +         <string>Choose when this rule should be applied. +If you want to provide more than one sender or keyword, input them in separate lines.</string> +        </property> +        <property name="wordWrap"> +         <bool>true</bool> +        </property> +       </widget> +      </item> +      <item row="1" column="0" colspan="2"> +       <widget class="Line" name="line"> +        <property name="orientation"> +         <enum>Qt::Horizontal</enum> +        </property> +       </widget> +      </item> +      <item row="2" column="0"> +       <widget class="QLabel" name="label_2"> +        <property name="text"> +         <string>&Apply to:</string> +        </property> +        <property name="buddy"> +         <cstring>applyTo</cstring> +        </property> +       </widget> +      </item> +      <item row="2" column="1"> +       <widget class="QComboBox" name="applyTo"/> +      </item> +      <item row="3" column="0"> +       <widget class="QLabel" name="label_3"> +        <property name="text"> +         <string>&Senders:</string> +        </property> +        <property name="buddy"> +         <cstring>senders</cstring> +        </property> +       </widget> +      </item> +      <item row="3" column="1"> +       <widget class="QPlainTextEdit" name="senders"/> +      </item> +      <item row="4" column="0"> +       <widget class="QLabel" name="label_4"> +        <property name="text"> +         <string>&Keywords:</string> +        </property> +        <property name="buddy"> +         <cstring>keywords</cstring> +        </property> +       </widget> +      </item> +      <item row="4" column="1"> +       <widget class="QPlainTextEdit" name="keywords"/> +      </item> +      <item row="5" column="1"> +       <widget class="QCheckBox" name="nickIsKeyword"> +        <property name="text"> +         <string>Treat &nick as a keyword (in MUC)</string> +        </property> +       </widget> +      </item> +      <item row="6" column="1"> +       <widget class="QCheckBox" name="matchWholeWords"> +        <property name="text"> +         <string>Match whole &words</string> +        </property> +       </widget> +      </item> +      <item row="7" column="1"> +       <widget class="QCheckBox" name="matchCase"> +        <property name="text"> +         <string>Match &case</string> +        </property> +       </widget> +      </item> +     </layout> +    </widget> +   </item> +   <item> +    <widget class="QGroupBox" name="groupBox_2"> +     <property name="title"> +      <string>Actions</string> +     </property> +     <layout class="QVBoxLayout" name="verticalLayout"> +      <item> +       <widget class="QCheckBox" name="highlightText"> +        <property name="text"> +         <string>&Highlight text</string> +        </property> +       </widget> +      </item> +      <item> +       <layout class="QHBoxLayout" name="horizontalLayout"> +        <item> +         <spacer name="horizontalSpacer"> +          <property name="orientation"> +           <enum>Qt::Horizontal</enum> +          </property> +          <property name="sizeType"> +           <enum>QSizePolicy::Fixed</enum> +          </property> +          <property name="sizeHint" stdset="0"> +           <size> +            <width>28</width> +            <height>20</height> +           </size> +          </property> +         </spacer> +        </item> +        <item> +         <widget class="QCheckBox" name="customColors"> +          <property name="enabled"> +           <bool>false</bool> +          </property> +          <property name="text"> +           <string>Custom c&olors:</string> +          </property> +         </widget> +        </item> +        <item> +         <widget class="Swift::QtColorToolButton" name="foreground"> +          <property name="enabled"> +           <bool>false</bool> +          </property> +          <property name="text"> +           <string>&Foreground</string> +          </property> +          <property name="toolButtonStyle"> +           <enum>Qt::ToolButtonTextBesideIcon</enum> +          </property> +         </widget> +        </item> +        <item> +         <widget class="Swift::QtColorToolButton" name="background"> +          <property name="enabled"> +           <bool>false</bool> +          </property> +          <property name="text"> +           <string>&Background</string> +          </property> +          <property name="toolButtonStyle"> +           <enum>Qt::ToolButtonTextBesideIcon</enum> +          </property> +         </widget> +        </item> +       </layout> +      </item> +      <item> +       <widget class="QCheckBox" name="playSound"> +        <property name="text"> +         <string>&Play sound</string> +        </property> +       </widget> +      </item> +      <item> +       <layout class="QHBoxLayout" name="horizontalLayout_2"> +        <item> +         <spacer name="horizontalSpacer_2"> +          <property name="orientation"> +           <enum>Qt::Horizontal</enum> +          </property> +          <property name="sizeType"> +           <enum>QSizePolicy::Fixed</enum> +          </property> +          <property name="sizeHint" stdset="0"> +           <size> +            <width>28</width> +            <height>20</height> +           </size> +          </property> +         </spacer> +        </item> +        <item> +         <widget class="QCheckBox" name="customSound"> +          <property name="enabled"> +           <bool>false</bool> +          </property> +          <property name="text"> +           <string>Custom soun&d:</string> +          </property> +         </widget> +        </item> +        <item> +         <widget class="QLineEdit" name="soundFile"> +          <property name="enabled"> +           <bool>false</bool> +          </property> +          <property name="readOnly"> +           <bool>true</bool> +          </property> +         </widget> +        </item> +        <item> +         <widget class="QToolButton" name="soundFileButton"> +          <property name="enabled"> +           <bool>false</bool> +          </property> +          <property name="text"> +           <string>...</string> +          </property> +         </widget> +        </item> +       </layout> +      </item> +     </layout> +    </widget> +   </item> +   <item> +    <spacer name="verticalSpacer"> +     <property name="orientation"> +      <enum>Qt::Vertical</enum> +     </property> +     <property name="sizeHint" stdset="0"> +      <size> +       <width>20</width> +       <height>101</height> +      </size> +     </property> +    </spacer> +   </item> +  </layout> + </widget> + <customwidgets> +  <customwidget> +   <class>Swift::QtColorToolButton</class> +   <extends>QToolButton</extends> +   <header>QtColorToolButton.h</header> +  </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtHighlightRulesItemModel.cpp b/Swift/QtUI/QtHighlightRulesItemModel.cpp new file mode 100644 index 0000000..ff2f639 --- /dev/null +++ b/Swift/QtUI/QtHighlightRulesItemModel.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <boost/algorithm/string.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <QStringList> +#include <QColor> + +#include <Swift/Controllers/HighlightManager.h> +#include <Swift/QtUI/QtHighlightRulesItemModel.h> +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtHighlightRulesItemModel::QtHighlightRulesItemModel(QObject* parent) : QAbstractItemModel(parent), highlightManager_(NULL) +{ +} + +void QtHighlightRulesItemModel::setHighlightManager(HighlightManager* highlightManager) +{ +	emit layoutAboutToBeChanged(); +	highlightManager_ = highlightManager; +	emit layoutChanged(); +} + +QVariant QtHighlightRulesItemModel::headerData(int section, Qt::Orientation /* orientation */, int role) const +{ +	if (role == Qt::DisplayRole) { +		switch (section) { +			case ApplyTo: return QVariant(tr("Apply to")); +			case Sender: return QVariant(tr("Sender")); +			case Keyword: return QVariant(tr("Keyword")); +			case Action: return QVariant(tr("Action")); +			case NickIsKeyword: return QVariant(tr("Nick Is Keyword")); +			case MatchCase: return QVariant(tr("Match Case")); +			case MatchWholeWords: return QVariant(tr("Match Whole Words")); +			case HighlightText: return QVariant(tr("Highlight Text")); +			case TextColor: return QVariant(tr("Text Color")); +			case TextBackground: return QVariant(tr("Text Background")); +			case PlaySound: return QVariant(tr("Play Sounds")); +			case SoundFile: return QVariant(tr("Sound File")); +		} +	} + +	return QVariant(); +} + +int QtHighlightRulesItemModel::columnCount(const QModelIndex& /* parent */) const +{ +	return NumberOfColumns; +} + +QVariant QtHighlightRulesItemModel::data(const QModelIndex &index, int role) const +{ +	if (index.isValid() && highlightManager_ && (role == Qt::DisplayRole || role == Qt::EditRole)) { + +		const char* separator = (role == Qt::DisplayRole) ? " ; " : "\n"; + +		if (boost::numeric_cast<std::vector<std::string>::size_type>(index.row()) < highlightManager_->getRules().size()) { +			const HighlightRule& r = highlightManager_->getRules()[index.row()]; +			switch (index.column()) { +				case ApplyTo: { +					int applyTo = 0; +					if (r.getMatchChat() && r.getMatchMUC()) { +						applyTo = 1; +					} else if (r.getMatchChat()) { +						applyTo = 2; +					} else if (r.getMatchMUC()) { +						applyTo = 3; +					} + +					if (role == Qt::DisplayRole) { +						return QVariant(getApplyToString(applyTo)); +					} else { +						return QVariant(applyTo); +					} +				} +				case Sender: { +					std::string s = boost::join(r.getSenders(), separator); +					return QVariant(P2QSTRING(s)); +				} +				case Keyword: { +					std::string s = boost::join(r.getKeywords(), separator); +					QString qs(P2QSTRING(s)); +					if (role == Qt::DisplayRole && r.getNickIsKeyword()) { +						qs = tr("<nick>") + (qs.isEmpty() ? "" : separator + qs); +					} +					return QVariant(qs); +				} +				case Action: { +					std::vector<std::string> v; +					const HighlightAction & action = r.getAction(); +					if (action.highlightText()) { +						v.push_back(Q2PSTRING(tr("Highlight text"))); +					} +					if (action.playSound()) { +						v.push_back(Q2PSTRING(tr("Play sound"))); +					} +					std::string s = boost::join(v, separator); +					return QVariant(P2QSTRING(s)); +				} +				case NickIsKeyword: { +					return QVariant(r.getNickIsKeyword()); +				} +				case MatchCase: { +					return QVariant(r.getMatchCase()); +				} +				case MatchWholeWords: { +					return QVariant(r.getMatchWholeWords()); +				} +				case HighlightText: { +					return QVariant(r.getAction().highlightText()); +				} +				case TextColor: { +					return QVariant(QColor(P2QSTRING(r.getAction().getTextColor()))); +				} +				case TextBackground: { +					return QVariant(QColor(P2QSTRING(r.getAction().getTextBackground()))); +				} +				case PlaySound: { +					return QVariant(r.getAction().playSound()); +				} +				case SoundFile: { +					return QVariant(P2QSTRING(r.getAction().getSoundFile())); +				} +			} +		} +	} +	return QVariant(); +} + +bool QtHighlightRulesItemModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ +	if (index.isValid() && highlightManager_ && role == Qt::EditRole) { +		if (boost::numeric_cast<std::vector<std::string>::size_type>(index.row()) < highlightManager_->getRules().size()) { +			HighlightRule r = highlightManager_->getRule(index.row()); +			std::vector<int> changedColumns; +			switch (index.column()) { +				case ApplyTo: { +					bool ok = false; +					int applyTo = value.toInt(&ok); +					if (!ok) { +						return false; +					} +					r.setMatchChat(applyTo == ApplyToAll || applyTo == ApplyToChat); +					r.setMatchMUC(applyTo == ApplyToAll || applyTo == ApplyToMUC); +					break; +				} +				case Sender: +				case Keyword: { +					std::string s = Q2PSTRING(value.toString()); +					std::vector<std::string> v; +					boost::split(v, s, boost::is_any_of("\n")); +					v.erase(std::remove_if(v.begin(), v.end(), boost::lambda::_1 == ""), v.end()); +					if (index.column() == Sender) { +						r.setSenders(v); +					} else { +						r.setKeywords(v); +					} +					break; +				} +				case NickIsKeyword: { +					r.setNickIsKeyword(value.toBool()); +					changedColumns.push_back(Keyword);	// "<nick>" +					break; +				} +				case MatchCase: { +					r.setMatchCase(value.toBool()); +					break; +				} +				case MatchWholeWords: { +					r.setMatchWholeWords(value.toBool()); +					break; +				} +				case HighlightText: { +					r.getAction().setHighlightText(value.toBool()); +					changedColumns.push_back(Action); +					break; +				} +				case TextColor: { +					QColor c = value.value<QColor>(); +					r.getAction().setTextColor(c.isValid() ? Q2PSTRING(c.name()) : ""); +					break; +				} +				case TextBackground: { +					QColor c = value.value<QColor>(); +					r.getAction().setTextBackground(c.isValid() ? Q2PSTRING(c.name()) : ""); +					break; +				} +				case PlaySound: { +					r.getAction().setPlaySound(value.toBool()); +					changedColumns.push_back(Action); +					break; +				} +				case SoundFile: { +					r.getAction().setSoundFile(Q2PSTRING(value.toString())); +					break; +				} +			} + +			highlightManager_->setRule(index.row(), r); +			emit dataChanged(index, index); +			foreach (int column, changedColumns) { +				QModelIndex i = createIndex(index.row(), column, 0); +				emit dataChanged(i, i); +			} +		} +	} + +	return false; +} + +QModelIndex QtHighlightRulesItemModel::parent(const QModelIndex& /* child */) const +{ +	return QModelIndex(); +} + +int QtHighlightRulesItemModel::rowCount(const QModelIndex& /* parent */) const +{ +	return highlightManager_ ? highlightManager_->getRules().size() : 0; +} + +QModelIndex QtHighlightRulesItemModel::index(int row, int column, const QModelIndex& /* parent */) const +{ +	return createIndex(row, column, 0); +} + +bool QtHighlightRulesItemModel::insertRows(int row, int count, const QModelIndex& /* parent */) +{ +	if (highlightManager_) { +		beginInsertRows(QModelIndex(), row, row + count); +		while (count--) { +			highlightManager_->insertRule(row, HighlightRule()); +		} +		endInsertRows(); +		return true; +	} +	return false; +} + +bool QtHighlightRulesItemModel::removeRows(int row, int count, const QModelIndex& /* parent */) +{ +	if (highlightManager_) { +		beginRemoveRows(QModelIndex(), row, row + count); +		while (count--) { +			highlightManager_->removeRule(row); +		} +		endRemoveRows(); +		return true; +	} +	return false; +} + +bool QtHighlightRulesItemModel::swapRows(int row1, int row2) +{ +	if (highlightManager_) { +		assert(row1 >= 0 && row2 >= 0 && boost::numeric_cast<std::vector<std::string>::size_type>(row1) < highlightManager_->getRules().size() && boost::numeric_cast<std::vector<std::string>::size_type>(row2) < highlightManager_->getRules().size()); +		HighlightRule r = highlightManager_->getRule(row1); +		highlightManager_->setRule(row1, highlightManager_->getRule(row2)); +		highlightManager_->setRule(row2, r); +		emit dataChanged(index(row1, 0, QModelIndex()), index(row1, 0, QModelIndex())); +		emit dataChanged(index(row2, 0, QModelIndex()), index(row2, 0, QModelIndex())); +		return true; +	} +	return false; +} + +QString QtHighlightRulesItemModel::getApplyToString(int applyTo) +{ +	switch (applyTo) { +		case ApplyToNone: return tr("None"); +		case ApplyToAll: return tr("Chat or MUC"); +		case ApplyToChat: return tr("Chat"); +		case ApplyToMUC: return tr("MUC"); +		default: return ""; +	} +} + +} diff --git a/Swift/QtUI/QtHighlightRulesItemModel.h b/Swift/QtUI/QtHighlightRulesItemModel.h new file mode 100644 index 0000000..ac85628 --- /dev/null +++ b/Swift/QtUI/QtHighlightRulesItemModel.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QAbstractItemModel> + +namespace Swift { + +	class HighlightManager; + +	class QtHighlightRulesItemModel : public QAbstractItemModel { +		Q_OBJECT + +		public: +			QtHighlightRulesItemModel(QObject* parent = NULL); + +			void setHighlightManager(HighlightManager* highlightManager); + +			QVariant headerData(int section, Qt::Orientation orientation, int role) const; +			int columnCount(const QModelIndex& parent) const; +			QVariant data(const QModelIndex& index, int role) const; +			bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); +			QModelIndex parent(const QModelIndex& child) const; +			int rowCount(const QModelIndex& parent) const; +			QModelIndex index(int row, int column, const QModelIndex& parent) const; +			bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()); +			bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()); +			bool swapRows(int row1, int row2); + +			static QString getApplyToString(int); + +			enum Columns { +				ApplyTo = 0, +				Sender, +				Keyword, +				Action, +				NickIsKeyword, +				MatchCase, +				MatchWholeWords, +				HighlightText, +				TextColor, +				TextBackground, +				PlaySound, +				SoundFile, +				NumberOfColumns // end of list marker +			}; + +			enum ApplyToValues { +				ApplyToNone = 0, +				ApplyToAll, +				ApplyToChat, +				ApplyToMUC, +				ApplyToEOL	// end of list marker +			}; + +		private: +			HighlightManager* highlightManager_; +	}; + +} diff --git a/Swift/QtUI/QtHistoryWindow.cpp b/Swift/QtUI/QtHistoryWindow.cpp index e54bd51..875235d 100644 --- a/Swift/QtUI/QtHistoryWindow.cpp +++ b/Swift/QtUI/QtHistoryWindow.cpp @@ -23,6 +23,7 @@  #include <QLineEdit>  #include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp>  #include <boost/date_time/gregorian/gregorian.hpp>  namespace Swift { @@ -183,7 +184,7 @@ void QtHistoryWindow::handleScrollReachedTop() {  	int year, month, day;  	QDate firstDate = *dates_.begin();  	firstDate.getDate(&year, &month, &day); -	onScrollReachedTop(boost::gregorian::date(year, month, day)); +	onScrollReachedTop(boost::gregorian::date(boost::numeric_cast<unsigned short>(year), boost::numeric_cast<unsigned short>(month), boost::numeric_cast<unsigned short>(day)));  }  void QtHistoryWindow::handleScrollReachedBottom() { @@ -194,7 +195,7 @@ void QtHistoryWindow::handleScrollReachedBottom() {  	int year, month, day;  	QDate lastDate = *dates_.rbegin();  	lastDate.getDate(&year, &month, &day); -	onScrollReachedBottom(boost::gregorian::date(year, month, day)); +	onScrollReachedBottom(boost::gregorian::date(boost::numeric_cast<unsigned short>(year), boost::numeric_cast<unsigned short>(month), boost::numeric_cast<unsigned short>(day)));  }  void QtHistoryWindow::handleReturnPressed() { @@ -205,7 +206,7 @@ void QtHistoryWindow::handleCalendarClicked(const QDate& date) {  	int year, month, day;  	QDate tempDate = date; // getDate discards const qualifier  	tempDate.getDate(&year, &month, &day); -	onCalendarClicked(boost::gregorian::date(year, month, day)); +	onCalendarClicked(boost::gregorian::date(boost::numeric_cast<unsigned short>(year), boost::numeric_cast<unsigned short>(month), boost::numeric_cast<unsigned short>(day)));  }  void QtHistoryWindow::setDate(const boost::gregorian::date& date) { @@ -242,7 +243,7 @@ boost::gregorian::date QtHistoryWindow::getLastVisibleDate() {  		int year, month, day;  		lastDate.getDate(&year, &month, &day); -		return boost::gregorian::date(year, month, day); +		return boost::gregorian::date(boost::numeric_cast<unsigned short>(year), boost::numeric_cast<unsigned short>(month), boost::numeric_cast<unsigned short>(day));  	}  	return boost::gregorian::date(boost::gregorian::not_a_date_time);  } diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp index c27edfb..cf22ad0 100644 --- a/Swift/QtUI/QtLoginWindow.cpp +++ b/Swift/QtUI/QtLoginWindow.cpp @@ -30,6 +30,7 @@  #include <Swift/Controllers/UIEvents/UIEventStream.h>  #include <Swift/Controllers/UIEvents/RequestXMLConsoleUIEvent.h>  #include <Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestHighlightEditorUIEvent.h>  #include <Swift/Controllers/Settings/SettingsProvider.h>  #include <Swift/Controllers/SettingConstants.h>  #include <Swift/QtUI/QtUISettingConstants.h> @@ -190,6 +191,10 @@ QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream, SettingsProvider* set  	generalMenu_->addAction(fileTransferOverviewAction_);  #endif +	highlightEditorAction_ = new QAction(tr("&Edit Highlight Rules"), this); +	connect(highlightEditorAction_, SIGNAL(triggered()), SLOT(handleShowHighlightEditor())); +	generalMenu_->addAction(highlightEditorAction_); +  	toggleSoundsAction_ = new QAction(tr("&Play Sounds"), this);  	toggleSoundsAction_->setCheckable(true);  	toggleSoundsAction_->setChecked(settings_->getSetting(SettingConstants::PLAY_SOUNDS)); @@ -438,6 +443,10 @@ void QtLoginWindow::handleShowFileTransferOverview() {  	uiEventStream_->send(boost::make_shared<RequestFileTransferListUIEvent>());  } +void QtLoginWindow::handleShowHighlightEditor() { +	uiEventStream_->send(boost::make_shared<RequestHighlightEditorUIEvent>()); +} +  void QtLoginWindow::handleToggleSounds(bool enabled) {  	settings_->storeSetting(SettingConstants::PLAY_SOUNDS, enabled);  } diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h index c1966d8..7415fbf 100644 --- a/Swift/QtUI/QtLoginWindow.h +++ b/Swift/QtUI/QtLoginWindow.h @@ -62,6 +62,7 @@ namespace Swift {  			void handleQuit();  			void handleShowXMLConsole();  			void handleShowFileTransferOverview(); +			void handleShowHighlightEditor();  			void handleToggleSounds(bool enabled);  			void handleToggleNotifications(bool enabled);  			void handleAbout(); @@ -103,6 +104,7 @@ namespace Swift {  			SettingsProvider* settings_;  			QAction* xmlConsoleAction_;  			QAction* fileTransferOverviewAction_; +			QAction* highlightEditorAction_;  			TimerFactory* timerFactory_;  			ClientOptions currentOptions_;  	}; diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index 5d50c1e..9749397 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -47,14 +47,14 @@  namespace Swift { -QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStream, QtLoginWindow::QtMenus loginMenus, bool emoticonsExist) : QWidget(), MainWindow(false), loginMenus_(loginMenus) { +QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStream, QtLoginWindow::QtMenus loginMenus, StatusCache* statusCache, bool emoticonsExist) : QWidget(), MainWindow(false), loginMenus_(loginMenus) {  	uiEventStream_ = uiEventStream;  	settings_ = settings;  	setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));  	QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this);  	mainLayout->setContentsMargins(0,0,0,0);  	mainLayout->setSpacing(0); -	meView_ = new QtRosterHeader(settings, this); +	meView_ = new QtRosterHeader(settings, statusCache, this);  	mainLayout->addWidget(meView_);  	connect(meView_, SIGNAL(onChangeStatusRequest(StatusShow::Type, const QString&)), this, SLOT(handleStatusChanged(StatusShow::Type, const QString&)));  	connect(meView_, SIGNAL(onEditProfileRequest()), this, SLOT(handleEditProfileRequest())); @@ -95,6 +95,14 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr  	QMenu* viewMenu = new QMenu(tr("&View"), this);  	menus_.push_back(viewMenu); + +	compactRosterAction_ = new QAction(tr("&Compact Roster"), this); +	compactRosterAction_->setCheckable(true); +	compactRosterAction_->setChecked(false); +	connect(compactRosterAction_, SIGNAL(toggled(bool)), SLOT(handleCompactRosterToggled(bool))); +	viewMenu->addAction(compactRosterAction_); +	handleCompactRosterToggled(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER)); +  	showOfflineAction_ = new QAction(tr("&Show offline contacts"), this);  	showOfflineAction_->setCheckable(true);  	showOfflineAction_->setChecked(false); @@ -261,6 +269,14 @@ void QtMainWindow::handleSettingChanged(const std::string& settingPath) {  	if (settingPath == SettingConstants::REQUEST_DELIVERYRECEIPTS.getKey()) {  		toggleRequestDeliveryReceipts_->setChecked(settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS));  	} +	if (settingPath == QtUISettingConstants::COMPACT_ROSTER.getKey()) { +		handleCompactRosterToggled(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER)); +	} +} + +void QtMainWindow::handleCompactRosterToggled(bool state) { +	settings_->storeSetting(QtUISettingConstants::COMPACT_ROSTER, state); +	compactRosterAction_->setChecked(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER));  }  void QtMainWindow::handleShowOfflineToggled(bool state) { diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h index 26d25e1..99bc675 100644 --- a/Swift/QtUI/QtMainWindow.h +++ b/Swift/QtUI/QtMainWindow.h @@ -32,11 +32,12 @@ namespace Swift {  	class QtTabWidget;  	class SettingsProvider;  	class QtUIPreferences; +	class StatusCache;  	class QtMainWindow : public QWidget, public MainWindow {  		Q_OBJECT  		public: -			QtMainWindow(SettingsProvider*, UIEventStream* eventStream, QtLoginWindow::QtMenus loginMenus, bool emoticonsExist); +			QtMainWindow(SettingsProvider*, UIEventStream* eventStream, QtLoginWindow::QtMenus loginMenus, StatusCache* statusCache, bool emoticonsExist);  			virtual ~QtMainWindow();  			std::vector<QMenu*> getMenus() {return menus_;}  			void setMyNick(const std::string& name); @@ -55,6 +56,7 @@ namespace Swift {  		private slots:  			void handleStatusChanged(StatusShow::Type showType, const QString &statusMessage);  			void handleSettingChanged(const std::string& settingPath); +			void handleCompactRosterToggled(bool);  			void handleShowOfflineToggled(bool);  			void handleShowEmoticonsToggled(bool);  			void handleJoinMUCAction(); @@ -81,6 +83,7 @@ namespace Swift {  			QAction* editUserAction_;  			QAction* chatUserAction_;  			QAction* showOfflineAction_; +			QAction* compactRosterAction_;  			QAction* showEmoticonsAction_;  			QAction* toggleRequestDeliveryReceipts_;  			QMenu* serverAdHocMenu_; diff --git a/Swift/QtUI/QtProfileWindow.cpp b/Swift/QtUI/QtProfileWindow.cpp index 0faa78f..ccc6ae9 100644 --- a/Swift/QtUI/QtProfileWindow.cpp +++ b/Swift/QtUI/QtProfileWindow.cpp @@ -4,97 +4,80 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +  #include "QtProfileWindow.h" +#include "ui_QtProfileWindow.h" -#include <QImage> -#include <QPixmap> -#include <QSizePolicy> -#include <QGridLayout> -#include <QLabel> -#include <QLineEdit> -#include <QPushButton> +#include <QCloseEvent>  #include <QMovie> +#include <QShortcut> +#include <QTextDocument> -#include "QtSwiftUtil.h" -#include "QtAvatarWidget.h" +#include <Swift/QtUI/QtSwiftUtil.h>  namespace Swift { -QtProfileWindow::QtProfileWindow() { -	setWindowTitle(tr("Edit Profile")); - -	QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); -	sizePolicy.setHorizontalStretch(0); -	sizePolicy.setVerticalStretch(0); -	sizePolicy.setHeightForWidth(this->sizePolicy().hasHeightForWidth()); -	setSizePolicy(sizePolicy); - -	QVBoxLayout* layout = new QVBoxLayout(this); -	layout->setContentsMargins(10, 10, 10, 10); - -	QHBoxLayout* topLayout = new QHBoxLayout(); - -	avatar = new QtAvatarWidget(this); -	topLayout->addWidget(avatar); - -	QVBoxLayout* fieldsLayout = new QVBoxLayout(); - -	QHBoxLayout* horizontalLayout_2 = new QHBoxLayout(); -	nicknameLabel = new QLabel(tr("Nickname:"), this); -	horizontalLayout_2->addWidget(nicknameLabel); -	nickname = new QLineEdit(this); -	horizontalLayout_2->addWidget(nickname); - -	fieldsLayout->addLayout(horizontalLayout_2); - -	errorLabel = new QLabel(this); -	errorLabel->setAlignment(Qt::AlignHCenter); -	fieldsLayout->addWidget(errorLabel); - -	fieldsLayout->addItem(new QSpacerItem(198, 17, QSizePolicy::Minimum, QSizePolicy::Expanding)); -	topLayout->addLayout(fieldsLayout); - -	layout->addLayout(topLayout); - -	QHBoxLayout* horizontalLayout = new QHBoxLayout(); -	horizontalLayout->setContentsMargins(0, 0, 0, 0); -	horizontalLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); - -	throbberLabel = new QLabel(this); -	throbberLabel->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this)); -	horizontalLayout->addWidget(throbberLabel); - -	saveButton = new QPushButton(tr("Save"), this); -	saveButton->setDefault( true ); -	connect(saveButton, SIGNAL(clicked()), SLOT(handleSave())); -	horizontalLayout->addWidget(saveButton); +QtProfileWindow::QtProfileWindow(QWidget* parent) : +	QWidget(parent), +	ui(new Ui::QtProfileWindow) { +	ui->setupUi(this); +	new QShortcut(QKeySequence::Close, this, SLOT(close())); +	ui->throbberLabel->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this)); +	connect(ui->savePushButton, SIGNAL(clicked()), SLOT(handleSave())); +	setEditable(false); +} -	fieldsLayout->addLayout(horizontalLayout); +QtProfileWindow::~QtProfileWindow() { +	delete ui; +} -	resize(360, 120); +void QtProfileWindow::setJID(const JID& jid) { +	this->jid = jid; +	updateTitle();  } -void QtProfileWindow::setVCard(Swift::VCard::ref vcard) { -	this->vcard = vcard; -	nickname->setText(P2QSTRING(vcard->getNickname())); -	avatar->setAvatar(vcard->getPhoto(), vcard->getPhotoType()); +void QtProfileWindow::setVCard(VCard::ref vcard) { +	ui->vcard->setVCard(vcard);  }  void QtProfileWindow::setEnabled(bool b) { -	nickname->setEnabled(b); -	nicknameLabel->setEnabled(b); -	avatar->setEnabled(b); -	saveButton->setEnabled(b); +	ui->vcard->setEnabled(b); +	ui->savePushButton->setEnabled(b); +} + +void QtProfileWindow::setEditable(bool b) { +	if (b) { +		ui->savePushButton->show(); +		ui->vcard->setEditable(true); +	} else { +		ui->savePushButton->hide(); +		ui->vcard->setEditable(false); +	} +	updateTitle();  }  void QtProfileWindow::setProcessing(bool processing) {  	if (processing) { -		throbberLabel->movie()->start(); -		throbberLabel->show(); +		ui->throbberLabel->movie()->start(); +		ui->throbberLabel->show(); +	} +	else { +		ui->throbberLabel->hide(); +		ui->throbberLabel->movie()->stop(); +	} +} + +void QtProfileWindow::setError(const std::string& error) { +	if (!error.empty()) { +		ui->errorLabel->setText("<font color='red'>" + Qt::escape(P2QSTRING(error)) + "</font>");  	}  	else { -		throbberLabel->hide(); -		throbberLabel->movie()->stop(); +		ui->errorLabel->setText("");  	}  } @@ -103,31 +86,30 @@ void QtProfileWindow::show() {  	QWidget::activateWindow();  } -void QtProfileWindow::hideEvent(QHideEvent* event) { -	QWidget::hideEvent(event); -} -  void QtProfileWindow::hide() {  	QWidget::hide();  } -void QtProfileWindow::handleSave() { -	assert(vcard); -	vcard->setNickname(Q2PSTRING(nickname->text())); -	vcard->setPhoto(avatar->getAvatarData()); -	vcard->setPhotoType(avatar->getAvatarType()); -	onVCardChangeRequest(vcard); -} - -void QtProfileWindow::setError(const std::string& error) { -	if (!error.empty()) { -		errorLabel->setText("<font color='red'>" + P2QSTRING(error) + "</font>"); +void QtProfileWindow::updateTitle() { +	QString jidString; +	if (jid.isValid()) { +		jidString = QString(" ( %1 )").arg(P2QSTRING(jid.toString()));  	} -	else { -		errorLabel->setText(""); + +	if (ui->vcard->isEditable()) { +		setWindowTitle(tr("Edit Profile") + jidString); +	} else { +		setWindowTitle(tr("Show Profile") + jidString);  	}  } +void QtProfileWindow::closeEvent(QCloseEvent* event) { +	onWindowClosed(jid); +	event->accept(); +} +void QtProfileWindow::handleSave() { +	onVCardChangeRequest(ui->vcard->getVCard()); +}  } diff --git a/Swift/QtUI/QtProfileWindow.h b/Swift/QtUI/QtProfileWindow.h index edb9cce..1dbc0fb 100644 --- a/Swift/QtUI/QtProfileWindow.h +++ b/Swift/QtUI/QtProfileWindow.h @@ -4,45 +4,54 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ +  #pragma once -#include <QWidget> +#include <Swiften/JID/JID.h>  #include <Swift/Controllers/UIInterfaces/ProfileWindow.h> -class QLabel; -class QLineEdit; -class QHBoxLayout; -class QPushButton; +#include <QWidget> + +namespace Ui { +	class QtProfileWindow; +}  namespace Swift { -	class QtAvatarWidget; - -	class QtProfileWindow : public QWidget, public ProfileWindow { -			Q_OBJECT -		public: -			QtProfileWindow(); - -			void setVCard(Swift::VCard::ref); -			void setEnabled(bool); -			void setProcessing(bool); -			virtual void setError(const std::string&); -			void show(); -			void hide(); - -			void hideEvent (QHideEvent* event); - -		private slots: -			void handleSave(); - -		private: -			VCard::ref vcard; -	    QtAvatarWidget* avatar; -	    QLabel* nicknameLabel; -	    QLineEdit* nickname; -	    QLabel* throbberLabel; -	    QLabel* errorLabel; -	    QHBoxLayout* horizontalLayout; -	    QPushButton* saveButton; -	}; + +class QtProfileWindow : public QWidget, public ProfileWindow { +	Q_OBJECT + +	public: +		explicit QtProfileWindow(QWidget* parent = 0); +		virtual ~QtProfileWindow(); + +		virtual void setJID(const JID& jid); +		virtual void setVCard(VCard::ref vcard); + +		virtual void setEnabled(bool b); +		virtual void setProcessing(bool processing); +		virtual void setError(const std::string& error); +		virtual void setEditable(bool b); + +		virtual void show(); +		virtual void hide(); + +	private: +		void updateTitle(); +		virtual void closeEvent(QCloseEvent* event); + +	private slots: +		void handleSave(); + +	private: +		Ui::QtProfileWindow* ui; +		JID jid; +}; +  } diff --git a/Swift/QtUI/QtProfileWindow.ui b/Swift/QtUI/QtProfileWindow.ui new file mode 100644 index 0000000..68a36da --- /dev/null +++ b/Swift/QtUI/QtProfileWindow.ui @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtProfileWindow</class> + <widget class="QWidget" name="QtProfileWindow"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>334</width> +    <height>197</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Edit Profile</string> +  </property> +  <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0"> +   <property name="margin"> +    <number>0</number> +   </property> +   <item> +    <widget class="Swift::QtVCardWidget" name="vcard" native="true"/> +   </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> + <customwidgets> +  <customwidget> +   <class>Swift::QtVCardWidget</class> +   <extends>QWidget</extends> +   <header>Swift/QtUI/QtVCardWidget/QtVCardWidget.h</header> +   <container>1</container> +  </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtRosterHeader.cpp b/Swift/QtUI/QtRosterHeader.cpp index d32a12e..44459d5 100644 --- a/Swift/QtUI/QtRosterHeader.cpp +++ b/Swift/QtUI/QtRosterHeader.cpp @@ -23,7 +23,7 @@  #include "QtScaledAvatarCache.h"  namespace Swift { -QtRosterHeader::QtRosterHeader(SettingsProvider* settings, QWidget* parent) : QWidget(parent) { +QtRosterHeader::QtRosterHeader(SettingsProvider* settings, StatusCache* statusCache, QWidget* parent) : QWidget(parent) {  	QHBoxLayout* topLayout = new QHBoxLayout();  	topLayout->setSpacing(3);  	topLayout->setContentsMargins(4,4,4,4); @@ -62,7 +62,7 @@ QtRosterHeader::QtRosterHeader(SettingsProvider* settings, QWidget* parent) : QW  	rightLayout->addLayout(nameAndSecurityLayout); -	statusWidget_ = new QtStatusWidget(this); +	statusWidget_ = new QtStatusWidget(statusCache, this);  	connect(statusWidget_, SIGNAL(onChangeStatusRequest(StatusShow::Type, const QString&)), this, SLOT(handleChangeStatusRequest(StatusShow::Type, const QString&)));  	rightLayout->addWidget(statusWidget_); diff --git a/Swift/QtUI/QtRosterHeader.h b/Swift/QtUI/QtRosterHeader.h index 9527cf4..ad19178 100644 --- a/Swift/QtUI/QtRosterHeader.h +++ b/Swift/QtUI/QtRosterHeader.h @@ -24,11 +24,12 @@ namespace Swift {  	class QtStatusWidget;  	class QtNameWidget;  	class SettingsProvider; +	class StatusCache;  	class QtRosterHeader : public QWidget {  		Q_OBJECT  	public: -		QtRosterHeader(SettingsProvider* settings, QWidget* parent = NULL); +		QtRosterHeader(SettingsProvider* settings, StatusCache* statusCache, QWidget* parent = NULL);  		void setAvatar(const QString& path);  		void setJID(const QString& jid); diff --git a/Swift/QtUI/QtSingleWindow.cpp b/Swift/QtUI/QtSingleWindow.cpp new file mode 100644 index 0000000..733571f --- /dev/null +++ b/Swift/QtUI/QtSingleWindow.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2010-2012 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/QtUI/QtSingleWindow.h> + +#include <Swift/QtUI/QtSettingsProvider.h> +#include <Swiften/Base/foreach.h> + +namespace Swift { + +static const QString SINGLE_WINDOW_GEOMETRY = QString("SINGLE_WINDOW_GEOMETRY"); +static const QString SINGLE_WINDOW_SPLITS = QString("SINGLE_WINDOW_SPLITS"); + +QtSingleWindow::QtSingleWindow(QtSettingsProvider* settings) : QSplitter() { +	settings_ = settings; +	QVariant geometryVariant = settings_->getQSettings()->value(SINGLE_WINDOW_GEOMETRY); +	if (geometryVariant.isValid()) { +		restoreGeometry(geometryVariant.toByteArray()); +	} +	connect(this, SIGNAL(splitterMoved(int, int)), this, SLOT(handleSplitterMoved(int, int))); +	restoreSplitters(); +} + +QtSingleWindow::~QtSingleWindow() { + +} + +void QtSingleWindow::handleSplitterMoved(int, int) { +	QList<QVariant> variantValues; +	QList<int> intValues = sizes(); +	foreach (int value, intValues) { +		variantValues.append(QVariant(value)); +	} +	settings_->getQSettings()->setValue(SINGLE_WINDOW_SPLITS, QVariant(variantValues)); +} + +void QtSingleWindow::restoreSplitters() { +	QList<QVariant> variantValues = settings_->getQSettings()->value(SINGLE_WINDOW_SPLITS).toList(); +	QList<int> intValues; +	foreach (QVariant value, variantValues) { +		intValues.append(value.toInt()); +	} +	setSizes(intValues); +} + +void QtSingleWindow::insertAtFront(QWidget* widget) { +	insertWidget(0, widget); +	restoreSplitters(); +} + +void QtSingleWindow::handleGeometryChanged() { +	settings_->getQSettings()->setValue(SINGLE_WINDOW_GEOMETRY, saveGeometry()); + +} + +void QtSingleWindow::resizeEvent(QResizeEvent*) { +	handleGeometryChanged(); +} + +void QtSingleWindow::moveEvent(QMoveEvent*) { +	handleGeometryChanged(); +} + +} diff --git a/Swift/QtUI/QtSingleWindow.h b/Swift/QtUI/QtSingleWindow.h new file mode 100644 index 0000000..6cbc3e4 --- /dev/null +++ b/Swift/QtUI/QtSingleWindow.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2010-2012 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QSplitter> + +namespace Swift { +	class QtSettingsProvider; + +	class QtSingleWindow : public QSplitter { +		Q_OBJECT +		public: +			QtSingleWindow(QtSettingsProvider* settings); +			virtual ~QtSingleWindow(); +			void insertAtFront(QWidget* widget); +		protected: +			void resizeEvent(QResizeEvent*); +			void moveEvent(QMoveEvent*); +		private slots: +			void handleSplitterMoved(int, int); +		private: +			void handleGeometryChanged(); +			void restoreSplitters(); + +		private: + +			QtSettingsProvider* settings_; +	}; + +} + diff --git a/Swift/QtUI/QtSoundPlayer.cpp b/Swift/QtUI/QtSoundPlayer.cpp index 387c6f3..63f76f0 100644 --- a/Swift/QtUI/QtSoundPlayer.cpp +++ b/Swift/QtUI/QtSoundPlayer.cpp @@ -16,10 +16,11 @@ namespace Swift {  QtSoundPlayer::QtSoundPlayer(ApplicationPathProvider* applicationPathProvider) : applicationPathProvider(applicationPathProvider) {  } -void QtSoundPlayer::playSound(SoundEffect sound) { +void QtSoundPlayer::playSound(SoundEffect sound, const std::string& soundResource) { +  	switch (sound) {  		case MessageReceived: -			playSound("/sounds/message-received.wav"); +			playSound(soundResource.empty() ? "/sounds/message-received.wav" : soundResource);  			break;  	}  } @@ -29,6 +30,9 @@ void QtSoundPlayer::playSound(const std::string& soundResource) {  	if (boost::filesystem::exists(resourcePath)) {  		QSound::play(resourcePath.string().c_str());  	} +	else if (boost::filesystem::exists(soundResource)) { +		QSound::play(soundResource.c_str()); +	}  	else {  		std::cerr << "Unable to find sound: " << soundResource << std::endl;  	} diff --git a/Swift/QtUI/QtSoundPlayer.h b/Swift/QtUI/QtSoundPlayer.h index 6945f45..f8da392 100644 --- a/Swift/QtUI/QtSoundPlayer.h +++ b/Swift/QtUI/QtSoundPlayer.h @@ -19,7 +19,7 @@ namespace Swift {  		public:  			QtSoundPlayer(ApplicationPathProvider* applicationPathProvider); -			void playSound(SoundEffect sound); +			void playSound(SoundEffect sound, const std::string& soundResource);  		private:  			void playSound(const std::string& soundResource); diff --git a/Swift/QtUI/QtStatusWidget.cpp b/Swift/QtUI/QtStatusWidget.cpp index 0e2731a..8cc366a 100644 --- a/Swift/QtUI/QtStatusWidget.cpp +++ b/Swift/QtUI/QtStatusWidget.cpp @@ -6,6 +6,10 @@  #include "QtStatusWidget.h" +#include <algorithm> +#include <boost/lambda/lambda.hpp> +#include <boost/lambda/bind.hpp> +  #include <QBoxLayout>  #include <QComboBox>  #include <QLabel> @@ -23,10 +27,20 @@  #include "Swift/QtUI/QtLineEdit.h"  #include "Swift/QtUI/QtSwiftUtil.h"  #include <Swift/Controllers/StatusUtil.h> +#include <Swift/Controllers/StatusCache.h> + +namespace lambda = boost::lambda;  namespace Swift { -QtStatusWidget::QtStatusWidget(QWidget *parent) : QWidget(parent), editCursor_(Qt::IBeamCursor), viewCursor_(Qt::PointingHandCursor) { +QtStatusWidget::QtStatusWidget(StatusCache* statusCache, QWidget *parent) : QWidget(parent), statusCache_(statusCache), editCursor_(Qt::IBeamCursor), viewCursor_(Qt::PointingHandCursor) { +	allTypes_.push_back(StatusShow::Online); +	allTypes_.push_back(StatusShow::FFC); +	allTypes_.push_back(StatusShow::Away); +	allTypes_.push_back(StatusShow::XA); +	allTypes_.push_back(StatusShow::DND); +	allTypes_.push_back(StatusShow::None); +	  	isClicking_ = false;  	connecting_ = false;  	setMaximumHeight(24); @@ -134,15 +148,42 @@ void QtStatusWidget::generateList() {  		item->setStatusTip(item->toolTip());  		item->setData(Qt::UserRole, QVariant(type));  	} +	std::vector<StatusCache::PreviousStatus> previousStatuses = statusCache_->getMatches(Q2PSTRING(text), 8); +	foreach (StatusCache::PreviousStatus savedStatus, previousStatuses) { +		if (savedStatus.first.empty() || std::find_if(allTypes_.begin(), allTypes_.end(),  +					savedStatus.second == lambda::_1 && savedStatus.first == lambda::bind(&statusShowTypeToFriendlyName, lambda::_1)) != allTypes_.end()) { +			continue; +		} +		QListWidgetItem* item = new QListWidgetItem(P2QSTRING(savedStatus.first), menu_); +		item->setIcon(icons_[savedStatus.second]); +		item->setToolTip(item->text()); +		item->setStatusTip(item->toolTip()); +		item->setData(Qt::UserRole, QVariant(savedStatus.second)); +	}  	foreach (StatusShow::Type type, icons_.keys()) { +		if (Q2PSTRING(text) == statusShowTypeToFriendlyName(type)) { +			continue; +		}  		QListWidgetItem* item = new QListWidgetItem(P2QSTRING(statusShowTypeToFriendlyName(type)), menu_);  		item->setIcon(icons_[type]);  		item->setToolTip(item->text());  		item->setStatusTip(item->toolTip());  		item->setData(Qt::UserRole, QVariant(type));  	} +	resizeMenu();  } +void QtStatusWidget::resizeMenu() { +	int height = menu_->sizeHintForRow(0) * menu_->count(); +	int marginLeft; +	int marginTop; +	int marginRight; +	int marginBottom; +	menu_->getContentsMargins(&marginLeft, &marginTop, &marginRight, &marginBottom); +	height += marginTop + marginBottom; + +	menu_->setGeometry(menu_->x(), menu_->y(), menu_->width(), height); +}  void QtStatusWidget::handleClicked() {  	editing_ = true; @@ -159,18 +200,11 @@ void QtStatusWidget::handleClicked() {  	if (x + width > screenWidth) {  		x = screenWidth - width;  	} -	std::vector<StatusShow::Type> types; -	types.push_back(StatusShow::Online); -	types.push_back(StatusShow::FFC); -	types.push_back(StatusShow::Away); -	types.push_back(StatusShow::XA); -	types.push_back(StatusShow::DND); -	types.push_back(StatusShow::None); -	foreach (StatusShow::Type type, types) { -		if (statusEdit_->text() == P2QSTRING(statusShowTypeToFriendlyName(type))) { +	//foreach (StatusShow::Type type, allTypes_) { +	//	if (statusEdit_->text() == P2QSTRING(statusShowTypeToFriendlyName(type))) {  			statusEdit_->setText(""); -		} -	} +	//	} +	//}  	generateList();  	height = menu_->sizeHintForRow(0) * menu_->count(); @@ -197,7 +231,7 @@ void QtStatusWidget::viewMode() {  	disconnect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(handleApplicationFocusChanged(QWidget*, QWidget*)));  	editing_ = false;  	menu_->hide(); -	stack_->setCurrentIndex(0);	 +	stack_->setCurrentIndex(0);  }  void QtStatusWidget::handleEditComplete() { @@ -205,6 +239,7 @@ void QtStatusWidget::handleEditComplete() {  	statusText_ = newStatusText_;  	viewMode();  	emit onChangeStatusRequest(selectedStatusType_, statusText_); +	statusCache_->addRecent(Q2PSTRING(statusText_), selectedStatusType_);  }  void QtStatusWidget::handleEditCancelled() { diff --git a/Swift/QtUI/QtStatusWidget.h b/Swift/QtUI/QtStatusWidget.h index 75bcf52..87e8d4a 100644 --- a/Swift/QtUI/QtStatusWidget.h +++ b/Swift/QtUI/QtStatusWidget.h @@ -22,10 +22,12 @@ class QMovie;  namespace Swift {  	class QtLineEdit;  	class QtElidingLabel; +	class StatusCache; +  	class QtStatusWidget : public QWidget {  		Q_OBJECT  		public: -			QtStatusWidget(QWidget *parent); +			QtStatusWidget(StatusCache* statusCache, QWidget *parent);  			~QtStatusWidget();  			StatusShow::Type getSelectedStatusShow();  			void setStatusType(StatusShow::Type type); @@ -45,9 +47,11 @@ namespace Swift {  			void handleItemClicked(QListWidgetItem* item);  			static QString getNoMessage();  		private: +			void resizeMenu();  			void viewMode();  			void setNewToolTip();  			//QComboBox *types_; +			StatusCache* statusCache_;  			QStackedWidget* stack_;  			QLabel* statusIcon_;  			QtElidingLabel* statusTextLabel_; @@ -64,6 +68,7 @@ namespace Swift {  			QMovie* connectingMovie_;  			bool connecting_;  			static const QString NO_MESSAGE; +			std::vector<StatusShow::Type> allTypes_;  	};  } diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp index 223f3ae..4d10956 100644 --- a/Swift/QtUI/QtSwift.cpp +++ b/Swift/QtUI/QtSwift.cpp @@ -7,7 +7,6 @@  #include "QtSwift.h"  #include <string> -#include <QSplitter>  #include <QFile>  #include <boost/bind.hpp>  #include <QMessageBox> @@ -22,6 +21,7 @@  #include <QtSwiftUtil.h>  #include <QtUIFactory.h>  #include <QtChatWindowFactory.h> +#include <QtSingleWindow.h>  #include <Swiften/Base/Log.h>  #include <Swift/Controllers/Storages/CertificateFileStorageFactory.h>  #include <Swift/Controllers/Storages/FileStoragesFactory.h> @@ -38,6 +38,7 @@  #include <SwifTools/AutoUpdater/AutoUpdater.h>  #include <SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h>  #include "Swiften/Base/Paths.h" +#include <Swift/Controllers/StatusCache.h>  #if defined(SWIFTEN_PLATFORM_WINDOWS)  #include "WindowsNotifier.h" @@ -66,9 +67,9 @@  namespace Swift{  #if defined(SWIFTEN_PLATFORM_MACOSX) -#define SWIFT_APPCAST_URL "http://swift.im/appcast/swift-mac-dev.xml" +//#define SWIFT_APPCAST_URL "http://swift.im/appcast/swift-mac-dev.xml"  #else  -#define SWIFT_APPCAST_URL "" +//#define SWIFT_APPCAST_URL ""  #endif  po::options_description QtSwift::getOptionsDescription() { @@ -102,32 +103,26 @@ XMLSettingsProvider* QtSwift::loadSettingsFile(const QString& fileName) {  	return new XMLSettingsProvider("");  } -QMap<QString, QString> QtSwift::loadEmoticonsFile(const QString& fileName) { -	QMap<QString, QString> emoticons; +void QtSwift::loadEmoticonsFile(const QString& fileName, QMap<QString, QString>& emoticons)  {  	QFile file(fileName);  	if (file.exists() && file.open(QIODevice::ReadOnly)) {  		while (!file.atEnd()) {  			QString line = file.readLine();  			line.replace("\n", "");  			line.replace("\r", ""); -			qDebug() << "Parsing line : " << line;  			QStringList tokens = line.split(" ");  			if (tokens.size() == 2) { -				emoticons[tokens[0]] = "file://" + tokens[1]; -				qDebug() << "Adding mapping from " << tokens[0] << " to " << tokens[1]; +				QString emoticonFile = tokens[1]; +				if (!emoticonFile.startsWith(":/") && !emoticonFile.startsWith("qrc:/")) { +					emoticonFile = "file://" + emoticonFile; +				} +				emoticons[tokens[0]] = emoticonFile;  			}  		}  	} - -	return emoticons;  }  QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMainThreadCaller_), autoUpdater_(NULL), idleDetector_(&idleQuerier_, networkFactories_.getTimerFactory(), 1000) { -	if (options.count("netbook-mode")) { -		splitter_ = new QSplitter(); -	} else { -		splitter_ = NULL; -	}  	QCoreApplication::setApplicationName(SWIFT_APPLICATION_NAME);  	QCoreApplication::setOrganizationName(SWIFT_ORGANIZATION_NAME);  	QCoreApplication::setOrganizationDomain(SWIFT_ORGANIZATION_DOMAIN); @@ -139,7 +134,15 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa  	settingsHierachy_->addProviderToTopOfStack(xmlSettings_);  	settingsHierachy_->addProviderToTopOfStack(qtSettings_); -	QMap<QString, QString> emoticons = loadEmoticonsFile(P2QSTRING((Paths::getExecutablePath() / "emoticons.txt").string())); +	QMap<QString, QString> emoticons; +	loadEmoticonsFile(":/emoticons/emoticons.txt", emoticons); +	loadEmoticonsFile(P2QSTRING((Paths::getExecutablePath() / "emoticons.txt").string()), emoticons); + +	if (options.count("netbook-mode")) { +		splitter_ = new QtSingleWindow(qtSettings_); +	} else { +		splitter_ = NULL; +	}  	int numberOfAccounts = 1;  	try { @@ -190,6 +193,8 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa  	uriHandler_ = new QtDBUSURIHandler();  #endif +	statusCache_ = new StatusCache(applicationPathProvider_); +  	if (splitter_) {  		splitter_->show();  	} @@ -199,7 +204,7 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa  			// Don't add the first tray (see note above)  			systemTrays_.push_back(new QtSystemTray());  		} -		QtUIFactory* uiFactory = new QtUIFactory(settingsHierachy_, qtSettings_, tabs_, splitter_, systemTrays_[i], chatWindowFactory_, networkFactories_.getTimerFactory(), startMinimized, !emoticons.empty()); +		QtUIFactory* uiFactory = new QtUIFactory(settingsHierachy_, qtSettings_, tabs_, splitter_, systemTrays_[i], chatWindowFactory_, networkFactories_.getTimerFactory(), statusCache_, startMinimized, !emoticons.empty());  		uiFactories_.push_back(uiFactory);  		MainController* mainController = new MainController(  				&clientMainThreadCaller_, @@ -243,6 +248,7 @@ QtSwift::~QtSwift() {  	}  	delete tabs_;  	delete splitter_; +	delete statusCache_;  	delete uriHandler_;  	delete dock_;  	delete soundPlayer_; diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h index 42fb50f..216527d 100644 --- a/Swift/QtUI/QtSwift.h +++ b/Swift/QtUI/QtSwift.h @@ -50,6 +50,8 @@ namespace Swift {  	class URIHandler;  	class SettingsProviderHierachy;  	class XMLSettingsProvider; +	class StatusCache; +	class QtSingleWindow;  	class QtSwift : public QObject {  		Q_OBJECT @@ -59,7 +61,7 @@ namespace Swift {  			~QtSwift();  		private:  			XMLSettingsProvider* loadSettingsFile(const QString& fileName); -			QMap<QString, QString> loadEmoticonsFile(const QString& fileName); +			void loadEmoticonsFile(const QString& fileName, QMap<QString, QString>& emoticons);  		private:  			QtEventLoop clientMainThreadCaller_;  			PlatformTLSFactories tlsFactories_; @@ -71,7 +73,7 @@ namespace Swift {  			QtSettingsProvider* qtSettings_;  			XMLSettingsProvider* xmlSettings_;  			SettingsProviderHierachy* settingsHierachy_; -			QSplitter* splitter_; +			QtSingleWindow* splitter_;  			QtSoundPlayer* soundPlayer_;  			Dock* dock_;  			URIHandler* uriHandler_; @@ -81,6 +83,7 @@ namespace Swift {  			CertificateStorageFactory* certificateStorageFactory_;  			AutoUpdater* autoUpdater_;  			Notifier* notifier_; +			StatusCache* statusCache_;  			PlatformIdleQuerier idleQuerier_;  			ActualIdleDetector idleDetector_;  #if defined(SWIFTEN_PLATFORM_MACOSX) diff --git a/Swift/QtUI/QtSwiftUtil.h b/Swift/QtUI/QtSwiftUtil.h index 2d0f970..c903af1 100644 --- a/Swift/QtUI/QtSwiftUtil.h +++ b/Swift/QtUI/QtSwiftUtil.h @@ -9,4 +9,6 @@  #define P2QSTRING(a) QString::fromUtf8(a.c_str())  #define Q2PSTRING(a) std::string(a.toUtf8()) +#include <boost/date_time/posix_time/posix_time.hpp> +  #define B2QDATE(a) QDateTime::fromTime_t((a - boost::posix_time::from_time_t(0)).total_seconds()) diff --git a/Swift/QtUI/QtTabbable.h b/Swift/QtUI/QtTabbable.h index baab15c..0b67b19 100644 --- a/Swift/QtUI/QtTabbable.h +++ b/Swift/QtUI/QtTabbable.h @@ -17,10 +17,10 @@ namespace Swift {  			enum AlertType {NoActivity, WaitingActivity, ImpendingActivity};  			~QtTabbable();  			bool isWidgetSelected(); -			virtual AlertType getWidgetAlertState() {return NoActivity;}; +			virtual AlertType getWidgetAlertState() {return NoActivity;}  			virtual int getCount() {return 0;}  		protected: -			QtTabbable() : QWidget() {}; +			QtTabbable() : QWidget() {}  			void keyPressEvent(QKeyEvent* event);  		protected slots: diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp index a154fb0..2ec2818 100644 --- a/Swift/QtUI/QtUIFactory.cpp +++ b/Swift/QtUI/QtUIFactory.cpp @@ -25,15 +25,17 @@  #include "QtContactEditWindow.h"  #include "QtAdHocCommandWindow.h"  #include "QtFileTransferListWidget.h" +#include <QtHighlightEditorWidget.h>  #include "Whiteboard/QtWhiteboardWindow.h"  #include <Swift/Controllers/Settings/SettingsProviderHierachy.h>  #include <Swift/QtUI/QtUISettingConstants.h>  #include <QtHistoryWindow.h>  #include <Swiften/Whiteboard/WhiteboardSession.h> +#include <QtSingleWindow.h>  namespace Swift { -QtUIFactory::QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, bool startMinimized, bool emoticonsExist) : settings(settings), qtOnlySettings(qtOnlySettings), tabs(tabs), netbookSplitter(netbookSplitter), systemTray(systemTray), chatWindowFactory(chatWindowFactory), timerFactory_(timerFactory), lastMainWindow(NULL), loginWindow(NULL), startMinimized(startMinimized), emoticonsExist_(emoticonsExist) { +QtUIFactory::QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, StatusCache* statusCache, bool startMinimized, bool emoticonsExist) : settings(settings), qtOnlySettings(qtOnlySettings), tabs(tabs), netbookSplitter(netbookSplitter), systemTray(systemTray), chatWindowFactory(chatWindowFactory), timerFactory_(timerFactory), lastMainWindow(NULL), loginWindow(NULL), statusCache(statusCache), startMinimized(startMinimized), emoticonsExist_(emoticonsExist) {  	chatFontSize = settings->getSetting(QtUISettingConstants::CHATWINDOW_FONT_SIZE);  	historyFontSize_ = settings->getSetting(QtUISettingConstants::HISTORYWINDOW_FONT_SIZE);  } @@ -78,14 +80,14 @@ FileTransferListWidget* QtUIFactory::createFileTransferListWidget() {  }  MainWindow* QtUIFactory::createMainWindow(UIEventStream* eventStream) { -	lastMainWindow  = new QtMainWindow(settings, eventStream, loginWindow->getMenus(), emoticonsExist_); +	lastMainWindow  = new QtMainWindow(settings, eventStream, loginWindow->getMenus(), statusCache, emoticonsExist_);  	return lastMainWindow;  }  LoginWindow* QtUIFactory::createLoginWindow(UIEventStream* eventStream) {  	loginWindow = new QtLoginWindow(eventStream, settings, timerFactory_);  	if (netbookSplitter) { -		netbookSplitter->insertWidget(0, loginWindow); +		netbookSplitter->insertAtFront(loginWindow);  	}  	connect(systemTray, SIGNAL(clicked()), loginWindow, SLOT(toggleBringToFront())); @@ -161,6 +163,10 @@ WhiteboardWindow* QtUIFactory::createWhiteboardWindow(boost::shared_ptr<Whiteboa  	return new QtWhiteboardWindow(whiteboardSession);  } +HighlightEditorWidget* QtUIFactory::createHighlightEditorWidget() { +	return new QtHighlightEditorWidget(); +} +  void QtUIFactory::createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) {  	new QtAdHocCommandWindow(command);  } diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h index 30f0101..a1baa82 100644 --- a/Swift/QtUI/QtUIFactory.h +++ b/Swift/QtUI/QtUIFactory.h @@ -26,11 +26,13 @@ namespace Swift {  	class TimerFactory;  	class historyWindow_;  	class WhiteboardSession; +	class StatusCache; +	class QtSingleWindow;  	class QtUIFactory : public QObject, public UIFactory {  			Q_OBJECT  		public: -			QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, bool startMinimized, bool emoticonsExist); +			QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, StatusCache* statusCache, bool startMinimized, bool emoticonsExist);  			virtual XMLConsoleWidget* createXMLConsoleWidget();  			virtual HistoryWindow* createHistoryWindow(UIEventStream*); @@ -46,6 +48,7 @@ namespace Swift {  			virtual ContactEditWindow* createContactEditWindow();  			virtual FileTransferListWidget* createFileTransferListWidget();  			virtual WhiteboardWindow* createWhiteboardWindow(boost::shared_ptr<WhiteboardSession> whiteboardSession); +			virtual HighlightEditorWidget* createHighlightEditorWidget();  			virtual void createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command);  		private slots: @@ -57,12 +60,13 @@ namespace Swift {  			SettingsProviderHierachy* settings;  			QtSettingsProvider* qtOnlySettings;  			QtChatTabs* tabs; -			QSplitter* netbookSplitter; +			QtSingleWindow* netbookSplitter;  			QtSystemTray* systemTray;  			QtChatWindowFactory* chatWindowFactory;  			TimerFactory* timerFactory_;  			QtMainWindow* lastMainWindow;  			QtLoginWindow* loginWindow; +			StatusCache* statusCache;  			std::vector<QPointer<QtChatWindow> > chatWindows;  			bool startMinimized;  			int chatFontSize; diff --git a/Swift/QtUI/QtURLValidator.cpp b/Swift/QtUI/QtURLValidator.cpp index 2df59c4..4d56b98 100644 --- a/Swift/QtUI/QtURLValidator.cpp +++ b/Swift/QtUI/QtURLValidator.cpp @@ -10,11 +10,11 @@  #include <Swift/QtUI/QtSwiftUtil.h>  namespace Swift { -QtURLValidator::QtURLValidator(QObject* parent) { +QtURLValidator::QtURLValidator(QObject* parent) : QValidator(parent) {  } -QValidator::State QtURLValidator::validate(QString& input, int& pos) const { +QValidator::State QtURLValidator::validate(QString& input, int&) const {  	URL url = URL::fromString(Q2PSTRING(input));  	bool valid = !url.isEmpty();  	valid &= (url.getScheme() == "http" || url.getScheme() == "https"); diff --git a/Swift/QtUI/QtVCardWidget/QtCloseButton.cpp b/Swift/QtUI/QtVCardWidget/QtCloseButton.cpp new file mode 100644 index 0000000..a6afe81 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtCloseButton.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtCloseButton.h" + +#include <QMouseEvent> +#include <QPainter> +#include <QStyle> +#include <QStyleOption> + +namespace Swift { + +QtCloseButton::QtCloseButton(QWidget *parent) : QAbstractButton(parent) { +	lightPixmap = QPixmap(12,12); +	lightPixmap.fill(QColor(0,0,0,0)); +	QStyleOption opt; +	opt.init(this); +	opt.state = QStyle::State(0); +	opt.state |= QStyle::State_MouseOver; +	QPainter lightPixmapPainter(&lightPixmap); +	style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &lightPixmapPainter); + +	darkPixmap = QPixmap(12,12); +	darkPixmap.fill(QColor(0,0,0,0)); +	opt.init(this); +	opt.state = QStyle::State(0); +	QPainter darkPixmapPainter(&darkPixmap); +	style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &darkPixmapPainter); +} + +QSize QtCloseButton::sizeHint() const { +	return QSize(width(), height()); +} + +bool QtCloseButton::event(QEvent *e) { +	if (e->type() == QEvent::Enter || e->type() == QEvent::Leave) { +		update(); +	} +	return QAbstractButton::event(e); +} + +void QtCloseButton::paintEvent(QPaintEvent *) { +	QPainter painter(this); +	painter.setRenderHint(QPainter::HighQualityAntialiasing); +	if (underMouse()) { +		painter.drawPixmap(0, 0, height(), height(), darkPixmap); +	} else { +		painter.drawPixmap(0, 0, height(), height(), lightPixmap); +	} +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtCloseButton.h b/Swift/QtUI/QtVCardWidget/QtCloseButton.h new file mode 100644 index 0000000..6ce8d30 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtCloseButton.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QAbstractButton> + +namespace Swift { + +	class QtCloseButton : public QAbstractButton { +			Q_OBJECT +		public: +			explicit QtCloseButton(QWidget *parent = 0); +			virtual QSize sizeHint() const; + +		protected: +			virtual bool event(QEvent *e); +			virtual void paintEvent(QPaintEvent* ); + +		private: +			QPixmap lightPixmap; +			QPixmap darkPixmap; +	}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp new file mode 100644 index 0000000..b586444 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtRemovableItemDelegate.h" + +#include <QEvent> +#include <QPainter> + +namespace Swift { + +QtRemovableItemDelegate::QtRemovableItemDelegate(const QStyle* style) : style(style) { + +} + +void QtRemovableItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex&) const { +	QStyleOption opt; +	opt.state = QStyle::State(0); +	opt.state |= QStyle::State_MouseOver; +	painter->save(); +	painter->fillRect(option.rect, option.state & QStyle::State_Selected ? option.palette.highlight() : option.palette.base()); +	painter->translate(option.rect.x(), option.rect.y()+(option.rect.height() - 12)/2); +	style->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, painter); +	painter->restore(); +} + +QWidget* QtRemovableItemDelegate::createEditor(QWidget*, const QStyleOptionViewItem&, const QModelIndex&) const { +	return NULL; +} + +bool QtRemovableItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { +	if (event->type() == QEvent::MouseButtonRelease) { +		model->removeRow(index.row()); +		return true; +	} else { +		return QItemDelegate::editorEvent(event, model, option, index); +	} +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h new file mode 100644 index 0000000..3d99ad8 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QItemDelegate> + +namespace Swift { + +class QtRemovableItemDelegate : public QItemDelegate { +	public: +		QtRemovableItemDelegate(const QStyle* style); + +		virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex&) const; +		virtual QWidget* createEditor(QWidget*, const QStyleOptionViewItem&, const QModelIndex&) const; + +	protected: +		virtual bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index); + +	private: +		const QStyle* style; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.cpp b/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.cpp new file mode 100644 index 0000000..efe04dc --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtResizableLineEdit.h" + +namespace Swift { + +QtResizableLineEdit::QtResizableLineEdit(QWidget* parent) : +	QLineEdit(parent) { +	connect(this, SIGNAL(textChanged(QString)), SLOT(textChanged(QString))); +	setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); +	int marginHeight = 6; +	setMaximumHeight(fontMetrics().height() + marginHeight); +} + +QtResizableLineEdit::~QtResizableLineEdit() { +} + +bool QtResizableLineEdit::isEditable() const { +	return editable; +} +void QtResizableLineEdit::setEditable(const bool editable) { +	this->editable = editable; +	if (editable) { +		setReadOnly(false); +	} else { +		setReadOnly(true); +	} +} + + +QSize QtResizableLineEdit::sizeHint() const { +	int horizontalMargin = 10; +#if QT_VERSION >= 0x040700 +	int w = fontMetrics().boundingRect(text().isEmpty() ? placeholderText() : text()).width() + horizontalMargin; +#else +	int w = fontMetrics().boundingRect(text().isEmpty() ? QString("   ") : text()).width() + horizontalMargin; +#endif +	return QSize(w, height()); +} + +void QtResizableLineEdit::textChanged(QString) { +	updateGeometry(); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.h b/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.h new file mode 100644 index 0000000..9022d38 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QLineEdit> + +namespace Swift { + +	class QtResizableLineEdit : public QLineEdit { +		Q_OBJECT +		Q_PROPERTY(bool editable READ isEditable WRITE setEditable) + +		public: +			explicit QtResizableLineEdit(QWidget* parent = 0); +			~QtResizableLineEdit(); + +			bool isEditable() const; +			void setEditable(const bool); + +			virtual QSize sizeHint() const; + +		private slots: +			void textChanged(QString); + +		private: +			bool editable; +	}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtTagComboBox.cpp b/Swift/QtUI/QtVCardWidget/QtTagComboBox.cpp new file mode 100644 index 0000000..bade009 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtTagComboBox.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtTagComboBox.h" + +#include <QAbstractItemView> +#include <QtGui> + +namespace Swift { + +QtTagComboBox::QtTagComboBox(QWidget* parent) : QComboBox(parent) { +	setEditable(false); +	displayModel = new QStandardItemModel(); +	displayItem = new QStandardItem(); +	displayItem->setText(""); +	displayModel->insertRow(0, displayItem); +	editMenu = new QMenu(); +	this->setModel(displayModel); +	editable = true; +} + +QtTagComboBox::~QtTagComboBox() { + +} + +bool QtTagComboBox::isEditable() const { +	return editable; +} + +void QtTagComboBox::setEditable(const bool editable) { +	this->editable = editable; +} + +void QtTagComboBox::addTag(const QString &id, const QString &label) { +	QAction* tagAction = new QAction(editMenu); +	tagAction->setText(label); +	tagAction->setCheckable(true); +	tagAction->setData(QString(id)); +	editMenu->addAction(tagAction); +} + +void QtTagComboBox::setTag(const QString &id, bool value) { +	QList<QAction*> tagActions = editMenu->actions(); +	foreach(QAction* action, tagActions) { +		if (action->data() == id) { +			action->setChecked(value); +			updateDisplayItem(); +			return; +		} +	} +} + +bool QtTagComboBox::isTagSet(const QString &id) const { +	QList<QAction*> tagActions = editMenu->actions(); +	foreach(QAction* action, tagActions) { +		if (action->data() == id) { +			return action->isChecked(); +		} +	} +	return false; +} + +void QtTagComboBox::showPopup() { + +} + +void QtTagComboBox::hidePopup() { + +} + +bool QtTagComboBox::event(QEvent* event) { +	if (event->type() == QEvent::MouseButtonPress || +		event->type() == QEvent::KeyRelease) { +		if (!editable) return true; + +		QPoint p = mapToGlobal(QPoint(0,0)); +		p += QPoint(0, height()); +		editMenu->exec(p); +		updateDisplayItem(); +		return true; +	} +	return QComboBox::event(event); +} + +void QtTagComboBox::updateDisplayItem() { +	QList<QAction*> tagActions = editMenu->actions(); +	QString text = ""; +	foreach(QAction* action, tagActions) { +		if (action->isChecked()) { +			if (text != "") { +				text += ", "; +			} +			text += action->text(); +		} +	} +	setItemText(0, text); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtTagComboBox.h b/Swift/QtUI/QtVCardWidget/QtTagComboBox.h new file mode 100644 index 0000000..37a60af --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtTagComboBox.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QComboBox> +#include <QMenu> +#include <QStandardItem> +#include <QStandardItemModel> + +namespace Swift { + +class QtTagComboBox : public QComboBox { +	Q_OBJECT +	Q_PROPERTY(bool editable READ isEditable WRITE setEditable) + +	public: +		explicit QtTagComboBox(QWidget* parent = 0); +		~QtTagComboBox(); + +		bool isEditable() const; +		void setEditable(const bool); + +		void addTag(const QString& id, const QString& label); +		void setTag(const QString& id, bool value); +		bool isTagSet(const QString& id) const; + +		virtual void showPopup(); +		virtual void hidePopup(); + +		virtual bool event(QEvent* event); + +	private: +		void updateDisplayItem(); + +	private: +		bool editable; +		QStandardItemModel* displayModel; +		QStandardItem* displayItem; +		QMenu* editMenu; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardAddressField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardAddressField.cpp new file mode 100644 index 0000000..4d34e53 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardAddressField.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardAddressField.h" + +#include <QGridLayout> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardAddressField::QtVCardAddressField(QWidget* parent, QGridLayout *layout, bool editable) : +	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Address")) { +	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardAddressField::~QtVCardAddressField() { +	disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardAddressField::setupContentWidgets() { +	textFieldGridLayout = new QGridLayout(); + +	streetLineEdit = new QtResizableLineEdit(this); +	textFieldGridLayout->addWidget(streetLineEdit, 0, 0, Qt::AlignVCenter); + +	poboxLineEdit = new QtResizableLineEdit(this); +	textFieldGridLayout->addWidget(poboxLineEdit, 0, 1, Qt::AlignVCenter); + +	addressextLineEdit = new QtResizableLineEdit(this); +	textFieldGridLayout->addWidget(addressextLineEdit, 1, 0, Qt::AlignVCenter); + +	cityLineEdit = new QtResizableLineEdit(this); +	textFieldGridLayout->addWidget(cityLineEdit, 2, 0, Qt::AlignVCenter); + +	pocodeLineEdit = new QtResizableLineEdit(this); +	textFieldGridLayout->addWidget(pocodeLineEdit, 2, 1, Qt::AlignVCenter); + +	regionLineEdit = new QtResizableLineEdit(this); +	textFieldGridLayout->addWidget(regionLineEdit, 3, 0, Qt::AlignVCenter); + +	countryLineEdit = new QtResizableLineEdit(this); +	textFieldGridLayout->addWidget(countryLineEdit, 4, 0, Qt::AlignVCenter); +	textFieldGridLayout->setVerticalSpacing(2); +	getGridLayout()->addLayout(textFieldGridLayout, getGridLayout()->rowCount()-1, 2, 5, 2, Qt::AlignVCenter); +	textFieldGridLayoutItem = getGridLayout()->itemAtPosition(getGridLayout()->rowCount()-1, 2); + +#if QT_VERSION >= 0x040700 +	streetLineEdit->setPlaceholderText(tr("Street")); +	poboxLineEdit->setPlaceholderText(tr("PO Box")); +	addressextLineEdit->setPlaceholderText(tr("Address Extension")); +	cityLineEdit->setPlaceholderText(tr("City")); +	pocodeLineEdit->setPlaceholderText(tr("Postal Code")); +	regionLineEdit->setPlaceholderText(tr("Region")); +	countryLineEdit->setPlaceholderText(tr("Country")); +#endif + +	deliveryTypeLabel = new QLabel(this); +	deliveryTypeLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); +	getGridLayout()->addWidget(deliveryTypeLabel, getGridLayout()->rowCount()-3, 4, Qt::AlignVCenter); + +	domesticRadioButton = new QRadioButton(tr("Domestic Delivery"), this); +	getGridLayout()->addWidget(domesticRadioButton, getGridLayout()->rowCount()-2, 4, Qt::AlignVCenter); + +	internationalRadioButton = new QRadioButton(tr("International Delivery"), this); +	getGridLayout()->addWidget(internationalRadioButton, getGridLayout()->rowCount()-1, 4, Qt::AlignVCenter); + +	buttonGroup = new QButtonGroup(this); +	buttonGroup->addButton(domesticRadioButton); +	buttonGroup->addButton(internationalRadioButton); + +	setTabOrder(internationalRadioButton, getTagComboBox()); +	getTagComboBox()->addTag("postal", tr("Postal")); +	getTagComboBox()->addTag("parcel", tr("Parcel")); + +	QtVCardHomeWork::setTagComboBox(getTagComboBox()); + +	textFields << streetLineEdit << poboxLineEdit << addressextLineEdit << cityLineEdit << pocodeLineEdit << regionLineEdit << countryLineEdit; +	childWidgets << deliveryTypeLabel << domesticRadioButton << internationalRadioButton; +} + +void QtVCardAddressField::customCleanup() { +	foreach(QWidget* widget, textFields) { +		widget->hide(); +		textFieldGridLayout->removeWidget(widget); +	} +	getGridLayout()->removeItem(textFieldGridLayoutItem); +} + + + +bool QtVCardAddressField::isEmpty() const { +	return streetLineEdit->text().isEmpty() && +			poboxLineEdit->text().isEmpty() && +			addressextLineEdit->text().isEmpty() && +			cityLineEdit->text().isEmpty() && +			pocodeLineEdit->text().isEmpty() && +			regionLineEdit->text().isEmpty() && +			countryLineEdit->text().isEmpty(); +} + +void QtVCardAddressField::setAddress(const VCard::Address& address) { +	setPreferred(address.isPreferred); +	setHome(address.isHome); +	setWork(address.isWork); +	getTagComboBox()->setTag("postal", address.isPostal); +	getTagComboBox()->setTag("parcel", address.isParcel); +	domesticRadioButton->setChecked(address.deliveryType == VCard::DomesticDelivery); +	internationalRadioButton->setChecked(address.deliveryType == VCard::InternationalDelivery); +	streetLineEdit->setText(P2QSTRING(address.street)); +	poboxLineEdit->setText(P2QSTRING(address.poBox)); +	addressextLineEdit->setText(P2QSTRING(address.addressExtension)); +	cityLineEdit->setText(P2QSTRING(address.locality)); +	pocodeLineEdit->setText(P2QSTRING(address.postalCode)); +	regionLineEdit->setText(P2QSTRING(address.region)); +	countryLineEdit->setText(P2QSTRING(address.country)); +} + +VCard::Address QtVCardAddressField::getAddress() const { +	VCard::Address address; +	address.isPreferred = getPreferred(); +	address.isHome = getHome(); +	address.isWork = getWork(); +	address.deliveryType = domesticRadioButton->isChecked() ? VCard::DomesticDelivery : (internationalRadioButton->isChecked() ? VCard::InternationalDelivery : VCard::None); +	address.isPostal = getTagComboBox()->isTagSet("postal"); +	address.isParcel = getTagComboBox()->isTagSet("parcel"); +	address.street = Q2PSTRING(streetLineEdit->text()); +	address.poBox = Q2PSTRING(poboxLineEdit->text()); +	address.addressExtension = Q2PSTRING(addressextLineEdit->text()); +	address.locality = Q2PSTRING(cityLineEdit->text()); +	address.postalCode = Q2PSTRING(pocodeLineEdit->text()); +	address.region = Q2PSTRING(regionLineEdit->text()); +	address.country = Q2PSTRING(countryLineEdit->text()); +	return address; +} + +void QtVCardAddressField::handleEditibleChanged(bool isEditable) { +	if (streetLineEdit) streetLineEdit->setEditable(isEditable); +	if (poboxLineEdit) poboxLineEdit->setEditable(isEditable); +	if (addressextLineEdit) addressextLineEdit->setEditable(isEditable); +	if (cityLineEdit) cityLineEdit->setEditable(isEditable); +	if (pocodeLineEdit) pocodeLineEdit->setEditable(isEditable); +	if (regionLineEdit) regionLineEdit->setEditable(isEditable); +	if (countryLineEdit) countryLineEdit->setEditable(isEditable); + +	if (deliveryTypeLabel) { +		deliveryTypeLabel->setText(buttonGroup->checkedButton() == 0 ? "" : buttonGroup->checkedButton()->text()); +		deliveryTypeLabel->setVisible(!isEditable); +	} +	if (domesticRadioButton) domesticRadioButton->setVisible(isEditable); +	if (internationalRadioButton) internationalRadioButton->setVisible(isEditable); + +	foreach (QWidget* widget, textFields) { +		QtResizableLineEdit* lineEdit; +		if ((lineEdit = dynamic_cast<QtResizableLineEdit*>(widget))) { +			lineEdit->setShown(isEditable ? true : !lineEdit->text().isEmpty()); +			lineEdit->setStyleSheet(isEditable ? "" : "QLineEdit { border: none; background: transparent; }"); +		} +	} +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardAddressField.h b/Swift/QtUI/QtVCardWidget/QtVCardAddressField.h new file mode 100644 index 0000000..5a1256a --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardAddressField.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include <QButtonGroup> +#include <QRadioButton> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" +#include "QtVCardHomeWork.h" + +namespace Swift { + +class QtVCardAddressField : public QtVCardGeneralField, public QtVCardHomeWork { +	Q_OBJECT + +	public: +		GENERIC_QT_VCARD_FIELD_INFO("Address", UNLIMITED_INSTANCES, QtVCardAddressField) + +		QtVCardAddressField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); +		virtual ~QtVCardAddressField(); + +		virtual bool isEmpty() const; + +		void setAddress(const VCard::Address& address); +		VCard::Address getAddress() const; + +	protected: +		virtual void setupContentWidgets(); +		virtual void customCleanup(); + +	public slots: +		void handleEditibleChanged(bool isEditable); + +	private: +		QList<QWidget*> textFields; +		QtResizableLineEdit* streetLineEdit; +		QtResizableLineEdit* poboxLineEdit; +		QtResizableLineEdit* addressextLineEdit; +		QtResizableLineEdit* cityLineEdit; +		QtResizableLineEdit* pocodeLineEdit; +		QtResizableLineEdit* regionLineEdit; +		QtResizableLineEdit* countryLineEdit; +		QGridLayout* textFieldGridLayout; +		QLayoutItem* textFieldGridLayoutItem; + +		QLabel* deliveryTypeLabel; +		QRadioButton* domesticRadioButton; +		QRadioButton* internationalRadioButton; +		QButtonGroup* buttonGroup; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.cpp new file mode 100644 index 0000000..20f48b9 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardAddressLabelField.h" + +#include <QGridLayout> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardAddressLabelField::QtVCardAddressLabelField(QWidget* parent, QGridLayout *layout, bool editable) : +	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Address Label")) { +	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardAddressLabelField::~QtVCardAddressLabelField() { +	disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardAddressLabelField::setupContentWidgets() { +	addressLabelPlainTextEdit = new QPlainTextEdit(this); +	addressLabelPlainTextEdit->setTabChangesFocus(true); +	getGridLayout()->addWidget(addressLabelPlainTextEdit, getGridLayout()->rowCount()-1, 2, 3, 2, Qt::AlignVCenter); + +	deliveryTypeLabel = new QLabel(this); +	deliveryTypeLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); +	getGridLayout()->addWidget(deliveryTypeLabel, getGridLayout()->rowCount()-2, 4, Qt::AlignVCenter); + +	domesticRadioButton = new QRadioButton(tr("Domestic Delivery"), this); +	getGridLayout()->addWidget(domesticRadioButton, getGridLayout()->rowCount()-2, 4, Qt::AlignVCenter); + +	internationalRadioButton = new QRadioButton(tr("International Delivery"), this); +	getGridLayout()->addWidget(internationalRadioButton, getGridLayout()->rowCount()-1, 4, Qt::AlignVCenter); + +	buttonGroup = new QButtonGroup(this); +	buttonGroup->addButton(domesticRadioButton); +	buttonGroup->addButton(internationalRadioButton); + +	setTabOrder(internationalRadioButton, getTagComboBox()); +	getTagComboBox()->addTag("postal", tr("Postal")); +	getTagComboBox()->addTag("parcel", tr("Parcel")); + +	QtVCardHomeWork::setTagComboBox(getTagComboBox()); +	deliveryTypeLabel->hide(); +	childWidgets << addressLabelPlainTextEdit << deliveryTypeLabel << domesticRadioButton << internationalRadioButton; +} + +bool QtVCardAddressLabelField::isEmpty() const { +	return addressLabelPlainTextEdit->toPlainText().isEmpty(); +} + +void QtVCardAddressLabelField::setAddressLabel(const VCard::AddressLabel& addressLabel) { +	setPreferred(addressLabel.isPreferred); +	setHome(addressLabel.isHome); +	setWork(addressLabel.isWork); +	getTagComboBox()->setTag("postal", addressLabel.isPostal); +	getTagComboBox()->setTag("parcel", addressLabel.isParcel); +	domesticRadioButton->setChecked(addressLabel.deliveryType == VCard::DomesticDelivery); +	internationalRadioButton->setChecked(addressLabel.deliveryType == VCard::InternationalDelivery); +	std::string joinedLines = boost::algorithm::join(addressLabel.lines, "\n"); +	addressLabelPlainTextEdit->setPlainText(P2QSTRING(joinedLines)); +} + +VCard::AddressLabel QtVCardAddressLabelField::getAddressLabel() const { +	VCard::AddressLabel addressLabel; +	addressLabel.isPreferred = getPreferred(); +	addressLabel.isHome = getHome(); +	addressLabel.isWork = getWork(); +	addressLabel.deliveryType = domesticRadioButton->isChecked() ? VCard::DomesticDelivery : (internationalRadioButton->isChecked() ? VCard::InternationalDelivery : VCard::None); +	addressLabel.isPostal = getTagComboBox()->isTagSet("postal"); +	addressLabel.isParcel = getTagComboBox()->isTagSet("parcel"); + +	std::string lines = Q2PSTRING(addressLabelPlainTextEdit->toPlainText()); +	boost::split(addressLabel.lines, lines, boost::is_any_of("\n")); +	return addressLabel; +} + +void QtVCardAddressLabelField::handleEditibleChanged(bool isEditable) { +	if (addressLabelPlainTextEdit) { +		addressLabelPlainTextEdit->setReadOnly(!isEditable); +		addressLabelPlainTextEdit->setStyleSheet(isEditable ? "" : "QPlainTextEdit { background: transparent; }"); +	} + +	if (deliveryTypeLabel) { +		deliveryTypeLabel->setText(buttonGroup->checkedButton() == 0 ? "" : buttonGroup->checkedButton()->text()); +		deliveryTypeLabel->setVisible(!isEditable); +	} +	if (domesticRadioButton) domesticRadioButton->setVisible(isEditable); +	if (internationalRadioButton) internationalRadioButton->setVisible(isEditable); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.h b/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.h new file mode 100644 index 0000000..0e097d9 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QButtonGroup> +#include <QPlainTextEdit> +#include <QRadioButton> +#include <Swiften/Elements/VCard.h> + +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" +#include "QtVCardHomeWork.h" + +namespace Swift { + +class QtVCardAddressLabelField : public QtVCardGeneralField, public QtVCardHomeWork { +	Q_OBJECT + +	public: +		GENERIC_QT_VCARD_FIELD_INFO("AddressLabel", UNLIMITED_INSTANCES, QtVCardAddressLabelField) + +		QtVCardAddressLabelField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); +		virtual ~QtVCardAddressLabelField(); + +		virtual bool isEmpty() const; + +		void setAddressLabel(const VCard::AddressLabel& addressLabel); +		VCard::AddressLabel getAddressLabel() const; + +	protected: +		virtual void setupContentWidgets(); + +	public slots: +		void handleEditibleChanged(bool isEditable); + +	private: +		QPlainTextEdit* addressLabelPlainTextEdit; + +		QLabel* deliveryTypeLabel; +		QRadioButton* domesticRadioButton; +		QRadioButton* internationalRadioButton; +		QButtonGroup* buttonGroup; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.cpp new file mode 100644 index 0000000..2afc2f6 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardBirthdayField.h" + +#include <QGridLayout> +#include <QHBoxLayout> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardBirthdayField::QtVCardBirthdayField(QWidget* parent, QGridLayout *layout, bool editable) : +	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Birthday"), false, false) { +	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardBirthdayField::~QtVCardBirthdayField() { +	disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardBirthdayField::setupContentWidgets() { +	birthdayLabel = new QLabel(this); +	birthdayLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); +	birthdayDateEdit = new QDateEdit(this); +	birthdayDateEdit->setCalendarPopup(true); + +	QHBoxLayout* birthdayLayout = new QHBoxLayout(); +	birthdayLayout->addWidget(birthdayLabel); +	birthdayLayout->addWidget(birthdayDateEdit); + +	getGridLayout()->addLayout(birthdayLayout, getGridLayout()->rowCount()-1, 2, Qt::AlignVCenter); + +	getTagComboBox()->hide(); +	birthdayLabel->hide(); +	childWidgets << birthdayLabel << birthdayDateEdit; +} + +bool QtVCardBirthdayField::isEmpty() const { +	return false; +} + +void QtVCardBirthdayField::setBirthday(const boost::posix_time::ptime& birthday) { +	birthdayDateEdit->setDate(B2QDATE(birthday).date()); +} + +boost::posix_time::ptime QtVCardBirthdayField::getBirthday() const { +	return boost::posix_time::from_time_t(QDateTime(birthdayDateEdit->date()).toTime_t()); +} + +void QtVCardBirthdayField::handleEditibleChanged(bool isEditable) { +	birthdayLabel->setText(birthdayDateEdit->date().toString(Qt::DefaultLocaleLongDate)); +	birthdayDateEdit->setVisible(isEditable); +	birthdayLabel->setVisible(!isEditable); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.h b/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.h new file mode 100644 index 0000000..4be6e27 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QDateEdit> +#include <Swiften/Elements/VCard.h> + +#include "QtCloseButton.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardBirthdayField : public QtVCardGeneralField { +	Q_OBJECT + +	public: +		GENERIC_QT_VCARD_FIELD_INFO("Birthday", 1, QtVCardBirthdayField) + +		QtVCardBirthdayField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); +		virtual ~QtVCardBirthdayField(); + +		virtual bool isEmpty() const; + +		void setBirthday(const boost::posix_time::ptime& addressLabel); +		boost::posix_time::ptime getBirthday() const; + +	protected: +		virtual void setupContentWidgets(); + +	public slots: +		void handleEditibleChanged(bool isEditable); + +	private: +		QLabel* birthdayLabel; +		QDateEdit* birthdayDateEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.cpp new file mode 100644 index 0000000..f907d78 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardDescriptionField.h" + +#include <boost/algorithm/string.hpp> +#include <QFontMetrics> +#include <QGridLayout> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardDescriptionField::QtVCardDescriptionField(QWidget* parent, QGridLayout *layout, bool editable) : +	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Description"), false, false) { +	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardDescriptionField::~QtVCardDescriptionField() { +	disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardDescriptionField::setupContentWidgets() { +	descriptionPlainTextEdit = new QPlainTextEdit(this); +	descriptionPlainTextEdit->setMinimumHeight(70); +	getGridLayout()->addWidget(descriptionPlainTextEdit, getGridLayout()->rowCount()-1, 2, 2, 2, Qt::AlignVCenter); +	getTagComboBox()->hide(); +	childWidgets << descriptionPlainTextEdit; +} + +bool QtVCardDescriptionField::isEmpty() const { +	return descriptionPlainTextEdit->toPlainText().isEmpty(); +} + +void QtVCardDescriptionField::setDescription(const std::string& description) { +	descriptionPlainTextEdit->setPlainText(P2QSTRING(description)); +} + +std::string QtVCardDescriptionField::getDescription() const { +	return Q2PSTRING(descriptionPlainTextEdit->toPlainText()); +} + +void QtVCardDescriptionField::handleEditibleChanged(bool isEditable) { +	if (descriptionPlainTextEdit) { +		if (isEditable) { +			descriptionPlainTextEdit->setMinimumHeight(70); +		} else { +			QFontMetrics inputMetrics(descriptionPlainTextEdit->document()->defaultFont()); +			QRect horizontalBounds = contentsRect().adjusted(0,0,0,9999); +			QRect boundingRect = inputMetrics.boundingRect(horizontalBounds, Qt::TextWordWrap, descriptionPlainTextEdit->toPlainText() + "A"); +			int left, top, right, bottom; +			getContentsMargins(&left, &top, &right, &bottom); +			int height = boundingRect.height() + top + bottom + inputMetrics.height(); +			descriptionPlainTextEdit->setMinimumHeight(height > 70 ? 70 : height); +		} +		descriptionPlainTextEdit->setReadOnly(!isEditable); +		descriptionPlainTextEdit->setStyleSheet(isEditable ? "" : "QPlainTextEdit { background: transparent; }"); +	} +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.h b/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.h new file mode 100644 index 0000000..3b1b3d9 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include <QPlainTextEdit> + +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardDescriptionField : public QtVCardGeneralField { +	Q_OBJECT + +	public: +		GENERIC_QT_VCARD_FIELD_INFO("Description", 1, QtVCardDescriptionField) + +		QtVCardDescriptionField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); +		virtual ~QtVCardDescriptionField(); + +		virtual bool isEmpty() const; + +		void setDescription(const std::string& description); +		std::string getDescription() const; + +	protected: +		virtual void setupContentWidgets(); + +	public slots: +		void handleEditibleChanged(bool isEditable); + +	private: +		QPlainTextEdit* descriptionPlainTextEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h b/Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h new file mode 100644 index 0000000..168c01b --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QGridLayout> +#include <QObject> +#include <QString> +#include <typeinfo> + +#define GENERIC_QT_VCARD_FIELD_INFO(MENU_NAME, ALLOWED_INSTANCES, FIELD_CLASS) \ +	class FieldInfo : public QtVCardFieldInfo { \ +		public: \ +			virtual ~FieldInfo() { \ +			} \ +	\ +			virtual QString getMenuName() const { \ +				return QObject::tr(MENU_NAME); \ +			} \ +	\ +			virtual int getAllowedInstances() const { \ +				return ALLOWED_INSTANCES; \ +			} \ +	\ +			virtual QWidget* createFieldInstance(QWidget* parent, QGridLayout* layout, bool editable) const { \ +				return new FIELD_CLASS(parent, layout, editable); \ +			} \ +	\ +			virtual bool testInstance(QWidget* widget) const { \ +				return dynamic_cast<FIELD_CLASS*>(widget) != 0; \ +			} \ +	}; + +class QWidget; + +namespace Swift { + +	class QtVCardFieldInfo { +		public: +			static const int UNLIMITED_INSTANCES = -1; + +			virtual ~QtVCardFieldInfo() { +			} +			virtual QString getMenuName() const = 0; +			virtual int getAllowedInstances() const = 0; +			virtual QWidget* createFieldInstance(QWidget* parent, QGridLayout* layout, bool editable) const = 0; +			virtual bool testInstance(QWidget*) const = 0; +	}; +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.cpp new file mode 100644 index 0000000..5b3ef87 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardGeneralField.h" + +#include <QHBoxLayout> +#include <Swiften/Base/Log.h> + +namespace Swift { + +QtVCardGeneralField::QtVCardGeneralField(QWidget* parent, QGridLayout* layout, bool editable, int row, QString label, bool preferrable, bool taggable) : +	QWidget(parent), preferrable(preferrable), taggable(taggable), layout(layout), row(row), preferredCheckBox(0), label(0), labelText(label), +	tagComboBox(0), closeButton(0) { +	setEditable(editable); +} + +QtVCardGeneralField::~QtVCardGeneralField() { + +} + +void QtVCardGeneralField::initialize() { +	if (preferrable) { +		preferredCheckBox = new QCheckBox(this); +		preferredCheckBox->setStyleSheet( +					"QCheckBox::indicator { width: 18px; height: 18px; }" +					"QCheckBox::indicator:checked { image: url(:/icons/star-checked.png); }" +					"QCheckBox::indicator:unchecked { image: url(:/icons/star-unchecked); }" +			); +		layout->addWidget(preferredCheckBox, row, 0, Qt::AlignVCenter); +		childWidgets << preferredCheckBox; +	} +	label = new QLabel(this); +	label->setText(labelText); +	layout->addWidget(label, row, 1, Qt::AlignVCenter | Qt::AlignRight); + +	tagLabel = new QLabel(this); +	tagLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + +	tagComboBox = new QtTagComboBox(this); +	closeButton = new QtCloseButton(this); +	connect(closeButton, SIGNAL(clicked()), SLOT(handleCloseButtonClicked())); + +	QHBoxLayout* tagLayout = new QHBoxLayout(); +	tagLayout->addWidget(tagLabel); +	tagLayout->addWidget(tagComboBox); + +	setupContentWidgets(); +	layout->addLayout(tagLayout, row, 4, Qt::AlignTop); +	layout->addWidget(closeButton, row, 5, Qt::AlignCenter); +	closeButton->resize(12, 12); +	tagLabel->hide(); + +	childWidgets << label << tagComboBox << tagLabel << closeButton; +} + +bool QtVCardGeneralField::isEditable() const { +	return editable; +} + +void QtVCardGeneralField::setEditable(bool editable) { +	this->editable = editable; +	if (tagComboBox) { +		if (taggable) { +			tagLabel->setText(tagComboBox->itemText(0)); +			tagComboBox->setVisible(editable); +			tagLabel->setVisible(!editable); +		} else { +			tagLabel->hide(); +			tagComboBox->hide(); +		} +	} +	if (closeButton) closeButton->setVisible(editable); +	if (preferredCheckBox) { +		if (editable) { +			preferredCheckBox->show(); +		} else if (!preferredCheckBox->isChecked()) { +			preferredCheckBox->hide(); +		} +		preferredCheckBox->setEnabled(editable); +	} +	editableChanged(this->editable); +} + +void QtVCardGeneralField::setPreferred(const bool preferred) { +	if (preferredCheckBox) preferredCheckBox->setChecked(preferred); +} + +bool QtVCardGeneralField::getPreferred() const { +	return preferredCheckBox ? preferredCheckBox->isChecked() : false; +} + +void QtVCardGeneralField::customCleanup() { +} + +QtTagComboBox* QtVCardGeneralField::getTagComboBox() const { +	return tagComboBox; +} + +QGridLayout* QtVCardGeneralField::getGridLayout() const { +	return layout; +} + +void QtVCardGeneralField::handleCloseButtonClicked() { +	customCleanup(); +	foreach(QWidget* widget, childWidgets) { +		widget->hide(); +		layout->removeWidget(widget); +	} +	deleteField(this); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.h b/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.h new file mode 100644 index 0000000..4afe692 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QCheckBox> +#include <QGridLayout> +#include <QLabel> +#include <QWidget> + +#include "QtCloseButton.h" +#include "QtTagComboBox.h" + +namespace Swift { + +/* + *	covers features like: + *		- preffered (star ceckbox) + *		- combo check boxh + *		- label + *		- remove button + */ +class QtVCardGeneralField : public QWidget { +		Q_OBJECT +		Q_PROPERTY(bool editable READ isEditable WRITE setEditable NOTIFY editableChanged) +		Q_PROPERTY(bool empty READ isEmpty) + +	public: +		explicit QtVCardGeneralField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false, int row = 0, QString label = QString(), +										bool preferrable = true, bool taggable = true); +		virtual ~QtVCardGeneralField(); + +		void initialize(); + +		virtual bool isEditable() const; +		virtual void setEditable(bool); + +		virtual bool isEmpty() const = 0; + +		void setPreferred(const bool preferred); +		bool getPreferred() const; + +	protected: +		virtual void setupContentWidgets() = 0; +		virtual void customCleanup(); + +		QtTagComboBox* getTagComboBox() const; +		QGridLayout* getGridLayout() const; + +	signals: +		void editableChanged(bool); +		void deleteField(QtVCardGeneralField*); +		 +	public slots: +		void handleCloseButtonClicked(); + +	protected: +		QList<QWidget*> childWidgets; + +	private: +		bool editable; +		bool preferrable; +		bool taggable; +		QGridLayout* layout; +		int row; +		QCheckBox* preferredCheckBox; +		QLabel* label; +		QString labelText; +		QtTagComboBox* tagComboBox; +		QLabel* tagLabel; +		QtCloseButton* closeButton; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.cpp b/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.cpp new file mode 100644 index 0000000..3119a80 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardHomeWork.h" + +namespace Swift { + +QtVCardHomeWork::QtVCardHomeWork() : tagComboBox(0) { +} + +QtVCardHomeWork::~QtVCardHomeWork() { +} + +void QtVCardHomeWork::setTagComboBox(QtTagComboBox* tagBox) { +	tagComboBox = tagBox; +	tagComboBox->addTag("home", QObject::tr("Home")); +	tagComboBox->addTag("work", QObject::tr("Work")); +} + +void QtVCardHomeWork::setHome(const bool home) { +	tagComboBox->setTag("home", home); +} + +bool QtVCardHomeWork::getHome() const { +	return tagComboBox->isTagSet("home"); +} + +void QtVCardHomeWork::setWork(const bool work) { +	tagComboBox->setTag("work", work); +} + +bool QtVCardHomeWork::getWork() const { +	return tagComboBox->isTagSet("work"); +} + +} + diff --git a/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.h b/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.h new file mode 100644 index 0000000..768d984 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QObject> + +#include "QtTagComboBox.h" + +namespace Swift { + +class QtVCardHomeWork { +	public: +		QtVCardHomeWork(); +		virtual ~QtVCardHomeWork(); + +		void setTagComboBox(QtTagComboBox* tagBox); + +		void setHome(const bool home); +		bool getHome() const; +		void setWork(const bool work); +		bool getWork() const; + +	private: +		QtTagComboBox* tagComboBox; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.cpp new file mode 100644 index 0000000..46f253f --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardInternetEMailField.h" + +#include <QGridLayout> +#include <QHBoxLayout> +#include <QTextDocument> +#include <Swiften/Base/Log.h> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardInternetEMailField::QtVCardInternetEMailField(QWidget* parent, QGridLayout *layout, bool editable) : +	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("E-Mail")) { +	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardInternetEMailField::~QtVCardInternetEMailField() { +	disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardInternetEMailField::setupContentWidgets() { +	emailLabel = new QLabel(this); +	emailLabel->setOpenExternalLinks(true); +	emailLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); +	emailLineEdit = new QtResizableLineEdit(this); +#if QT_VERSION >= 0x040700 +	emailLineEdit->setPlaceholderText(tr("alice@wonderland.lit")); +#endif +	QHBoxLayout* emailLayout = new QHBoxLayout(); +	emailLayout->addWidget(emailLabel); +	emailLayout->addWidget(emailLineEdit); +	getGridLayout()->addLayout(emailLayout, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); +	setTabOrder(emailLineEdit, getTagComboBox()); +	QtVCardHomeWork::setTagComboBox(getTagComboBox()); +	emailLabel->hide(); +	childWidgets << emailLabel << emailLineEdit; +} + +bool QtVCardInternetEMailField::isEmpty() const { +	return emailLineEdit->text().isEmpty(); +} + +void QtVCardInternetEMailField::setInternetEMailAddress(const VCard::EMailAddress& address) { +	assert(address.isInternet); +	setPreferred(address.isPreferred); +	setHome(address.isHome); +	setWork(address.isWork); +	emailLineEdit->setText(P2QSTRING(address.address)); +} + +VCard::EMailAddress QtVCardInternetEMailField::getInternetEMailAddress() const { +	VCard::EMailAddress address; +	address.isInternet = true; +	address.isPreferred = getPreferred(); +	address.isHome = getHome(); +	address.isWork = getWork(); +	address.address = Q2PSTRING(emailLineEdit->text()); +	return address; +} + +void QtVCardInternetEMailField::handleEditibleChanged(bool isEditable) { +	if (isEditable) { +		if (emailLineEdit) emailLineEdit->show(); +		if (emailLabel) emailLabel->hide(); +	} else { +		if (emailLineEdit) emailLineEdit->hide(); +		if (emailLabel) { +			emailLabel->setText(QString("<a href=\"mailto:%1\">%1</a>").arg(Qt::escape(emailLineEdit->text()))); +			emailLabel->show(); +		} +	} +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.h b/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.h new file mode 100644 index 0000000..3f8a27f --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" +#include "QtVCardHomeWork.h" + +namespace Swift { + +class QtVCardInternetEMailField : public QtVCardGeneralField, public QtVCardHomeWork { +	Q_OBJECT + +	public: +		GENERIC_QT_VCARD_FIELD_INFO("E-Mail", UNLIMITED_INSTANCES, QtVCardInternetEMailField) + +		QtVCardInternetEMailField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); +		virtual ~QtVCardInternetEMailField(); + +		virtual bool isEmpty() const; + +		void setInternetEMailAddress(const VCard::EMailAddress& address); +		VCard::EMailAddress getInternetEMailAddress() const; + +	protected: +		virtual void setupContentWidgets(); + +	public slots: +		void handleEditibleChanged(bool isEditable); + +	private: +		QtResizableLineEdit* emailLineEdit; +		QLabel* emailLabel; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardJIDField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardJIDField.cpp new file mode 100644 index 0000000..dbb4b7c --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardJIDField.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardJIDField.h" + +#include <QGridLayout> +#include <QTextDocument> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardJIDField::QtVCardJIDField(QWidget* parent, QGridLayout *layout, bool editable) : +	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("JID"), false, false) { +	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardJIDField::~QtVCardJIDField() { +	disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardJIDField::setupContentWidgets() { +	jidLabel = new QLabel(this); +	jidLabel->setOpenExternalLinks(true); +	jidLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); +	jidLineEdit = new QtResizableLineEdit(this); +#if QT_VERSION >= 0x040700 +	jidLineEdit->setPlaceholderText(tr("alice@wonderland.lit")); +#endif +	QHBoxLayout* jidLayout = new QHBoxLayout(); +	jidLayout->addWidget(jidLabel); +	jidLayout->addWidget(jidLineEdit); +	getGridLayout()->addLayout(jidLayout, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); + +	jidLabel->hide(); +	getTagComboBox()->hide(); + +	childWidgets << jidLabel << jidLineEdit; +} + +bool QtVCardJIDField::isEmpty() const { +	return jidLineEdit->text().isEmpty(); +} + +void QtVCardJIDField::setJID(const JID& jid) { +	std::string jidStr = jid.toBare().toString(); +	jidLineEdit->setText(P2QSTRING(jidStr)); +} + +JID QtVCardJIDField::getJID() const { +	return JID(Q2PSTRING(jidLineEdit->text())); +} + +void QtVCardJIDField::handleEditibleChanged(bool isEditable) { +	if (isEditable) { +		if (jidLineEdit) jidLineEdit->show(); +		if (jidLabel) jidLabel->hide(); +	} else { +		if (jidLineEdit) jidLineEdit->hide(); +		if (jidLabel) { +			jidLabel->setText(QString("<a href=\"xmpp:%1\">%1</a>").arg(Qt::escape(jidLineEdit->text()))); +			jidLabel->show(); +		} +	} +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardJIDField.h b/Swift/QtUI/QtVCardWidget/QtVCardJIDField.h new file mode 100644 index 0000000..016bcf8 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardJIDField.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardJIDField : public QtVCardGeneralField { +	Q_OBJECT + +	public: +		GENERIC_QT_VCARD_FIELD_INFO("JID", UNLIMITED_INSTANCES, QtVCardJIDField) + +		QtVCardJIDField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); +		virtual ~QtVCardJIDField(); + +		virtual bool isEmpty() const; + +		void setJID(const JID& jid); +		JID getJID() const; + +	protected: +		virtual void setupContentWidgets(); + +	public slots: +		void handleEditibleChanged(bool isEditable); + +	private: +		QLabel* jidLabel; +		QtResizableLineEdit* jidLineEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.cpp new file mode 100644 index 0000000..5f231dc --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardOrganizationField.h" + +#include <QGridLayout> +#include <QHBoxLayout> +#include <QHeaderView> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardOrganizationField::QtVCardOrganizationField(QWidget* parent, QGridLayout *layout, bool editable) : +	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Organisation"), false, false) { +	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardOrganizationField::~QtVCardOrganizationField() { +	disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardOrganizationField::setupContentWidgets() { +	organizationLabel = new QLabel(this); +	organizationLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); +	organizationLineEdit = new QtResizableLineEdit(this); +	QHBoxLayout* organizationLayout = new QHBoxLayout(); +	organizationLayout->addWidget(organizationLabel); +	organizationLayout->addWidget(organizationLineEdit); + +	getGridLayout()->addLayout(organizationLayout, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); + +	itemDelegate = new QtRemovableItemDelegate(style()); + +	unitsTreeWidget = new QTreeWidget(this); +	unitsTreeWidget->setColumnCount(2); +	unitsTreeWidget->header()->setStretchLastSection(false); +	int closeIconWidth = unitsTreeWidget->fontMetrics().height(); +	unitsTreeWidget->header()->resizeSection(1, closeIconWidth); +	unitsTreeWidget->header()->setResizeMode(0, QHeaderView::Stretch); +	unitsTreeWidget->setHeaderHidden(true); +	unitsTreeWidget->setRootIsDecorated(false); +	unitsTreeWidget->setEditTriggers(QAbstractItemView::DoubleClicked); +	unitsTreeWidget->setItemDelegateForColumn(1, itemDelegate); +	connect(unitsTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), SLOT(handleItemChanged(QTreeWidgetItem*,int))); +	getGridLayout()->addWidget(unitsTreeWidget, getGridLayout()->rowCount()-1, 4, 2, 1); + +	QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << ""); +	item->setFlags(item->flags() | Qt::ItemIsEditable); +	unitsTreeWidget->addTopLevelItem(item); + +	getTagComboBox()->hide(); +	organizationLabel->hide(); +	childWidgets << organizationLabel << organizationLineEdit << unitsTreeWidget; +} + +bool QtVCardOrganizationField::isEmpty() const { +	return organizationLineEdit->text().isEmpty() && unitsTreeWidget->model()->rowCount() != 0; +} + +void QtVCardOrganizationField::setOrganization(const VCard::Organization& organization) { +	organizationLineEdit->setText(P2QSTRING(organization.name)); +	unitsTreeWidget->clear(); +	foreach(std::string unit, organization.units) { +		QTreeWidgetItem* item = new QTreeWidgetItem(QStringList(P2QSTRING(unit)) << ""); +		item->setFlags(item->flags() | Qt::ItemIsEditable); +		unitsTreeWidget->addTopLevelItem(item); +	} +} + +VCard::Organization QtVCardOrganizationField::getOrganization() const { +	VCard::Organization organization; +	organization.name = Q2PSTRING(organizationLineEdit->text()); +	for(int i=0; i < unitsTreeWidget->topLevelItemCount(); ++i) { +		QTreeWidgetItem* row = unitsTreeWidget->topLevelItem(i); +		if (!row->text(0).isEmpty()) { +			organization.units.push_back(Q2PSTRING(row->text(0))); +		} +	} + +	QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << ""); +	item->setFlags(item->flags() | Qt::ItemIsEditable); +	unitsTreeWidget->addTopLevelItem(item); + +	return organization; +} + +void QtVCardOrganizationField::handleEditibleChanged(bool isEditable) { +	if (organizationLineEdit) { +		organizationLineEdit->setVisible(isEditable); +		organizationLabel->setVisible(!isEditable); + +		if (!isEditable) { +			QString label; +			for(int i=0; i < unitsTreeWidget->topLevelItemCount(); ++i) { +				QTreeWidgetItem* row = unitsTreeWidget->topLevelItem(i); +				if (!row->text(0).isEmpty()) { +					label += row->text(0) + ", "; +				} +			} +			label += organizationLineEdit->text(); +			organizationLabel->setText(label); +		} +	} +	if (unitsTreeWidget) unitsTreeWidget->setVisible(isEditable); +} + +void QtVCardOrganizationField::handleItemChanged(QTreeWidgetItem *, int) { +	bool hasEmptyRow = false; +	QList<QTreeWidgetItem*> rows = unitsTreeWidget->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); +		unitsTreeWidget->addTopLevelItem(item); +	} +	getTagComboBox()->hide(); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.h b/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.h new file mode 100644 index 0000000..917e22a --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include <QTreeWidget> + +#include "QtRemovableItemDelegate.h" +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardOrganizationField : public QtVCardGeneralField { +	Q_OBJECT + +	public: +		GENERIC_QT_VCARD_FIELD_INFO("Organization", UNLIMITED_INSTANCES, QtVCardOrganizationField) + +		QtVCardOrganizationField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); +		virtual ~QtVCardOrganizationField(); + +		virtual bool isEmpty() const; + +		void setOrganization(const VCard::Organization& organization); +		VCard::Organization getOrganization() const; + +	protected: +		virtual void setupContentWidgets(); + +	public slots: +		void handleEditibleChanged(bool isEditable); + +	private slots: +		void handleItemChanged(QTreeWidgetItem*, int); + +	private: +		QLabel* organizationLabel; +		QtResizableLineEdit* organizationLineEdit; +		QTreeWidget* unitsTreeWidget; +		QtRemovableItemDelegate* itemDelegate; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.cpp b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.cpp new file mode 100644 index 0000000..3ddc86b --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardPhotoAndNameFields.h" +#include "ui_QtVCardPhotoAndNameFields.h" + +#include <QMenu> + +namespace Swift { + +QtVCardPhotoAndNameFields::QtVCardPhotoAndNameFields(QWidget* parent) : +	QWidget(parent), +	ui(new Ui::QtVCardPhotoAndNameFields) { +	ui->setupUi(this); +	ui->lineEditPREFIX->hide(); +	ui->lineEditMIDDLE->hide(); +	ui->lineEditSUFFIX->hide(); +	ui->lineEditFN->hide(); +	ui->lineEditNICKNAME->hide(); +	ui->labelFULLNAME->hide(); + +#if QT_VERSION >= 0x040700 +	ui->lineEditFN->setPlaceholderText(tr("Formatted Name")); +	ui->lineEditNICKNAME->setPlaceholderText(tr("Nickname")); +	ui->lineEditPREFIX->setPlaceholderText(tr("Prefix")); +	ui->lineEditGIVEN->setPlaceholderText(tr("Given Name")); +	ui->lineEditMIDDLE->setPlaceholderText(tr("Middle Name")); +	ui->lineEditFAMILY->setPlaceholderText(tr("Last Name")); +	ui->lineEditSUFFIX->setPlaceholderText(tr("Suffix")); +#endif + +	addFieldMenu = new QMenu("Name", this); + +	actionSignalMapper = new QSignalMapper(this); + +	connect(actionSignalMapper, SIGNAL(mapped(const QString &)), this, SLOT(showField(const QString &))); +	prepareAddFieldMenu(); +} + +QtVCardPhotoAndNameFields::~QtVCardPhotoAndNameFields() { +	delete ui; +	delete actionSignalMapper; +} + +bool QtVCardPhotoAndNameFields::isEditable() const { +	return editable; +} + +void QtVCardPhotoAndNameFields::setEditable(bool editable) { +	this->editable = editable; + +	ui->avatarWidget->setEditable(editable); +	ui->lineEditFN->setVisible(editable ? true : !ui->lineEditFN->text().isEmpty()); +	ui->lineEditFN->setEditable(editable); +	ui->lineEditFN->setStyleSheet(editable ? "" : "QLineEdit {border: none; background-color: transparent;}"); + +	ui->lineEditNICKNAME->setVisible(editable ? true : !ui->lineEditNICKNAME->text().isEmpty()); +	ui->lineEditNICKNAME->setEditable(editable); +	ui->lineEditNICKNAME->setStyleSheet(editable ? "" : "QLineEdit {border: none; background-color: transparent;}"); + +	// prefix given middle last suffix +	ui->lineEditPREFIX->setVisible(editable); +	ui->lineEditGIVEN->setVisible(editable); +	ui->lineEditMIDDLE->setVisible(editable); +	ui->lineEditFAMILY->setVisible(editable); +	ui->lineEditSUFFIX->setVisible(editable); +	ui->labelFULLNAME->setVisible(!editable); +	ui->labelFULLNAME->setText(	ui->lineEditPREFIX->text() + " " + ui->lineEditGIVEN->text() + " " + +								ui->lineEditMIDDLE->text() + " " + ui->lineEditFAMILY->text() + " " + ui->lineEditSUFFIX->text()); + +	prepareAddFieldMenu(); +} + +QMenu* QtVCardPhotoAndNameFields::getAddFieldMenu() const { +	return addFieldMenu; +} + +void QtVCardPhotoAndNameFields::setAvatar(const ByteArray &data, const std::string &type) { +	ui->avatarWidget->setAvatar(data, type); +} + +ByteArray QtVCardPhotoAndNameFields::getAvatarData() const { +	return ui->avatarWidget->getAvatarData(); +} + +std::string QtVCardPhotoAndNameFields::getAvatarType() const { +	return ui->avatarWidget->getAvatarType(); +} + +void QtVCardPhotoAndNameFields::setFormattedName(const QString formattedName) { +	ui->lineEditFN->setText(formattedName); +} + +QString QtVCardPhotoAndNameFields::getFormattedName() const { +	return ui->lineEditFN->text(); +} + +void QtVCardPhotoAndNameFields::setNickname(const QString nickname) { +	ui->lineEditNICKNAME->setText(nickname); +} + +QString QtVCardPhotoAndNameFields::getNickname() const { +	return ui->lineEditNICKNAME->text(); +} + +void QtVCardPhotoAndNameFields::setPrefix(const QString prefix) { +	ui->lineEditPREFIX->setText(prefix); +} + +QString QtVCardPhotoAndNameFields::getPrefix() const { +	return ui->lineEditPREFIX->text(); +} + +void QtVCardPhotoAndNameFields::setGivenName(const QString givenName) { +	ui->lineEditGIVEN->setText(givenName); +} + +QString QtVCardPhotoAndNameFields::getGivenName() const { +	return ui->lineEditGIVEN->text(); +} + +void QtVCardPhotoAndNameFields::setMiddleName(const QString middleName) { +	ui->lineEditMIDDLE->setText(middleName); +} + +QString QtVCardPhotoAndNameFields::getMiddleName() const { +	return ui->lineEditMIDDLE->text(); +} + +void QtVCardPhotoAndNameFields::setFamilyName(const QString familyName) { +	ui->lineEditFAMILY->setText(familyName); +} + +QString QtVCardPhotoAndNameFields::getFamilyName() const { +	return ui->lineEditFAMILY->text(); +} + +void QtVCardPhotoAndNameFields::setSuffix(const QString suffix) { +	ui->lineEditSUFFIX->setText(suffix); +} + +QString QtVCardPhotoAndNameFields::getSuffix() const { +	return ui->lineEditSUFFIX->text(); +} + +void QtVCardPhotoAndNameFields::prepareAddFieldMenu() { +	foreach(QAction* action, addFieldMenu->actions()) { +		actionSignalMapper->removeMappings(action); +	} + +	addFieldMenu->clear(); +	foreach(QObject* obj, children()) { +		QLineEdit* lineEdit = 0; +		if (!(lineEdit = dynamic_cast<QLineEdit*>(obj))) continue; +		if (lineEdit->isHidden()) { +#if QT_VERSION >= 0x040700 +			QAction* action = addFieldMenu->addAction(QString("Add ") + lineEdit->placeholderText(), actionSignalMapper, SLOT(map())); +#else +			QAction* action = addFieldMenu->addAction(QString("Add ") + lineEdit->toolTip(), actionSignalMapper, SLOT(map())); +#endif +			actionSignalMapper->setMapping(action, lineEdit->objectName()); +		} +	} +} + +void QtVCardPhotoAndNameFields::showField(const QString& widgetName) { +	QLineEdit* lineEditToShow = findChild<QLineEdit*>(widgetName); +	if (lineEditToShow) lineEditToShow->show(); + +	prepareAddFieldMenu(); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.h b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.h new file mode 100644 index 0000000..f279701 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QMenu> +#include <QSignalMapper> +#include <QWidget> +#include <Swiften/Base/ByteArray.h> + +namespace Ui { +	class QtVCardPhotoAndNameFields; +} + + +namespace Swift { + +	class QtVCardPhotoAndNameFields : public QWidget { +		Q_OBJECT +		Q_PROPERTY(bool editable READ isEditable WRITE setEditable) + +		public: +			explicit QtVCardPhotoAndNameFields(QWidget* parent = 0); +			~QtVCardPhotoAndNameFields(); + +			bool isEditable() const; +			void setEditable(bool); + +			QMenu* getAddFieldMenu() const; + +			void setAvatar(const ByteArray& data, const std::string& type); +			ByteArray getAvatarData() const; +			std::string getAvatarType() const; + +			void setFormattedName(const QString formattedName); +			QString getFormattedName() const; + +			void setNickname(const QString nickname); +			QString getNickname() const; + +			void setPrefix(const QString prefix); +			QString getPrefix() const; + +			void setGivenName(const QString givenName); +			QString getGivenName() const; + +			void setMiddleName(const QString middleName); +			QString getMiddleName() const; + +			void setFamilyName(const QString familyName); +			QString getFamilyName() const; + +			void setSuffix(const QString suffix); +			QString getSuffix() const; + +		public slots: +			void showField(const QString& widgetName); + +		private: +			void prepareAddFieldMenu(); + +		private: +			Ui::QtVCardPhotoAndNameFields* ui; +			bool editable; + +			QMenu* addFieldMenu; +			QSignalMapper* actionSignalMapper; +	}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.ui b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.ui new file mode 100644 index 0000000..04da2bc --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.ui @@ -0,0 +1,251 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtVCardPhotoAndNameFields</class> + <widget class="QWidget" name="QtVCardPhotoAndNameFields"> +  <property name="enabled"> +   <bool>true</bool> +  </property> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>522</width> +    <height>81</height> +   </rect> +  </property> +  <property name="sizePolicy"> +   <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> +    <horstretch>0</horstretch> +    <verstretch>0</verstretch> +   </sizepolicy> +  </property> +  <property name="windowTitle"> +   <string>Form</string> +  </property> +  <layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0" rowminimumheight="0,0,0,0,0"> +   <property name="sizeConstraint"> +    <enum>QLayout::SetMinimumSize</enum> +   </property> +   <property name="horizontalSpacing"> +    <number>5</number> +   </property> +   <property name="verticalSpacing"> +    <number>1</number> +   </property> +   <property name="margin"> +    <number>0</number> +   </property> +   <item row="0" column="0" rowspan="5"> +    <widget class="Swift::QtAvatarWidget" name="avatarWidget" native="true"> +     <property name="sizePolicy"> +      <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> +       <horstretch>0</horstretch> +       <verstretch>0</verstretch> +      </sizepolicy> +     </property> +     <property name="text" stdset="0"> +      <string/> +     </property> +     <property name="flat" stdset="0"> +      <bool>false</bool> +     </property> +    </widget> +   </item> +   <item row="0" column="1"> +    <layout class="QHBoxLayout" name="horizontalLayout_2"> +     <item> +      <widget class="Swift::QtResizableLineEdit" name="lineEditFN"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="font"> +        <font> +         <pointsize>18</pointsize> +         <weight>50</weight> +         <bold>false</bold> +        </font> +       </property> +       <property name="toolTip"> +        <string>Formatted Name</string> +       </property> +      </widget> +     </item> +     <item> +      <spacer name="horizontalSpacer_2"> +       <property name="orientation"> +        <enum>Qt::Horizontal</enum> +       </property> +       <property name="sizeHint" stdset="0"> +        <size> +         <width>40</width> +         <height>20</height> +        </size> +       </property> +      </spacer> +     </item> +    </layout> +   </item> +   <item row="1" column="1"> +    <layout class="QHBoxLayout" name="horizontalLayout_3"> +     <item> +      <widget class="Swift::QtResizableLineEdit" name="lineEditNICKNAME"> +       <property name="toolTip"> +        <string>Nickname</string> +       </property> +       <property name="frame"> +        <bool>true</bool> +       </property> +      </widget> +     </item> +     <item> +      <spacer name="horizontalSpacer_3"> +       <property name="orientation"> +        <enum>Qt::Horizontal</enum> +       </property> +       <property name="sizeHint" stdset="0"> +        <size> +         <width>40</width> +         <height>20</height> +        </size> +       </property> +      </spacer> +     </item> +    </layout> +   </item> +   <item row="2" column="1"> +    <layout class="QHBoxLayout" name="horizontalLayout"> +     <property name="spacing"> +      <number>2</number> +     </property> +     <property name="sizeConstraint"> +      <enum>QLayout::SetMinimumSize</enum> +     </property> +     <item> +      <widget class="QLabel" name="labelFULLNAME"> +       <property name="text"> +        <string/> +       </property> +       <property name="textInteractionFlags"> +        <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> +       </property> +      </widget> +     </item> +     <item> +      <widget class="Swift::QtResizableLineEdit" name="lineEditPREFIX"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="toolTip"> +        <string>Prefix</string> +       </property> +      </widget> +     </item> +     <item> +      <widget class="Swift::QtResizableLineEdit" name="lineEditGIVEN"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="toolTip"> +        <string>Given Name</string> +       </property> +       <property name="text"> +        <string/> +       </property> +       <property name="readOnly"> +        <bool>false</bool> +       </property> +      </widget> +     </item> +     <item> +      <widget class="Swift::QtResizableLineEdit" name="lineEditMIDDLE"> +       <property name="enabled"> +        <bool>true</bool> +       </property> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="toolTip"> +        <string>Middle Name</string> +       </property> +       <property name="styleSheet"> +        <string notr="true"/> +       </property> +       <property name="frame"> +        <bool>true</bool> +       </property> +      </widget> +     </item> +     <item> +      <widget class="Swift::QtResizableLineEdit" name="lineEditFAMILY"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="toolTip"> +        <string>Last Name</string> +       </property> +      </widget> +     </item> +     <item> +      <widget class="Swift::QtResizableLineEdit" name="lineEditSUFFIX"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="toolTip"> +        <string>Suffix</string> +       </property> +       <property name="readOnly"> +        <bool>false</bool> +       </property> +      </widget> +     </item> +     <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> +    </layout> +   </item> +  </layout> + </widget> + <customwidgets> +  <customwidget> +   <class>Swift::QtResizableLineEdit</class> +   <extends>QLineEdit</extends> +   <header>QtResizableLineEdit.h</header> +  </customwidget> +  <customwidget> +   <class>Swift::QtAvatarWidget</class> +   <extends>QWidget</extends> +   <header>Swift/QtUI/QtAvatarWidget.h</header> +   <container>1</container> +  </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtVCardWidget/QtVCardRoleField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardRoleField.cpp new file mode 100644 index 0000000..8af4e64 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardRoleField.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardRoleField.h" + +#include <QGridLayout> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardRoleField::QtVCardRoleField(QWidget* parent, QGridLayout *layout, bool editable) : +	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Role"), false, false) { +	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardRoleField::~QtVCardRoleField() { +} + +void QtVCardRoleField::setupContentWidgets() { +	roleLineEdit = new QtResizableLineEdit(this); +	getGridLayout()->addWidget(roleLineEdit, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); +	getTagComboBox()->hide(); +	childWidgets << roleLineEdit; +} + +bool QtVCardRoleField::isEmpty() const { +	return roleLineEdit->text().isEmpty(); +} + +void QtVCardRoleField::setRole(const std::string& role) { +	roleLineEdit->setText(P2QSTRING(role)); +} + +std::string QtVCardRoleField::getRole() const { +	return Q2PSTRING(roleLineEdit->text()); +} + +void QtVCardRoleField::handleEditibleChanged(bool isEditable) { +	if (roleLineEdit) { +		roleLineEdit->setEditable(isEditable); +		roleLineEdit->setStyleSheet(isEditable ? "" : "QLineEdit { border: none; background: transparent; }"); +	} +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardRoleField.h b/Swift/QtUI/QtVCardWidget/QtVCardRoleField.h new file mode 100644 index 0000000..3c819ed --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardRoleField.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardRoleField : public QtVCardGeneralField { +	Q_OBJECT + +	public: +		GENERIC_QT_VCARD_FIELD_INFO("Role", UNLIMITED_INSTANCES, QtVCardRoleField) + +		QtVCardRoleField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); +		virtual ~QtVCardRoleField(); + +		virtual bool isEmpty() const; + +		void setRole(const std::string& role); +		std::string getRole() const; + +	protected: +		virtual void setupContentWidgets(); + +	public slots: +		void handleEditibleChanged(bool isEditable); + +	private: +		QtResizableLineEdit* roleLineEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.cpp new file mode 100644 index 0000000..ee93c01 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardTelephoneField.h" + +#include <QGridLayout> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardTelephoneField::QtVCardTelephoneField(QWidget* parent, QGridLayout *layout, bool editable) : +	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Telephone")) { +	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardTelephoneField::~QtVCardTelephoneField() { +	disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardTelephoneField::setupContentWidgets() { +	telephoneLineEdit = new QtResizableLineEdit(this); +#if QT_VERSION >= 0x040700 +	telephoneLineEdit->setPlaceholderText(tr("0118 999 881 999 119 7253")); +#endif +	getGridLayout()->addWidget(telephoneLineEdit, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); +	setTabOrder(telephoneLineEdit, getTagComboBox()); +	QtVCardHomeWork::setTagComboBox(getTagComboBox()); + +	getTagComboBox()->addTag("voice", QObject::tr("Voice")); +	getTagComboBox()->addTag("fax",	QObject::tr("Fax")); +	getTagComboBox()->addTag("pager", QObject::tr("Pager")); +	getTagComboBox()->addTag("msg",	QObject::tr("Voice Messaging")); +	getTagComboBox()->addTag("cell", QObject::tr("Cell")); +	getTagComboBox()->addTag("video", QObject::tr("Video")); +	getTagComboBox()->addTag("bbs",	QObject::tr("Bulletin Board System")); +	getTagComboBox()->addTag("modem", QObject::tr("Modem")); +	getTagComboBox()->addTag("isdn", QObject::tr("ISDN")); +	getTagComboBox()->addTag("pcs",	QObject::tr("Personal Communication Services")); + +	childWidgets << telephoneLineEdit; +} + +bool QtVCardTelephoneField::isEmpty() const { +	return telephoneLineEdit->text().isEmpty(); +} + +void QtVCardTelephoneField::setTelephone(const VCard::Telephone& telephone) { +	setPreferred(telephone.isPreferred); +	setHome(telephone.isHome); +	setWork(telephone.isWork); + +	telephoneLineEdit->setText(P2QSTRING(telephone.number)); + +	getTagComboBox()->setTag("voice", telephone.isVoice); +	getTagComboBox()->setTag("fax", telephone.isFax); +	getTagComboBox()->setTag("pager", telephone.isPager); +	getTagComboBox()->setTag("msg", telephone.isMSG); +	getTagComboBox()->setTag("cell", telephone.isCell); +	getTagComboBox()->setTag("video", telephone.isVideo); +	getTagComboBox()->setTag("bbs", telephone.isBBS); +	getTagComboBox()->setTag("modem", telephone.isModem); +	getTagComboBox()->setTag("isdn", telephone.isISDN); +	getTagComboBox()->setTag("pcs", telephone.isPCS); +} + +VCard::Telephone QtVCardTelephoneField::getTelephone() const { +	VCard::Telephone telephone; + +	telephone.number = Q2PSTRING(telephoneLineEdit->text()); + +	telephone.isPreferred = getPreferred(); +	telephone.isHome = getHome(); +	telephone.isWork = getWork(); + +	telephone.isVoice = getTagComboBox()->isTagSet("voice"); +	telephone.isFax = getTagComboBox()->isTagSet("fax"); +	telephone.isPager = getTagComboBox()->isTagSet("pager"); +	telephone.isMSG = getTagComboBox()->isTagSet("msg"); +	telephone.isCell = getTagComboBox()->isTagSet("cell"); +	telephone.isVideo = getTagComboBox()->isTagSet("video"); +	telephone.isBBS = getTagComboBox()->isTagSet("bbs"); +	telephone.isModem = getTagComboBox()->isTagSet("modem"); +	telephone.isISDN = getTagComboBox()->isTagSet("isdn"); +	telephone.isPCS = getTagComboBox()->isTagSet("pcs"); + +	return telephone; +} + +void QtVCardTelephoneField::handleEditibleChanged(bool isEditable) { +	if (telephoneLineEdit) { +		telephoneLineEdit->setEditable(isEditable); +		telephoneLineEdit->setStyleSheet(isEditable ? "" : "QLineEdit { border: none; background: transparent; }"); +	} +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.h b/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.h new file mode 100644 index 0000000..b433e3c --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" +#include "QtVCardHomeWork.h" + +namespace Swift { + +class QtVCardTelephoneField : public QtVCardGeneralField, public QtVCardHomeWork { +	Q_OBJECT + +	public: +		GENERIC_QT_VCARD_FIELD_INFO("Telephone", UNLIMITED_INSTANCES, QtVCardTelephoneField) + +		QtVCardTelephoneField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); +		virtual ~QtVCardTelephoneField(); + +		virtual bool isEmpty() const; + +		void setTelephone(const VCard::Telephone& telephone); +		VCard::Telephone getTelephone() const; + +	protected: +		virtual void setupContentWidgets(); + +	public slots: +		void handleEditibleChanged(bool isEditable); + +	private: +		QtResizableLineEdit* telephoneLineEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardTitleField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardTitleField.cpp new file mode 100644 index 0000000..aac4e31 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardTitleField.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardTitleField.h" + +#include <QGridLayout> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardTitleField::QtVCardTitleField(QWidget* parent, QGridLayout *layout, bool editable) : +	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Title"), false, false) { +	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardTitleField::~QtVCardTitleField() { +	disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardTitleField::setupContentWidgets() { +	titleLineEdit = new QtResizableLineEdit(this); +	getGridLayout()->addWidget(titleLineEdit, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); +	getTagComboBox()->hide(); +	childWidgets << titleLineEdit; +} + +bool QtVCardTitleField::isEmpty() const { +	return titleLineEdit->text().isEmpty(); +} + +void QtVCardTitleField::setTitle(const std::string& title) { +	titleLineEdit->setText(P2QSTRING(title)); +} + +std::string QtVCardTitleField::getTitle() const { +	return Q2PSTRING(titleLineEdit->text()); +} + +void QtVCardTitleField::handleEditibleChanged(bool isEditable) { +	if (titleLineEdit) { +		titleLineEdit->setEditable(isEditable); +		titleLineEdit->setStyleSheet(isEditable ? "" : "QLineEdit { border: none; background: transparent; }"); +	} +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardTitleField.h b/Swift/QtUI/QtVCardWidget/QtVCardTitleField.h new file mode 100644 index 0000000..28dc603 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardTitleField.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardTitleField : public QtVCardGeneralField { +	Q_OBJECT + +	public: +		GENERIC_QT_VCARD_FIELD_INFO("Title", UNLIMITED_INSTANCES, QtVCardTitleField) + +		QtVCardTitleField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); +		virtual ~QtVCardTitleField(); + +		virtual bool isEmpty() const; + +		void setTitle(const std::string& title); +		std::string getTitle() const; + +	protected: +		virtual void setupContentWidgets(); + +	public slots: +		void handleEditibleChanged(bool isEditable); + +	private: +		QtResizableLineEdit* titleLineEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardURLField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardURLField.cpp new file mode 100644 index 0000000..0b6f0c1 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardURLField.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardURLField.h" + +#include <QGridLayout> +#include <QHBoxLayout> +#include <QTextDocument> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardURLField::QtVCardURLField(QWidget* parent, QGridLayout *layout, bool editable) : +	QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("URL"), false, false) { +	connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardURLField::~QtVCardURLField() { +	disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardURLField::setupContentWidgets() { +	urlLabel = new QLabel(this); +	urlLabel->setOpenExternalLinks(true); +	urlLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); +	urlLineEdit = new QtResizableLineEdit(this); + +	QHBoxLayout* urlLayout = new QHBoxLayout(); +	urlLayout->addWidget(urlLabel); +	urlLayout->addWidget(urlLineEdit); + +	getGridLayout()->addLayout(urlLayout, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); +	getTagComboBox()->hide(); +	urlLabel->hide(); +	childWidgets << urlLabel << urlLineEdit; +} + +bool QtVCardURLField::isEmpty() const { +	return urlLineEdit->text().isEmpty(); +} + +void QtVCardURLField::setURL(const std::string& url) { +	urlLineEdit->setText(P2QSTRING(url)); +} + +std::string QtVCardURLField::getURL() const { +	return Q2PSTRING(urlLineEdit->text()); +} + +void QtVCardURLField::handleEditibleChanged(bool isEditable) { +	if (isEditable) { +		if (urlLineEdit) urlLineEdit->show(); +		if (urlLabel) urlLabel->hide(); +	} else { +		if (urlLineEdit) urlLineEdit->hide(); +		if (urlLabel) { +			urlLabel->setText(QString("<a href=\"%1\">%1</a>").arg(Qt::escape(urlLineEdit->text()))); +			urlLabel->show(); +		} +	} +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardURLField.h b/Swift/QtUI/QtVCardWidget/QtVCardURLField.h new file mode 100644 index 0000000..2c011d8 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardURLField.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardURLField : public QtVCardGeneralField { +	Q_OBJECT + +	public: +		GENERIC_QT_VCARD_FIELD_INFO("URL", UNLIMITED_INSTANCES, QtVCardURLField) + +		QtVCardURLField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); +		virtual ~QtVCardURLField(); + +		virtual bool isEmpty() const; + +		void setURL(const std::string& url); +		std::string getURL() const; + +	protected: +		virtual void setupContentWidgets(); + +	public slots: +		void handleEditibleChanged(bool isEditable); + +	private: +		QLabel* urlLabel; +		QtResizableLineEdit* urlLineEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp b/Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp new file mode 100644 index 0000000..1c80fa4 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardWidget.h" +#include "ui_QtVCardWidget.h" + +#include <QDebug> +#include <QLineEdit> +#include <QMenu> + +#include "QtVCardAddressField.h" +#include "QtVCardAddressLabelField.h" +#include "QtVCardBirthdayField.h" +#include "QtVCardDescriptionField.h" +#include "QtVCardGeneralField.h" +#include "QtVCardInternetEMailField.h" +#include "QtVCardJIDField.h" +#include "QtVCardOrganizationField.h" +#include "QtVCardRoleField.h" +#include "QtVCardTelephoneField.h" +#include "QtVCardTitleField.h" +#include "QtVCardURLField.h" + +#include <Swift/QtUI/QtSwiftUtil.h> + +#include <Swiften/Base/Log.h> + +namespace Swift { + +QtVCardWidget::QtVCardWidget(QWidget* parent) : +	QWidget(parent), +	ui(new ::Ui::QtVCardWidget) { +	ui->setupUi(this); + +	ui->cardFields->setColumnStretch(0,0); +	ui->cardFields->setColumnStretch(1,0); +	ui->cardFields->setColumnStretch(2,2); +	ui->cardFields->setColumnStretch(3,1); +	ui->cardFields->setColumnStretch(4,2); +	menu = new QMenu(this); + +	menu->addMenu(ui->photoAndName->getAddFieldMenu()); +	ui->toolButton->setMenu(menu); + +	addFieldType(menu, boost::make_shared<QtVCardInternetEMailField::FieldInfo>()); +	addFieldType(menu, boost::make_shared<QtVCardTelephoneField::FieldInfo>()); +	addFieldType(menu, boost::make_shared<QtVCardAddressField::FieldInfo>()); +	addFieldType(menu, boost::make_shared<QtVCardAddressLabelField::FieldInfo>()); +	addFieldType(menu, boost::make_shared<QtVCardBirthdayField::FieldInfo>()); +	addFieldType(menu, boost::make_shared<QtVCardJIDField::FieldInfo>()); +	addFieldType(menu, boost::make_shared<QtVCardDescriptionField::FieldInfo>()); +	addFieldType(menu, boost::make_shared<QtVCardRoleField::FieldInfo>()); +	addFieldType(menu, boost::make_shared<QtVCardTitleField::FieldInfo>()); +	addFieldType(menu, boost::make_shared<QtVCardOrganizationField::FieldInfo>()); +	addFieldType(menu, boost::make_shared<QtVCardURLField::FieldInfo>()); + +	setEditable(false); +} + +QtVCardWidget::~QtVCardWidget() { +	delete ui; +} + +bool QtVCardWidget::isEditable() const { +	return editable; +} + +void QtVCardWidget::setEditable(bool editable) { +	this->editable = editable; + +	ui->photoAndName->setProperty("editable", QVariant(editable)); + +	foreach(QtVCardGeneralField* field, fields) { +		field->setEditable(editable); +	} + +	if (editable) { +		ui->toolButton->show(); +		//if ((findChild<QtVCardBirthdayField*>() == 0)) { +		//} +	} else { +		ui->toolButton->hide(); +	} + +	editableChanged(editable); +} + +void QtVCardWidget::setVCard(VCard::ref vcard) { +	SWIFT_LOG(debug) << std::endl; +	clearFields(); +	this->vcard = vcard; +	ui->photoAndName->setFormattedName(P2QSTRING(vcard->getFullName())); +	ui->photoAndName->setNickname(P2QSTRING(vcard->getNickname())); +	ui->photoAndName->setPrefix(P2QSTRING(vcard->getPrefix())); +	ui->photoAndName->setGivenName(P2QSTRING(vcard->getGivenName())); +	ui->photoAndName->setMiddleName(P2QSTRING(vcard->getMiddleName())); +	ui->photoAndName->setFamilyName(P2QSTRING(vcard->getFamilyName())); +	ui->photoAndName->setSuffix(P2QSTRING(vcard->getSuffix())); +	ui->photoAndName->setAvatar(vcard->getPhoto(), vcard->getPhotoType()); + +	foreach (const VCard::EMailAddress& address, vcard->getEMailAddresses()) { +		if (address.isInternet) { +			QtVCardInternetEMailField* internetEmailField = new QtVCardInternetEMailField(this, ui->cardFields); +			internetEmailField->initialize(); +			internetEmailField->setInternetEMailAddress(address); +			appendField(internetEmailField); +		} +	} + +	foreach (const VCard::Telephone& telephone, vcard->getTelephones()) { +		QtVCardTelephoneField* telField = new QtVCardTelephoneField(this, ui->cardFields); +		telField->initialize(); +		telField->setTelephone(telephone); +		appendField(telField); +	} + +	foreach (const VCard::Address& address, vcard->getAddresses()) { +		QtVCardAddressField* addressField = new QtVCardAddressField(this, ui->cardFields); +		addressField->initialize(); +		addressField->setAddress(address); +		appendField(addressField); +	} + +	foreach (const VCard::AddressLabel& label, vcard->getAddressLabels()) { +		QtVCardAddressLabelField* addressLabelField = new QtVCardAddressLabelField(this, ui->cardFields); +		addressLabelField->initialize(); +		addressLabelField->setAddressLabel(label); +		appendField(addressLabelField); +	} + +	if (!vcard->getBirthday().is_not_a_date_time()) { +		QtVCardBirthdayField* bdayField = new QtVCardBirthdayField(this, ui->cardFields); +		bdayField->initialize(); +		bdayField->setBirthday(vcard->getBirthday()); +		appendField(bdayField); +	} + +	foreach (const JID& jid, vcard->getJIDs()) { +		QtVCardJIDField* jidField = new QtVCardJIDField(this, ui->cardFields); +		jidField->initialize(); +		jidField->setJID(jid); +		appendField(jidField); +	} + +	if (!vcard->getDescription().empty()) { +		QtVCardDescriptionField* descField = new QtVCardDescriptionField(this, ui->cardFields); +		descField->initialize(); +		descField->setDescription(vcard->getDescription()); +		appendField(descField); +	} + +	foreach (const VCard::Organization& org, vcard->getOrganizations()) { +		QtVCardOrganizationField* orgField = new QtVCardOrganizationField(this, ui->cardFields); +		orgField->initialize(); +		orgField->setOrganization(org); +		appendField(orgField); +	} + +	foreach (const std::string& role, vcard->getRoles()) { +		QtVCardRoleField* roleField = new QtVCardRoleField(this, ui->cardFields); +		roleField->initialize(); +		roleField->setRole(role); +		appendField(roleField); +	} + +	foreach (const std::string& title, vcard->getTitles()) { +		QtVCardTitleField* titleField = new QtVCardTitleField(this, ui->cardFields); +		titleField->initialize(); +		titleField->setTitle(title); +		appendField(titleField); +	} + +	foreach (const std::string& url, vcard->getURLs()) { +		QtVCardURLField* urlField = new QtVCardURLField(this, ui->cardFields); +		urlField->initialize(); +		urlField->setURL(url); +		appendField(urlField); +	} + +	setEditable(editable); +} + +VCard::ref QtVCardWidget::getVCard() { +	SWIFT_LOG(debug) << std::endl; +	clearEmptyFields(); +	vcard->setFullName(Q2PSTRING(ui->photoAndName->getFormattedName())); +	vcard->setNickname(Q2PSTRING(ui->photoAndName->getNickname())); +	vcard->setPrefix(Q2PSTRING(ui->photoAndName->getPrefix())); +	vcard->setGivenName(Q2PSTRING(ui->photoAndName->getGivenName())); +	vcard->setMiddleName(Q2PSTRING(ui->photoAndName->getMiddleName())); +	vcard->setFamilyName(Q2PSTRING(ui->photoAndName->getFamilyName())); +	vcard->setSuffix(Q2PSTRING(ui->photoAndName->getSuffix())); +	vcard->setPhoto(ui->photoAndName->getAvatarData()); +	vcard->setPhotoType(ui->photoAndName->getAvatarType()); + +	vcard->clearEMailAddresses(); +	vcard->clearJIDs(); +	vcard->clearURLs(); +	vcard->clearTelephones(); +	vcard->clearRoles(); +	vcard->clearTitles(); +	vcard->clearOrganizations(); +	vcard->clearAddresses(); +	vcard->clearAddressLabels(); + + +	QtVCardBirthdayField* bdayField = NULL; +	QtVCardDescriptionField* descriptionField = NULL; + +	foreach(QtVCardGeneralField* field, fields) { +		QtVCardInternetEMailField* emailField; +		if ((emailField = dynamic_cast<QtVCardInternetEMailField*>(field))) { +			vcard->addEMailAddress(emailField->getInternetEMailAddress()); +			continue; +		} + +		QtVCardTelephoneField* telephoneField; +		if ((telephoneField = dynamic_cast<QtVCardTelephoneField*>(field))) { +			vcard->addTelephone(telephoneField->getTelephone()); +			continue; +		} + +		QtVCardAddressField* addressField; +		if ((addressField = dynamic_cast<QtVCardAddressField*>(field))) { +			vcard->addAddress(addressField->getAddress()); +			continue; +		} + +		QtVCardAddressLabelField* addressLabelField; +		if ((addressLabelField = dynamic_cast<QtVCardAddressLabelField*>(field))) { +			vcard->addAddressLabel(addressLabelField->getAddressLabel()); +			continue; +		} + +		if ((bdayField = dynamic_cast<QtVCardBirthdayField*>(field))) { +			continue; +		} + +		QtVCardJIDField* jidField; +		if ((jidField = dynamic_cast<QtVCardJIDField*>(field))) { +			vcard->addJID(jidField->getJID()); +			continue; +		} + +		if ((descriptionField = dynamic_cast<QtVCardDescriptionField*>(field))) { +			continue; +		} + +		QtVCardOrganizationField* orgField; +		if ((orgField = dynamic_cast<QtVCardOrganizationField*>(field))) { +			vcard->addOrganization(orgField->getOrganization()); +			continue; +		} + +		QtVCardRoleField* roleField; +		if ((roleField = dynamic_cast<QtVCardRoleField*>(field))) { +			vcard->addRole(roleField->getRole()); +			continue; +		} + +		QtVCardTitleField* titleField; +		if ((titleField = dynamic_cast<QtVCardTitleField*>(field))) { +			vcard->addTitle(titleField->getTitle()); +			continue; +		} + +		QtVCardURLField* urlField; +		if ((urlField = dynamic_cast<QtVCardURLField*>(field))) { +			vcard->addURL(urlField->getURL()); +			continue; +		} +	} + +	if (bdayField) { +		vcard->setBirthday(bdayField->getBirthday()); +	} else { +		vcard->setBirthday(boost::posix_time::ptime()); +	} + +	if (descriptionField) { +		vcard->setDescription(descriptionField->getDescription()); +	} else { +		vcard->setDescription(""); +	} + +	return vcard; +} + +void QtVCardWidget::addField() { +	QAction* action = NULL; +	if ((action = dynamic_cast<QAction*>(sender()))) { +		boost::shared_ptr<QtVCardFieldInfo> fieldInfo = actionFieldInfo[action]; +		QWidget* newField = fieldInfo->createFieldInstance(this, ui->cardFields, true); +		QtVCardGeneralField* newGeneralField = dynamic_cast<QtVCardGeneralField*>(newField); +		if (newGeneralField) { +			newGeneralField->initialize(); +		} +		appendField(newGeneralField); +	} +} + +void QtVCardWidget::removeField(QtVCardGeneralField *field) { +	fields.remove(field); +	delete field; +} + +void QtVCardWidget::addFieldType(QMenu* menu, boost::shared_ptr<QtVCardFieldInfo> fieldType) { +	QAction* action = new QAction(tr("Add ") + fieldType->getMenuName(), this); +	actionFieldInfo[action] = fieldType; +	connect(action, SIGNAL(triggered()), this, SLOT(addField())); +	menu->addAction(action); +} + +int QtVCardWidget::fieldTypeInstances(boost::shared_ptr<QtVCardFieldInfo> fieldType) { +	int instances = 0; +	for (int n = 0; n < ui->cardFields->count(); n++) { +		if (fieldType->testInstance(ui->cardFields->itemAt(n)->widget())) instances++; +	} +	return instances; +} + +void layoutDeleteChildren(QLayout *layout) { +	while(layout->count() > 0) { +		QLayoutItem* child; +		if ((child = layout->takeAt(0)) != 0) { +			if (child->layout()) { +				layoutDeleteChildren(child->layout()); +			} +			delete child->widget(); +			delete child; +		} +	} +} + +void QtVCardWidget::clearFields() { +	foreach(QtVCardGeneralField* field, fields) { +		delete field; +	} +	fields.clear(); + +	assert(ui->cardFields->count() >= 0); +	layoutDeleteChildren(ui->cardFields); +} + +void QtVCardWidget::clearEmptyFields() { +	std::vector<QtVCardGeneralField*> items_to_remove; +	foreach (QtVCardGeneralField* field, fields) { +		if (field->property("empty").isValid() && field->property("empty").toBool()) { +			ui->cardFields->removeWidget(field); +			items_to_remove.push_back(field); +			delete field; +		} +	} + +	foreach(QtVCardGeneralField* field, items_to_remove) { +		fields.remove(field); +	} +} + +void QtVCardWidget::appendField(QtVCardGeneralField *field) { +	connect(field, SIGNAL(deleteField(QtVCardGeneralField*)), SLOT(removeField(QtVCardGeneralField*))); +	fields.push_back(field); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardWidget.h b/Swift/QtUI/QtVCardWidget/QtVCardWidget.h new file mode 100644 index 0000000..07f528d --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardWidget.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QWidget> +#include <Swiften/Elements/VCard.h> +#include <boost/smart_ptr/make_shared.hpp> + +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" +#include "QtVCardPhotoAndNameFields.h" + +namespace Ui { +	class QtVCardWidget; +} + +namespace Swift { + +	class QtVCardWidget : public QWidget { +		Q_OBJECT +		Q_PROPERTY(bool editable READ isEditable WRITE setEditable) + +		public : +			explicit QtVCardWidget(QWidget* parent = 0); +			~QtVCardWidget(); + +			bool isEditable() const; +			void setEditable(bool); + +			void setVCard(VCard::ref vcard); +			VCard::ref getVCard(); + +		signals: +			void editableChanged(bool editable); + +		private slots: +			void addField(); +			void removeField(QtVCardGeneralField* field); + +		private: +			void addFieldType(QMenu*, boost::shared_ptr<QtVCardFieldInfo>); +			int fieldTypeInstances(boost::shared_ptr<QtVCardFieldInfo>); +			void clearFields(); +			void clearEmptyFields(); +			void appendField(QtVCardGeneralField* field); + +		private: +			VCard::ref vcard; +			Ui::QtVCardWidget* ui; +			bool editable; +			QMenu* menu; +			std::list<QtVCardGeneralField*> fields; +			std::map<QAction*, boost::shared_ptr<QtVCardFieldInfo> > actionFieldInfo; +	}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardWidget.ui b/Swift/QtUI/QtVCardWidget/QtVCardWidget.ui new file mode 100644 index 0000000..eae1006 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardWidget.ui @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtVCardWidget</class> + <widget class="QWidget" name="QtVCardWidget"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>535</width> +    <height>126</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Form</string> +  </property> +  <layout class="QGridLayout" name="gridLayout" rowstretch="1,0" columnstretch="1,0"> +   <property name="margin"> +    <number>5</number> +   </property> +   <item row="0" column="0" colspan="2"> +    <layout class="QVBoxLayout" name="card" stretch="0,0,1"> +     <property name="spacing"> +      <number>2</number> +     </property> +     <property name="sizeConstraint"> +      <enum>QLayout::SetDefaultConstraint</enum> +     </property> +     <item> +      <widget class="Swift::QtVCardPhotoAndNameFields" name="photoAndName" native="true"/> +     </item> +     <item> +      <widget class="Line" name="line"> +       <property name="orientation"> +        <enum>Qt::Horizontal</enum> +       </property> +      </widget> +     </item> +     <item> +      <widget class="QScrollArea" name="scrollArea"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="frameShape"> +        <enum>QFrame::NoFrame</enum> +       </property> +       <property name="frameShadow"> +        <enum>QFrame::Plain</enum> +       </property> +       <property name="horizontalScrollBarPolicy"> +        <enum>Qt::ScrollBarAlwaysOff</enum> +       </property> +       <property name="widgetResizable"> +        <bool>true</bool> +       </property> +       <property name="alignment"> +        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> +       </property> +       <widget class="QWidget" name="scrollAreaWidgetContents"> +        <property name="geometry"> +         <rect> +          <x>0</x> +          <y>0</y> +          <width>523</width> +          <height>76</height> +         </rect> +        </property> +        <property name="sizePolicy"> +         <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> +          <horstretch>0</horstretch> +          <verstretch>0</verstretch> +         </sizepolicy> +        </property> +        <layout class="QVBoxLayout" name="verticalLayout"> +         <property name="sizeConstraint"> +          <enum>QLayout::SetMinAndMaxSize</enum> +         </property> +         <property name="margin"> +          <number>0</number> +         </property> +         <item> +          <layout class="QGridLayout" name="cardFields"> +          </layout> +         </item> +         <item> +          <spacer name="verticalSpacer"> +           <property name="orientation"> +            <enum>Qt::Vertical</enum> +           </property> +           <property name="sizeType"> +            <enum>QSizePolicy::Preferred</enum> +           </property> +           <property name="sizeHint" stdset="0"> +            <size> +             <width>20</width> +             <height>1000</height> +            </size> +           </property> +          </spacer> +         </item> +        </layout> +       </widget> +      </widget> +     </item> +    </layout> +   </item> +   <item row="1" column="1"> +    <widget class="QToolButton" name="toolButton"> +     <property name="text"> +      <string>Add Field</string> +     </property> +     <property name="popupMode"> +      <enum>QToolButton::InstantPopup</enum> +     </property> +     <property name="autoRaise"> +      <bool>false</bool> +     </property> +     <property name="arrowType"> +      <enum>Qt::NoArrow</enum> +     </property> +    </widget> +   </item> +  </layout> + </widget> + <customwidgets> +  <customwidget> +   <class>Swift::QtVCardPhotoAndNameFields</class> +   <extends>QWidget</extends> +   <header>QtVCardPhotoAndNameFields.h</header> +   <container>1</container> +  </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtWebView.cpp b/Swift/QtUI/QtWebView.cpp index 388f06a..1222ee2 100644 --- a/Swift/QtUI/QtWebView.cpp +++ b/Swift/QtUI/QtWebView.cpp @@ -9,6 +9,7 @@  #include <QKeyEvent>  #include <QFocusEvent> +#include <boost/numeric/conversion/cast.hpp>  #include <QMenu>  namespace Swift { @@ -30,7 +31,7 @@ void QtWebView::keyPressEvent(QKeyEvent* event) {  							   modifiers,  							   event->text(),  							   event->isAutoRepeat(), -							   event->count()); +							   boost::numeric_cast<unsigned short>(event->count()));  	QWebView::keyPressEvent(translatedEvent);  	delete translatedEvent;  } diff --git a/Swift/QtUI/QtXMLConsoleWidget.cpp b/Swift/QtUI/QtXMLConsoleWidget.cpp index e674df8..284f3c3 100644 --- a/Swift/QtUI/QtXMLConsoleWidget.cpp +++ b/Swift/QtUI/QtXMLConsoleWidget.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -96,7 +96,11 @@ void QtXMLConsoleWidget::appendTextIfEnabled(const std::string& data, const QCol  		cursor.insertText(P2QSTRING(data));  		cursor.endEditBlock(); -		if (scrollToBottom) { +		// Checking for the scrollbar again, because it could have appeared after inserting text. +		// In practice, I suspect that the scrollbar is always there, but hidden, but since we were +		// explicitly testing for this already above, I'm leaving the code in. +		scrollBar = textEdit->verticalScrollBar(); +		if (scrollToBottom && scrollBar) {  			scrollBar->setValue(scrollBar->maximum());  		}  	} diff --git a/Swift/QtUI/Roster/QtOccupantListWidget.cpp b/Swift/QtUI/Roster/QtOccupantListWidget.cpp index 5d26c46..12dc1e4 100644 --- a/Swift/QtUI/Roster/QtOccupantListWidget.cpp +++ b/Swift/QtUI/Roster/QtOccupantListWidget.cpp @@ -58,6 +58,7 @@ void QtOccupantListWidget::contextMenuEvent(QContextMenuEvent* event) {  					case ChatWindow::MakeParticipant: text = tr("Make participant"); break;  					case ChatWindow::MakeVisitor: text = tr("Remove voice"); break;  					case ChatWindow::AddContact: text = tr("Add to contacts"); break; +					case ChatWindow::ShowProfile: text = tr("Show profile"); break;  				}  				QAction* action = contextMenu.addAction(text);  				actions[action] = availableAction; diff --git a/Swift/QtUI/Roster/QtRosterWidget.cpp b/Swift/QtUI/Roster/QtRosterWidget.cpp index 1cf073b..b783ff6 100644 --- a/Swift/QtUI/Roster/QtRosterWidget.cpp +++ b/Swift/QtUI/Roster/QtRosterWidget.cpp @@ -16,6 +16,7 @@  #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" @@ -57,6 +58,7 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) {  	if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) {  		QAction* editContact = contextMenu.addAction(tr("Edit…"));  		QAction* removeContact = contextMenu.addAction(tr("Remove")); +		QAction* showProfileForContact = contextMenu.addAction(tr("Show Profile"));  #ifdef SWIFT_EXPERIMENTAL_FT  		QAction* sendFile = NULL;  		if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) { @@ -78,6 +80,9 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) {  				eventStream_->send(boost::make_shared<RemoveRosterItemUIEvent>(contact->getJID()));  			}  		} +		else if (result == showProfileForContact) { +			eventStream_->send(boost::make_shared<ShowProfileForRosterItemUIEvent>(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/RosterDelegate.cpp b/Swift/QtUI/Roster/RosterDelegate.cpp index 7e6428b..5c964ca 100644 --- a/Swift/QtUI/Roster/RosterDelegate.cpp +++ b/Swift/QtUI/Roster/RosterDelegate.cpp @@ -35,6 +35,7 @@ RosterDelegate::~RosterDelegate() {  void RosterDelegate::setCompact(bool compact) {  	compact_ = compact; +	emit sizeHintChanged(QModelIndex());  }  QSize RosterDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const { diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 64c3b15..4ab6792 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -20,7 +20,12 @@ def generateDefaultTheme(dir) :  Import("env")  myenv = env.Clone() + +# Disable warnings that affect Qt  myenv["CXXFLAGS"] = filter(lambda x : x != "-Wfloat-equal", myenv["CXXFLAGS"]) +if "clang" in env["CC"] : +	myenv.Append(CXXFLAGS = ["-Wno-float-equal", "-Wno-shorten-64-to-32", "-Wno-missing-prototypes", "-Wno-unreachable-code", "-Wno-disabled-macro-expansion", "-Wno-unused-private-field", "-Wno-extra-semi", "-Wno-duplicate-enum", "-Wno-missing-variable-declarations", "-Wno-conversion"]) +  myenv.UseFlags(env["SWIFT_CONTROLLERS_FLAGS"])  myenv.UseFlags(env["SWIFTOOLS_FLAGS"])  if myenv["HAVE_XSS"] : @@ -107,6 +112,11 @@ sources = [      "QtEditBookmarkWindow.cpp",      "QtContactEditWindow.cpp",      "QtContactEditWidget.cpp", +    "QtSingleWindow.cpp", +    "QtHighlightEditorWidget.cpp", +    "QtHighlightRulesItemModel.cpp", +    "QtHighlightRuleWidget.cpp", +    "QtColorToolButton.cpp",      "ChatSnippet.cpp",      "MessageSnippet.cpp",      "SystemMessageSnippet.cpp", @@ -165,6 +175,34 @@ sources = [      "QtURLValidator.cpp"    ] +# QtVCardWidget +sources.extend([ +	"QtVCardWidget/QtCloseButton.cpp", +	"QtVCardWidget/QtRemovableItemDelegate.cpp", +	"QtVCardWidget/QtResizableLineEdit.cpp", +	"QtVCardWidget/QtTagComboBox.cpp", +	"QtVCardWidget/QtVCardHomeWork.cpp", +	"QtVCardWidget/QtVCardAddressField.cpp", +	"QtVCardWidget/QtVCardAddressLabelField.cpp", +	"QtVCardWidget/QtVCardBirthdayField.cpp", +	"QtVCardWidget/QtVCardDescriptionField.cpp", +	"QtVCardWidget/QtVCardInternetEMailField.cpp", +	"QtVCardWidget/QtVCardJIDField.cpp", +	"QtVCardWidget/QtVCardOrganizationField.cpp", +	"QtVCardWidget/QtVCardPhotoAndNameFields.cpp", +	"QtVCardWidget/QtVCardRoleField.cpp", +	"QtVCardWidget/QtVCardTelephoneField.cpp", +	"QtVCardWidget/QtVCardTitleField.cpp", +	"QtVCardWidget/QtVCardURLField.cpp", +	"QtVCardWidget/QtVCardGeneralField.cpp", +	"QtVCardWidget/QtVCardWidget.cpp" +]) + +myenv.Uic4("QtVCardWidget/QtVCardPhotoAndNameFields.ui") +myenv.Uic4("QtVCardWidget/QtVCardWidget.ui") +myenv.Uic4("QtProfileWindow.ui") + +  # Determine the version  myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")  if env["PLATFORM"] == "win32" : @@ -225,6 +263,8 @@ myenv.Uic4("QtAffiliationEditor.ui")  myenv.Uic4("QtJoinMUCWindow.ui")  myenv.Uic4("QtHistoryWindow.ui")  myenv.Uic4("QtConnectionSettings.ui") +myenv.Uic4("QtHighlightRuleWidget.ui") +myenv.Uic4("QtHighlightEditorWidget.ui")  myenv.Qrc("DefaultTheme.qrc")  myenv.Qrc("Swift.qrc") diff --git a/Swift/QtUI/Swift.qrc b/Swift/QtUI/Swift.qrc index eb4f7ee..934bd80 100644 --- a/Swift/QtUI/Swift.qrc +++ b/Swift/QtUI/Swift.qrc @@ -29,5 +29,16 @@  		<file alias="icons/polygon.png">../resources/icons/polygon.png</file>  		<file alias="icons/cursor.png">../resources/icons/cursor.png</file>  		<file alias="icons/eraser.png">../resources/icons/eraser.png</file> +		<file alias="emoticons/emoticons.txt">../resources/emoticons/emoticons.txt</file> +		<file alias="emoticons/evilgrin.png">../resources/emoticons/evilgrin.png</file> +		<file alias="emoticons/grin.png">../resources/emoticons/grin.png</file> +		<file alias="emoticons/happy.png">../resources/emoticons/happy.png</file> +		<file alias="emoticons/smile.png">../resources/emoticons/smile.png</file> +		<file alias="emoticons/surprised.png">../resources/emoticons/surprised.png</file> +		<file alias="emoticons/tongue.png">../resources/emoticons/tongue.png</file> +		<file alias="emoticons/unhappy.png">../resources/emoticons/unhappy.png</file> +		<file alias="emoticons/wink.png">../resources/emoticons/wink.png</file> +		<file alias="icons/star-checked.png">../resources/icons/star-checked2.png</file> +		<file alias="icons/star-unchecked.png">../resources/icons/star-unchecked2.png</file>  	</qresource>  </RCC> diff --git a/Swift/QtUI/Whiteboard/ColorWidget.h b/Swift/QtUI/Whiteboard/ColorWidget.h index 6abdf00..ae1af0f 100644 --- a/Swift/QtUI/Whiteboard/ColorWidget.h +++ b/Swift/QtUI/Whiteboard/ColorWidget.h @@ -10,7 +10,7 @@  namespace Swift {  	class ColorWidget : public QWidget { -		Q_OBJECT; +		Q_OBJECT  	public:  		ColorWidget(QWidget* parent = 0);  		QSize sizeHint() const; diff --git a/Swift/QtUI/Whiteboard/FreehandLineItem.h b/Swift/QtUI/Whiteboard/FreehandLineItem.h index f3c6607..b1af3d1 100644 --- a/Swift/QtUI/Whiteboard/FreehandLineItem.h +++ b/Swift/QtUI/Whiteboard/FreehandLineItem.h @@ -10,8 +10,6 @@  #include <QPainter>  #include <iostream> -using namespace std; -  namespace Swift {  	class FreehandLineItem : public QGraphicsItem {  	public: diff --git a/Swift/QtUI/Whiteboard/GView.h b/Swift/QtUI/Whiteboard/GView.h index 88ea326..6a4fd2f 100644 --- a/Swift/QtUI/Whiteboard/GView.h +++ b/Swift/QtUI/Whiteboard/GView.h @@ -18,7 +18,7 @@  namespace Swift {  	class GView : public QGraphicsView { -		Q_OBJECT; +		Q_OBJECT  	public:  		enum Mode {	Rubber, Line, Rect, Circle, HandLine, Text, Polygon, Select };  		enum Type { New, Update, MoveUp, MoveDown }; diff --git a/Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp b/Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp index 50d7f54..414e590 100644 --- a/Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp +++ b/Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp @@ -10,6 +10,7 @@  #include <boost/bind.hpp>  #include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp>  #include <Swiften/Whiteboard/WhiteboardSession.h>  #include <Swiften/Elements/WhiteboardPayload.h> @@ -264,7 +265,9 @@ namespace Swift {  			std::vector<std::pair<int, int> > points;  			QVector<QPointF>::const_iterator it = freehandLineItem->points().constBegin();  			for ( ; it != freehandLineItem->points().constEnd(); ++it) { -				points.push_back(std::pair<int, int>(it->x()+item->pos().x(), it->y()+item->pos().y())); +				points.push_back(std::pair<int, int>( +							boost::numeric_cast<int>(it->x()+item->pos().x()),  +							boost::numeric_cast<int>(it->y()+item->pos().y())));  			}  			element->setColor(WhiteboardColor(color.red(), color.green(), color.blue(), color.alpha())); @@ -310,7 +313,9 @@ namespace Swift {  			std::vector<std::pair<int, int> > points;  			QVector<QPointF>::const_iterator it = polygon.begin();  			for (; it != polygon.end(); ++it) { -				points.push_back(std::pair<int, int>(it->x()+item->pos().x(), it->y()+item->pos().y())); +				points.push_back(std::pair<int, int>( +							boost::numeric_cast<int>(it->x()+item->pos().x()), +							boost::numeric_cast<int>(it->y()+item->pos().y())));  			}  			element->setPoints(points); @@ -328,10 +333,10 @@ namespace Swift {  		QGraphicsEllipseItem* ellipseItem = qgraphicsitem_cast<QGraphicsEllipseItem*>(item);  		if (ellipseItem) {  			QRectF rect = ellipseItem->rect(); -			int cx = rect.x()+rect.width()/2 + item->pos().x(); -			int cy = rect.y()+rect.height()/2 + item->pos().y(); -			int rx = rect.width()/2; -			int ry = rect.height()/2; +			int cx = boost::numeric_cast<int>(rect.x()+rect.width()/2 + item->pos().x()); +			int cy = boost::numeric_cast<int>(rect.y()+rect.height()/2 + item->pos().y()); +			int rx = boost::numeric_cast<int>(rect.width()/2); +			int ry = boost::numeric_cast<int>(rect.height()/2);  			WhiteboardEllipseElement::ref element = boost::make_shared<WhiteboardEllipseElement>(cx, cy, rx, ry);  			QColor penColor = ellipseItem->pen().color(); diff --git a/Swift/QtUI/Whiteboard/QtWhiteboardWindow.h b/Swift/QtUI/Whiteboard/QtWhiteboardWindow.h index 4665ef0..3957bb7 100644 --- a/Swift/QtUI/Whiteboard/QtWhiteboardWindow.h +++ b/Swift/QtUI/Whiteboard/QtWhiteboardWindow.h @@ -30,7 +30,7 @@  namespace Swift {  	class QtWhiteboardWindow : public QWidget, public WhiteboardWindow  	{ -		Q_OBJECT; +		Q_OBJECT  	public:  		QtWhiteboardWindow(WhiteboardSession::ref whiteboardSession);  		void show(); diff --git a/Swift/QtUI/Whiteboard/TextDialog.h b/Swift/QtUI/Whiteboard/TextDialog.h index f4d9a13..64a0fe2 100644 --- a/Swift/QtUI/Whiteboard/TextDialog.h +++ b/Swift/QtUI/Whiteboard/TextDialog.h @@ -16,12 +16,10 @@  #include <iostream> -using namespace std; -  namespace Swift {  	class TextDialog : public QDialog  	{ -		Q_OBJECT; +		Q_OBJECT  	public:  		TextDialog(QGraphicsTextItem* item, QWidget* parent = 0); diff --git a/Swift/resources/emoticons/emoticons.txt b/Swift/resources/emoticons/emoticons.txt new file mode 100644 index 0000000..fdfa899 --- /dev/null +++ b/Swift/resources/emoticons/emoticons.txt @@ -0,0 +1,18 @@ +ignore >:) qrc:/emoticons/evilgrin.png +ignore >:-) qrc:/emoticons/evilgrin.png +:) qrc:/emoticons/smile.png +:-) qrc:/emoticons/smile.png +(: qrc:/emoticons/smile.png +(-: qrc:/emoticons/smile.png +:D qrc:/emoticons/happy.png +:-D qrc:/emoticons/happy.png +:o qrc:/emoticons/surprised.png +:-o qrc:/emoticons/surprised.png +:-O qrc:/emoticons/surprised.png +:O qrc:/emoticons/surprised.png +:p qrc:/emoticons/tongue.png +:-p qrc:/emoticons/tongue.png +:( qrc:/emoticons/unhappy.png +:-( qrc:/emoticons/unhappy.png +;) qrc:/emoticons/wink.png +;-) qrc:/emoticons/wink.png diff --git a/Swift/resources/emoticons/evilgrin.png b/Swift/resources/emoticons/evilgrin.pngBinary files differ new file mode 100644 index 0000000..817bd50 --- /dev/null +++ b/Swift/resources/emoticons/evilgrin.png diff --git a/Swift/resources/emoticons/grin.png b/Swift/resources/emoticons/grin.pngBinary files differ new file mode 100644 index 0000000..fc60c5e --- /dev/null +++ b/Swift/resources/emoticons/grin.png diff --git a/Swift/resources/emoticons/happy.png b/Swift/resources/emoticons/happy.pngBinary files differ new file mode 100644 index 0000000..6b7336e --- /dev/null +++ b/Swift/resources/emoticons/happy.png diff --git a/Swift/resources/emoticons/smile.png b/Swift/resources/emoticons/smile.pngBinary files differ new file mode 100644 index 0000000..ade4318 --- /dev/null +++ b/Swift/resources/emoticons/smile.png diff --git a/Swift/resources/emoticons/surprised.png b/Swift/resources/emoticons/surprised.pngBinary files differ new file mode 100644 index 0000000..4520cfc --- /dev/null +++ b/Swift/resources/emoticons/surprised.png diff --git a/Swift/resources/emoticons/tongue.png b/Swift/resources/emoticons/tongue.pngBinary files differ new file mode 100644 index 0000000..ecafd2f --- /dev/null +++ b/Swift/resources/emoticons/tongue.png diff --git a/Swift/resources/emoticons/unhappy.png b/Swift/resources/emoticons/unhappy.pngBinary files differ new file mode 100644 index 0000000..fd5d030 --- /dev/null +++ b/Swift/resources/emoticons/unhappy.png diff --git a/Swift/resources/emoticons/wink.png b/Swift/resources/emoticons/wink.pngBinary files differ new file mode 100644 index 0000000..a631949 --- /dev/null +++ b/Swift/resources/emoticons/wink.png diff --git a/Swift/resources/icons/star-checked2.png b/Swift/resources/icons/star-checked2.pngBinary files differ new file mode 100644 index 0000000..2908534 --- /dev/null +++ b/Swift/resources/icons/star-checked2.png diff --git a/Swift/resources/icons/star-checked2.svg b/Swift/resources/icons/star-checked2.svg new file mode 100644 index 0000000..ea4d1da --- /dev/null +++ b/Swift/resources/icons/star-checked2.svg @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<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="89.89167" +   height="85.751091" +   id="svg2" +   version="1.1" +   inkscape:version="0.48.2 r9819" +   sodipodi:docname="star-checked2.svg"> +  <defs +     id="defs4" /> +  <sodipodi:namedview +     id="base" +     pagecolor="#ffffff" +     bordercolor="#666666" +     borderopacity="1.0" +     inkscape:pageopacity="0.0" +     inkscape:pageshadow="2" +     inkscape:zoom="7.9195959" +     inkscape:cx="27.108144" +     inkscape:cy="28.243526" +     inkscape:document-units="px" +     inkscape:current-layer="layer1" +     showgrid="true" +     showguides="false" +     inkscape:snap-global="false" +     fit-margin-top="0.6" +     fit-margin-left="0.6" +     fit-margin-right="0.6" +     fit-margin-bottom="0.6" +     inkscape:window-width="1440" +     inkscape:window-height="852" +     inkscape:window-x="0" +     inkscape:window-y="0" +     inkscape:window-maximized="1"> +    <inkscape:grid +       type="xygrid" +       id="grid2987" +       empspacing="5" +       visible="true" +       enabled="true" +       snapvisiblegridlinesonly="true" /> +  </sodipodi:namedview> +  <metadata +     id="metadata7"> +    <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 /> +      </cc:Work> +    </rdf:RDF> +  </metadata> +  <g +     inkscape:label="Layer 1" +     inkscape:groupmode="layer" +     id="layer1" +     transform="translate(-114.63435,-344.09596)"> +    <path +       sodipodi:type="star" +       style="fill:#ffd700;fill-opacity:1;stroke:#444444;stroke-width:4;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" +       id="path2989" +       sodipodi:sides="5" +       sodipodi:cx="150" +       sodipodi:cy="393.07648" +       sodipodi:r1="44.498337" +       sodipodi:r2="24.474085" +       sodipodi:arg1="-0.32682665" +       sodipodi:arg2="0.30149188" +       inkscape:flatsided="false" +       inkscape:rounded="0.05" +       inkscape:randomized="0" +       d="m 192.14286,378.79076 c 0.4588,1.35347 -18.34832,20.18852 -18.77269,21.55318 -0.42437,1.36466 4.38466,27.54365 3.23921,28.39825 -1.14545,0.85459 -24.87036,-11.21169 -26.29937,-11.19359 -1.429,0.0181 -24.84064,12.68151 -26.00737,11.85621 -1.16672,-0.8253 2.97759,-27.11772 2.51879,-28.47119 -0.4588,-1.35347 -19.73702,-19.70605 -19.31265,-21.07071 0.42437,-1.36466 26.71061,-5.54798 27.85606,-6.40257 1.14545,-0.8546 12.64249,-24.86053 14.0715,-24.87863 1.429,-0.0181 13.53048,23.68888 14.69721,24.51418 1.16672,0.82531 27.5505,4.3414 28.00931,5.69487 z" +       inkscape:transform-center-x="0.015620988" +       inkscape:transform-center-y="-4.2343566" +       transform="matrix(0.99993351,0.01153134,-0.01153134,0.99993351,14.139118,-3.5976011)" /> +  </g> +</svg> diff --git a/Swift/resources/icons/star-unchecked2.png b/Swift/resources/icons/star-unchecked2.pngBinary files differ new file mode 100644 index 0000000..ee85d69 --- /dev/null +++ b/Swift/resources/icons/star-unchecked2.png diff --git a/Swift/resources/icons/star-unchecked2.svg b/Swift/resources/icons/star-unchecked2.svg new file mode 100644 index 0000000..4186d0e --- /dev/null +++ b/Swift/resources/icons/star-unchecked2.svg @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<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="89.89167" +   height="85.751091" +   id="svg2" +   version="1.1" +   inkscape:version="0.48.2 r9819" +   sodipodi:docname="star-unchecked2.svg"> +  <defs +     id="defs4" /> +  <sodipodi:namedview +     id="base" +     pagecolor="#ffffff" +     bordercolor="#666666" +     borderopacity="1.0" +     inkscape:pageopacity="0.0" +     inkscape:pageshadow="2" +     inkscape:zoom="7.9195959" +     inkscape:cx="27.108141" +     inkscape:cy="28.243523" +     inkscape:document-units="px" +     inkscape:current-layer="layer1" +     showgrid="true" +     showguides="false" +     inkscape:snap-global="false" +     fit-margin-top="0.6" +     fit-margin-left="0.6" +     fit-margin-right="0.6" +     fit-margin-bottom="0.6" +     inkscape:window-width="1440" +     inkscape:window-height="852" +     inkscape:window-x="0" +     inkscape:window-y="0" +     inkscape:window-maximized="1"> +    <inkscape:grid +       type="xygrid" +       id="grid2987" +       empspacing="5" +       visible="true" +       enabled="true" +       snapvisiblegridlinesonly="true" /> +  </sodipodi:namedview> +  <metadata +     id="metadata7"> +    <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 /> +      </cc:Work> +    </rdf:RDF> +  </metadata> +  <g +     inkscape:label="Layer 1" +     inkscape:groupmode="layer" +     id="layer1" +     transform="translate(-114.63435,-344.09596)"> +    <path +       sodipodi:type="star" +       style="fill:#ffffff;fill-opacity:1;stroke:#444444;stroke-width:4;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" +       id="path2989" +       sodipodi:sides="5" +       sodipodi:cx="150" +       sodipodi:cy="393.07648" +       sodipodi:r1="44.498337" +       sodipodi:r2="24.474085" +       sodipodi:arg1="-0.32682665" +       sodipodi:arg2="0.30149188" +       inkscape:flatsided="false" +       inkscape:rounded="0.05" +       inkscape:randomized="0" +       d="m 192.14286,378.79076 c 0.4588,1.35347 -18.34832,20.18852 -18.77269,21.55318 -0.42437,1.36466 4.38466,27.54365 3.23921,28.39825 -1.14545,0.85459 -24.87036,-11.21169 -26.29937,-11.19359 -1.429,0.0181 -24.84064,12.68151 -26.00737,11.85621 -1.16672,-0.8253 2.97759,-27.11772 2.51879,-28.47119 -0.4588,-1.35347 -19.73702,-19.70605 -19.31265,-21.07071 0.42437,-1.36466 26.71061,-5.54798 27.85606,-6.40257 1.14545,-0.8546 12.64249,-24.86053 14.0715,-24.87863 1.429,-0.0181 13.53048,23.68888 14.69721,24.51418 1.16672,0.82531 27.5505,4.3414 28.00931,5.69487 z" +       inkscape:transform-center-x="0.015620988" +       inkscape:transform-center-y="-4.2343566" +       transform="matrix(0.99993351,0.01153134,-0.01153134,0.99993351,14.139118,-3.5976011)" /> +  </g> +</svg> | 
 Swift
 Swift