summaryrefslogtreecommitdiffstats
path: root/Swift
diff options
context:
space:
mode:
Diffstat (limited to 'Swift')
-rw-r--r--Swift/ChangeLog.md5
-rw-r--r--Swift/Controllers/BlockListController.cpp159
-rw-r--r--Swift/Controllers/BlockListController.h48
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp116
-rw-r--r--Swift/Controllers/Chat/ChatController.h21
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.cpp66
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.h62
-rw-r--r--Swift/Controllers/Chat/ChatMessageParser.cpp112
-rw-r--r--Swift/Controllers/Chat/ChatMessageParser.h23
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp46
-rw-r--r--Swift/Controllers/Chat/ChatsManager.h16
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp78
-rw-r--r--Swift/Controllers/Chat/MUCController.h22
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp74
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp86
-rw-r--r--Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp17
-rw-r--r--Swift/Controllers/Chat/UnitTest/MockChatListWindow.h8
-rw-r--r--Swift/Controllers/DummySoundPlayer.h2
-rw-r--r--Swift/Controllers/DummySystemTray.h4
-rw-r--r--Swift/Controllers/FileTransfer/FileTransferController.cpp22
-rw-r--r--Swift/Controllers/FileTransfer/FileTransferProgressInfo.cpp4
-rw-r--r--Swift/Controllers/HighlightAction.cpp28
-rw-r--r--Swift/Controllers/HighlightAction.h45
-rw-r--r--Swift/Controllers/HighlightEditorController.cpp40
-rw-r--r--Swift/Controllers/HighlightEditorController.h38
-rw-r--r--Swift/Controllers/HighlightManager.cpp139
-rw-r--r--Swift/Controllers/HighlightManager.h49
-rw-r--r--Swift/Controllers/HighlightRule.cpp192
-rw-r--r--Swift/Controllers/HighlightRule.h77
-rw-r--r--Swift/Controllers/Highlighter.cpp41
-rw-r--r--Swift/Controllers/Highlighter.h37
-rw-r--r--Swift/Controllers/HistoryViewController.cpp16
-rw-r--r--Swift/Controllers/MainController.cpp163
-rw-r--r--Swift/Controllers/MainController.h48
-rw-r--r--Swift/Controllers/ProfileController.cpp8
-rw-r--r--Swift/Controllers/ProfileController.h1
-rw-r--r--Swift/Controllers/Roster/ContactRosterItem.cpp39
-rw-r--r--Swift/Controllers/Roster/ContactRosterItem.h25
-rw-r--r--Swift/Controllers/Roster/LeastCommonSubsequence.h25
-rw-r--r--Swift/Controllers/Roster/Roster.cpp35
-rw-r--r--Swift/Controllers/Roster/Roster.h10
-rw-r--r--Swift/Controllers/Roster/RosterController.cpp46
-rw-r--r--Swift/Controllers/Roster/RosterController.h17
-rw-r--r--Swift/Controllers/Roster/RosterItemOperation.h8
-rw-r--r--Swift/Controllers/Roster/SetAvatar.h7
-rw-r--r--Swift/Controllers/Roster/TableRoster.cpp7
-rw-r--r--Swift/Controllers/Roster/TableRoster.h5
-rw-r--r--Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp26
-rw-r--r--Swift/Controllers/Roster/UnitTest/TableRosterTest.cpp2
-rw-r--r--Swift/Controllers/SConscript13
-rw-r--r--Swift/Controllers/SettingConstants.cpp5
-rw-r--r--Swift/Controllers/SettingConstants.h5
-rw-r--r--Swift/Controllers/Settings/DummySettingsProvider.h12
-rw-r--r--Swift/Controllers/Settings/UnitTest/SettingsProviderHierachyTest.cpp2
-rw-r--r--Swift/Controllers/ShowProfileController.cpp74
-rw-r--r--Swift/Controllers/ShowProfileController.h36
-rw-r--r--Swift/Controllers/SoundEventController.cpp19
-rw-r--r--Swift/Controllers/SoundEventController.h8
-rw-r--r--Swift/Controllers/SoundPlayer.h6
-rw-r--r--Swift/Controllers/StatusCache.cpp107
-rw-r--r--Swift/Controllers/StatusCache.h40
-rw-r--r--Swift/Controllers/StatusTracker.cpp6
-rw-r--r--Swift/Controllers/StatusTracker.h5
-rw-r--r--Swift/Controllers/StatusUtil.cpp2
-rw-r--r--Swift/Controllers/Storages/AvatarFileStorage.cpp10
-rw-r--r--Swift/Controllers/Storages/AvatarFileStorage.h7
-rw-r--r--Swift/Controllers/Storages/CertificateFileStorage.cpp13
-rw-r--r--Swift/Controllers/Storages/CertificateFileStorage.h6
-rw-r--r--Swift/Controllers/Storages/CertificateFileStorageFactory.h8
-rw-r--r--Swift/Controllers/Storages/FileStorages.cpp13
-rw-r--r--Swift/Controllers/Storages/FileStorages.h7
-rw-r--r--Swift/Controllers/Storages/FileStoragesFactory.h9
-rw-r--r--Swift/Controllers/Storages/VCardFileStorage.cpp9
-rw-r--r--Swift/Controllers/Storages/VCardFileStorage.h7
-rw-r--r--Swift/Controllers/SystemTray.h2
-rw-r--r--Swift/Controllers/Translator.cpp2
-rw-r--r--Swift/Controllers/UIEvents/AddContactUIEvent.h6
-rw-r--r--Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h2
-rw-r--r--Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h6
-rw-r--r--Swift/Controllers/UIEvents/JoinMUCUIEvent.h2
-rw-r--r--Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h2
-rw-r--r--Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h6
-rw-r--r--Swift/Controllers/UIEvents/RequestAdHocUIEvent.h2
-rw-r--r--Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h8
-rw-r--r--Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h16
-rw-r--r--Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h37
-rw-r--r--Swift/Controllers/UIEvents/RequestChatUIEvent.h2
-rw-r--r--Swift/Controllers/UIEvents/RequestHighlightEditorUIEvent.h16
-rw-r--r--Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h2
-rw-r--r--Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h25
-rw-r--r--Swift/Controllers/UIEvents/ShowWhiteboardUIEvent.h2
-rw-r--r--Swift/Controllers/UIEvents/UIEventStream.h2
-rw-r--r--Swift/Controllers/UIInterfaces/AdHocCommandWindow.h2
-rw-r--r--Swift/Controllers/UIInterfaces/BlockListEditorWidget.h32
-rw-r--r--Swift/Controllers/UIInterfaces/BlockListEditorWidgetFactory.h20
-rw-r--r--Swift/Controllers/UIInterfaces/ChatListWindow.h2
-rw-r--r--Swift/Controllers/UIInterfaces/ChatWindow.h88
-rw-r--r--Swift/Controllers/UIInterfaces/ChatWindowFactory.h2
-rw-r--r--Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h2
-rw-r--r--Swift/Controllers/UIInterfaces/EventWindow.h2
-rw-r--r--Swift/Controllers/UIInterfaces/EventWindowFactory.h2
-rw-r--r--Swift/Controllers/UIInterfaces/HighlightEditorWidget.h22
-rw-r--r--Swift/Controllers/UIInterfaces/HighlightEditorWidgetFactory.h20
-rw-r--r--Swift/Controllers/UIInterfaces/HistoryWindow.h2
-rw-r--r--Swift/Controllers/UIInterfaces/HistoryWindowFactory.h2
-rw-r--r--Swift/Controllers/UIInterfaces/InviteToChatWindow.h2
-rw-r--r--Swift/Controllers/UIInterfaces/JoinMUCWindow.h2
-rw-r--r--Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h2
-rw-r--r--Swift/Controllers/UIInterfaces/LoginWindow.h2
-rw-r--r--Swift/Controllers/UIInterfaces/LoginWindowFactory.h2
-rw-r--r--Swift/Controllers/UIInterfaces/MUCSearchWindow.h2
-rw-r--r--Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h2
-rw-r--r--Swift/Controllers/UIInterfaces/MainWindow.h3
-rw-r--r--Swift/Controllers/UIInterfaces/MainWindowFactory.h2
-rw-r--r--Swift/Controllers/UIInterfaces/ProfileWindow.h7
-rw-r--r--Swift/Controllers/UIInterfaces/ProfileWindowFactory.h2
-rw-r--r--Swift/Controllers/UIInterfaces/UIFactory.h6
-rw-r--r--Swift/Controllers/UIInterfaces/UserSearchWindowFactory.h2
-rw-r--r--Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h2
-rw-r--r--Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h2
-rw-r--r--Swift/Controllers/UnitTest/ChatMessageSummarizerTest.cpp2
-rw-r--r--Swift/Controllers/UnitTest/HighlightRuleTest.cpp318
-rw-r--r--Swift/Controllers/UnitTest/MockChatWindow.h87
-rw-r--r--Swift/Controllers/UnitTest/MockMainWindow.h21
-rw-r--r--Swift/Controllers/UnitTest/MockMainWindowFactory.h6
-rw-r--r--Swift/Controllers/XMPPEvents/ErrorEvent.h8
-rw-r--r--Swift/Controllers/XMPPEvents/EventController.cpp5
-rw-r--r--Swift/Controllers/XMPPEvents/MessageEvent.h2
-rw-r--r--Swift/Controllers/XMPPEvents/StanzaEvent.h8
-rw-r--r--Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h12
-rw-r--r--Swift/Packaging/Debian/debian/control.in2
-rwxr-xr-xSwift/Packaging/Debian/package.sh4
-rw-r--r--Swift/QtUI/ChatList/ChatListDelegate.cpp5
-rw-r--r--Swift/QtUI/ChatList/ChatListGroupItem.h16
-rw-r--r--Swift/QtUI/ChatList/ChatListItem.h4
-rw-r--r--Swift/QtUI/ChatList/ChatListRecentItem.cpp5
-rw-r--r--Swift/QtUI/ChatList/ChatListRecentItem.h3
-rw-r--r--Swift/QtUI/ChatList/ChatListWhiteboardItem.cpp9
-rw-r--r--Swift/QtUI/ChatSnippet.cpp62
-rw-r--r--Swift/QtUI/ChatSnippet.h19
-rw-r--r--Swift/QtUI/EventViewer/EventDelegate.cpp36
-rw-r--r--Swift/QtUI/EventViewer/QtEvent.cpp5
-rw-r--r--Swift/QtUI/EventViewer/QtEvent.h2
-rw-r--r--Swift/QtUI/FreeDesktopNotifier.cpp7
-rw-r--r--Swift/QtUI/FreeDesktopNotifier.h7
-rw-r--r--Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp2
-rw-r--r--Swift/QtUI/MessageSnippet.cpp9
-rw-r--r--Swift/QtUI/MessageSnippet.h6
-rw-r--r--Swift/QtUI/QtAboutWidget.cpp2
-rw-r--r--Swift/QtUI/QtAffiliationEditor.cpp2
-rw-r--r--Swift/QtUI/QtAffiliationEditor.h2
-rw-r--r--Swift/QtUI/QtAvatarWidget.cpp8
-rw-r--r--Swift/QtUI/QtAvatarWidget.h10
-rw-r--r--Swift/QtUI/QtBlockListEditorWindow.cpp162
-rw-r--r--Swift/QtUI/QtBlockListEditorWindow.h42
-rw-r--r--Swift/QtUI/QtBlockListEditorWindow.ui89
-rw-r--r--Swift/QtUI/QtCachedImageScaler.cpp14
-rw-r--r--Swift/QtUI/QtChatTabs.cpp21
-rw-r--r--Swift/QtUI/QtChatTabs.h4
-rw-r--r--Swift/QtUI/QtChatTheme.h28
-rw-r--r--Swift/QtUI/QtChatView.cpp19
-rw-r--r--Swift/QtUI/QtChatWindow.cpp268
-rw-r--r--Swift/QtUI/QtChatWindow.h55
-rw-r--r--Swift/QtUI/QtChatWindowFactory.cpp17
-rw-r--r--Swift/QtUI/QtChatWindowFactory.h12
-rw-r--r--Swift/QtUI/QtColorToolButton.cpp45
-rw-r--r--Swift/QtUI/QtColorToolButton.h32
-rw-r--r--Swift/QtUI/QtFileTransferListItemModel.cpp16
-rw-r--r--Swift/QtUI/QtFileTransferListItemModel.h2
-rw-r--r--Swift/QtUI/QtFormResultItemModel.cpp2
-rw-r--r--Swift/QtUI/QtHighlightEditorWidget.cpp149
-rw-r--r--Swift/QtUI/QtHighlightEditorWidget.h44
-rw-r--r--Swift/QtUI/QtHighlightEditorWidget.ui124
-rw-r--r--Swift/QtUI/QtHighlightRuleWidget.cpp134
-rw-r--r--Swift/QtUI/QtHighlightRuleWidget.h49
-rw-r--r--Swift/QtUI/QtHighlightRuleWidget.ui260
-rw-r--r--Swift/QtUI/QtHighlightRulesItemModel.cpp284
-rw-r--r--Swift/QtUI/QtHighlightRulesItemModel.h64
-rw-r--r--Swift/QtUI/QtHistoryWindow.cpp20
-rw-r--r--Swift/QtUI/QtJoinMUCWindow.ui6
-rw-r--r--Swift/QtUI/QtLoginWindow.cpp11
-rw-r--r--Swift/QtUI/QtLoginWindow.h2
-rw-r--r--Swift/QtUI/QtMainWindow.cpp33
-rw-r--r--Swift/QtUI/QtMainWindow.h8
-rw-r--r--Swift/QtUI/QtNameWidget.h2
-rw-r--r--Swift/QtUI/QtProfileWindow.cpp160
-rw-r--r--Swift/QtUI/QtProfileWindow.h77
-rw-r--r--Swift/QtUI/QtProfileWindow.ui85
-rw-r--r--Swift/QtUI/QtRosterHeader.cpp4
-rw-r--r--Swift/QtUI/QtRosterHeader.h3
-rw-r--r--Swift/QtUI/QtSettingsProvider.cpp2
-rw-r--r--Swift/QtUI/QtSingleWindow.cpp81
-rw-r--r--Swift/QtUI/QtSingleWindow.h37
-rw-r--r--Swift/QtUI/QtSoundPlayer.cpp16
-rw-r--r--Swift/QtUI/QtSoundPlayer.h2
-rw-r--r--Swift/QtUI/QtSpellCheckerWindow.cpp115
-rw-r--r--Swift/QtUI/QtSpellCheckerWindow.h34
-rw-r--r--Swift/QtUI/QtSpellCheckerWindow.ui105
-rw-r--r--Swift/QtUI/QtStatusWidget.cpp61
-rw-r--r--Swift/QtUI/QtStatusWidget.h7
-rw-r--r--Swift/QtUI/QtSwift.cpp109
-rw-r--r--Swift/QtUI/QtSwift.h7
-rw-r--r--Swift/QtUI/QtSwiftUtil.h2
-rw-r--r--Swift/QtUI/QtSystemTray.cpp4
-rw-r--r--Swift/QtUI/QtTabbable.h4
-rw-r--r--Swift/QtUI/QtTextEdit.cpp171
-rw-r--r--Swift/QtUI/QtTextEdit.h28
-rw-r--r--Swift/QtUI/QtTranslator.h4
-rw-r--r--Swift/QtUI/QtUIFactory.cpp59
-rw-r--r--Swift/QtUI/QtUIFactory.h9
-rw-r--r--Swift/QtUI/QtURLValidator.cpp4
-rw-r--r--Swift/QtUI/QtUtilities.cpp15
-rw-r--r--Swift/QtUI/QtUtilities.h5
-rw-r--r--Swift/QtUI/QtVCardWidget/QtCloseButton.cpp55
-rw-r--r--Swift/QtUI/QtVCardWidget/QtCloseButton.h28
-rw-r--r--Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp42
-rw-r--r--Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h27
-rw-r--r--Swift/QtUI/QtVCardWidget/QtResizableLineEdit.cpp49
-rw-r--r--Swift/QtUI/QtVCardWidget/QtResizableLineEdit.h33
-rw-r--r--Swift/QtUI/QtVCardWidget/QtTagComboBox.cpp102
-rw-r--r--Swift/QtUI/QtVCardWidget/QtTagComboBox.h46
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardAddressField.cpp165
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardAddressField.h60
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.cpp97
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.h49
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.cpp61
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.h43
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.cpp64
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.h42
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h52
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardGeneralField.cpp115
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardGeneralField.h77
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardHomeWork.cpp40
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardHomeWork.h31
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.cpp81
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.h43
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardJIDField.cpp72
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardJIDField.h42
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.cpp135
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.h50
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.cpp176
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.h73
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.ui251
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardRoleField.cpp50
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardRoleField.h41
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.cpp100
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.h42
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardTitleField.cpp51
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardTitleField.h41
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardURLField.cpp70
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardURLField.h42
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp368
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardWidget.h60
-rw-r--r--Swift/QtUI/QtVCardWidget/QtVCardWidget.ui137
-rw-r--r--Swift/QtUI/QtWebView.cpp3
-rw-r--r--Swift/QtUI/QtWin32NotifierWindow.h2
-rw-r--r--Swift/QtUI/QtXMLConsoleWidget.cpp8
-rw-r--r--Swift/QtUI/Roster/DelegateCommons.cpp9
-rw-r--r--Swift/QtUI/Roster/DelegateCommons.h5
-rw-r--r--Swift/QtUI/Roster/QtOccupantListWidget.cpp1
-rw-r--r--Swift/QtUI/Roster/QtRosterWidget.cpp46
-rw-r--r--Swift/QtUI/Roster/QtTreeWidget.cpp1
-rw-r--r--Swift/QtUI/Roster/RosterDelegate.cpp4
-rw-r--r--Swift/QtUI/Roster/RosterModel.cpp37
-rw-r--r--Swift/QtUI/Roster/RosterModel.h2
-rw-r--r--Swift/QtUI/SConscript87
-rw-r--r--Swift/QtUI/Swift.qrc13
-rw-r--r--Swift/QtUI/SystemMessageSnippet.cpp5
-rw-r--r--Swift/QtUI/SystemMessageSnippet.h2
-rw-r--r--Swift/QtUI/UserSearch/QtUserSearchWindow.cpp6
-rw-r--r--Swift/QtUI/Whiteboard/ColorWidget.h2
-rw-r--r--Swift/QtUI/Whiteboard/FreehandLineItem.h2
-rw-r--r--Swift/QtUI/Whiteboard/GView.h2
-rw-r--r--Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp19
-rw-r--r--Swift/QtUI/Whiteboard/QtWhiteboardWindow.h2
-rw-r--r--Swift/QtUI/Whiteboard/TextDialog.h4
-rw-r--r--Swift/QtUI/WinUIHelpers.cpp2
-rw-r--r--Swift/QtUI/main.cpp11
-rw-r--r--Swift/resources/emoticons/emoticons.txt18
-rw-r--r--Swift/resources/emoticons/evilgrin.pngbin0 -> 727 bytes
-rw-r--r--Swift/resources/emoticons/grin.pngbin0 -> 714 bytes
-rw-r--r--Swift/resources/emoticons/happy.pngbin0 -> 731 bytes
-rw-r--r--Swift/resources/emoticons/smile.pngbin0 -> 725 bytes
-rw-r--r--Swift/resources/emoticons/surprised.pngbin0 -> 741 bytes
-rw-r--r--Swift/resources/emoticons/tongue.pngbin0 -> 727 bytes
-rw-r--r--Swift/resources/emoticons/unhappy.pngbin0 -> 723 bytes
-rw-r--r--Swift/resources/emoticons/wink.pngbin0 -> 712 bytes
-rw-r--r--Swift/resources/icons/star-checked2.pngbin0 -> 1346 bytes
-rw-r--r--Swift/resources/icons/star-checked2.svg88
-rw-r--r--Swift/resources/icons/star-unchecked2.pngbin0 -> 894 bytes
-rw-r--r--Swift/resources/icons/star-unchecked2.svg88
-rw-r--r--Swift/resources/icons/stop.pngbin0 -> 454 bytes
-rw-r--r--Swift/resources/icons/stop.svg74
-rw-r--r--Swift/resources/icons/stop.txt2
-rw-r--r--Swift/resources/icons/zzz.pngbin0 -> 850 bytes
-rw-r--r--Swift/resources/icons/zzz.svg166
-rwxr-xr-xSwift/resources/themes/Default/Incoming/Content.html2
-rwxr-xr-xSwift/resources/themes/Default/Incoming/Context.html2
-rwxr-xr-xSwift/resources/themes/Default/Incoming/NextContent.html2
-rwxr-xr-xSwift/resources/themes/Default/Incoming/NextContext.html2
-rwxr-xr-xSwift/resources/themes/Default/Outgoing/Content.html2
-rwxr-xr-xSwift/resources/themes/Default/Outgoing/Context.html2
-rwxr-xr-xSwift/resources/themes/Default/Outgoing/NextContent.html2
-rwxr-xr-xSwift/resources/themes/Default/Outgoing/NextContext.html2
-rwxr-xr-xSwift/resources/themes/Default/Status.html2
305 files changed, 9768 insertions, 1060 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/BlockListController.cpp b/Swift/Controllers/BlockListController.cpp
new file mode 100644
index 0000000..e7bc45d
--- /dev/null
+++ b/Swift/Controllers/BlockListController.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swift/Controllers/BlockListController.h>
+
+#include <boost/bind.hpp>
+
+#include <Swiften/Client/ClientBlockListManager.h>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/format.h>
+#include <Swift/Controllers/Intl.h>
+#include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h>
+#include <Swift/Controllers/XMPPEvents/ErrorEvent.h>
+#include <Swift/Controllers/UIInterfaces/BlockListEditorWidget.h>
+#include <Swift/Controllers/XMPPEvents/EventController.h>
+
+namespace Swift {
+
+BlockListController::BlockListController(ClientBlockListManager* blockListManager, UIEventStream* uiEventStream, BlockListEditorWidgetFactory* blockListEditorWidgetFactory, EventController* eventController) : blockListManager_(blockListManager), blockListEditorWidgetFactory_(blockListEditorWidgetFactory), blockListEditorWidget_(0), eventController_(eventController), remainingRequests_(0) {
+ uiEventStream->onUIEvent.connect(boost::bind(&BlockListController::handleUIEvent, this, _1));
+ blockListManager_->getBlockList()->onItemAdded.connect(boost::bind(&BlockListController::handleBlockListChanged, this));
+ blockListManager_->getBlockList()->onItemRemoved.connect(boost::bind(&BlockListController::handleBlockListChanged, this));
+}
+
+BlockListController::~BlockListController() {
+ blockListManager_->getBlockList()->onItemAdded.disconnect(boost::bind(&BlockListController::handleBlockListChanged, this));
+ blockListManager_->getBlockList()->onItemRemoved.disconnect(boost::bind(&BlockListController::handleBlockListChanged, this));
+}
+
+void BlockListController::blockListDifferences(const std::vector<JID> &newBlockList, std::vector<JID> &jidsToUnblock, std::vector<JID> &jidsToBlock) const {
+ foreach (const JID& jid, blockListBeforeEdit) {
+ if (std::find(newBlockList.begin(), newBlockList.end(), jid) == newBlockList.end()) {
+ jidsToUnblock.push_back(jid);
+ }
+ }
+
+ foreach (const JID& jid, newBlockList) {
+ if (std::find(blockListBeforeEdit.begin(), blockListBeforeEdit.end(), jid) == blockListBeforeEdit.end()) {
+ jidsToBlock.push_back(jid);
+ }
+ }
+}
+
+void BlockListController::handleUIEvent(boost::shared_ptr<UIEvent> rawEvent) {
+ // handle UI dialog
+ boost::shared_ptr<RequestBlockListDialogUIEvent> requestDialogEvent = boost::dynamic_pointer_cast<RequestBlockListDialogUIEvent>(rawEvent);
+ if (requestDialogEvent != NULL) {
+ if (blockListEditorWidget_ == NULL) {
+ blockListEditorWidget_ = blockListEditorWidgetFactory_->createBlockListEditorWidget();
+ blockListEditorWidget_->onSetNewBlockList.connect(boost::bind(&BlockListController::handleSetNewBlockList, this, _1));
+ }
+ blockListBeforeEdit = blockListManager_->getBlockList()->getItems();
+ blockListEditorWidget_->setCurrentBlockList(blockListBeforeEdit);
+ blockListEditorWidget_->show();
+ return;
+ }
+
+ // handle block state change
+ boost::shared_ptr<RequestChangeBlockStateUIEvent> changeStateEvent = boost::dynamic_pointer_cast<RequestChangeBlockStateUIEvent>(rawEvent);
+ if (changeStateEvent != NULL) {
+ if (changeStateEvent->getBlockState() == RequestChangeBlockStateUIEvent::Blocked) {
+ GenericRequest<BlockPayload>::ref blockRequest = blockListManager_->createBlockJIDRequest(changeStateEvent->getContact());
+ blockRequest->onResponse.connect(boost::bind(&BlockListController::handleBlockResponse, this, blockRequest, _1, _2, std::vector<JID>(1, changeStateEvent->getContact()), false));
+ blockRequest->send();
+ } else if (changeStateEvent->getBlockState() == RequestChangeBlockStateUIEvent::Unblocked) {
+ GenericRequest<UnblockPayload>::ref unblockRequest = blockListManager_->createUnblockJIDRequest(changeStateEvent->getContact());
+ unblockRequest->onResponse.connect(boost::bind(&BlockListController::handleUnblockResponse, this, unblockRequest, _1, _2, std::vector<JID>(1, changeStateEvent->getContact()), false));
+ unblockRequest->send();
+ }
+ return;
+ }
+}
+
+void BlockListController::handleBlockResponse(GenericRequest<BlockPayload>::ref request, boost::shared_ptr<BlockPayload>, ErrorPayload::ref error, const std::vector<JID>& jids, bool originEditor) {
+ if (error) {
+ std::string errorMessage;
+ // FIXME: Handle reporting of list of JIDs in a translatable way.
+ errorMessage = str(format(QT_TRANSLATE_NOOP("", "Failed to block %1%.")) % jids.at(0).toString());
+ if (!error->getText().empty()) {
+ errorMessage = str(format(QT_TRANSLATE_NOOP("", "%1%: %2%.")) % errorMessage % error->getText());
+ }
+ eventController_->handleIncomingEvent(boost::make_shared<ErrorEvent>(request->getReceiver(), errorMessage));
+ }
+ if (originEditor) {
+ remainingRequests_--;
+ if (blockListEditorWidget_ && (remainingRequests_ == 0)) {
+ blockListEditorWidget_->setBusy(false);
+ }
+ }
+}
+
+void BlockListController::handleUnblockResponse(GenericRequest<UnblockPayload>::ref request, boost::shared_ptr<UnblockPayload>, ErrorPayload::ref error, const std::vector<JID>& jids, bool originEditor) {
+ if (error) {
+ std::string errorMessage;
+ // FIXME: Handle reporting of list of JIDs in a translatable way.
+ errorMessage = str(format(QT_TRANSLATE_NOOP("", "Failed to unblock %1%.")) % jids.at(0).toString());
+ if (!error->getText().empty()) {
+ errorMessage = str(format(QT_TRANSLATE_NOOP("", "%1%: %2%.")) % errorMessage % error->getText());
+ }
+ eventController_->handleIncomingEvent(boost::make_shared<ErrorEvent>(request->getReceiver(), errorMessage));
+ }
+ if (originEditor) {
+ remainingRequests_--;
+ if (blockListEditorWidget_ && (remainingRequests_ == 0)) {
+ blockListEditorWidget_->setBusy(false);
+ }
+ }
+}
+
+void BlockListController::handleSetNewBlockList(const std::vector<JID> &newBlockList) {
+ std::vector<JID> jidsToBlock;
+ std::vector<JID> jidsToUnblock;
+
+ blockListDifferences(newBlockList, jidsToUnblock, jidsToBlock);
+
+ if (!jidsToBlock.empty()) {
+ remainingRequests_++;
+ GenericRequest<BlockPayload>::ref blockRequest = blockListManager_->createBlockJIDsRequest(jidsToBlock);
+ blockRequest->onResponse.connect(boost::bind(&BlockListController::handleBlockResponse, this, blockRequest, _1, _2, jidsToBlock, true));
+ blockRequest->send();
+ }
+ if (!jidsToUnblock.empty()) {
+ remainingRequests_++;
+ GenericRequest<UnblockPayload>::ref unblockRequest = blockListManager_->createUnblockJIDsRequest(jidsToUnblock);
+ unblockRequest->onResponse.connect(boost::bind(&BlockListController::handleUnblockResponse, this, unblockRequest, _1, _2, jidsToUnblock, true));
+ unblockRequest->send();
+ }
+ if (!jidsToBlock.empty() || jidsToUnblock.empty()) {
+ assert(blockListEditorWidget_);
+ blockListEditorWidget_->setBusy(true);
+ }
+}
+
+void BlockListController::handleBlockListChanged() {
+ if (blockListEditorWidget_) {
+ std::vector<JID> jidsToBlock;
+ std::vector<JID> jidsToUnblock;
+
+ blockListDifferences(blockListEditorWidget_->getCurrentBlockList(), jidsToUnblock, jidsToBlock);
+ blockListBeforeEdit = blockListManager_->getBlockList()->getItems();
+
+ foreach (const JID& jid, jidsToBlock) {
+ blockListBeforeEdit.push_back(jid);
+ }
+
+ foreach (const JID& jid, jidsToUnblock) {
+ blockListBeforeEdit.erase(std::remove(blockListBeforeEdit.begin(), blockListBeforeEdit.end(), jid), blockListBeforeEdit.end());;
+ }
+
+ blockListEditorWidget_->setCurrentBlockList(blockListBeforeEdit);
+ }
+}
+
+}
diff --git a/Swift/Controllers/BlockListController.h b/Swift/Controllers/BlockListController.h
new file mode 100644
index 0000000..4c9caad
--- /dev/null
+++ b/Swift/Controllers/BlockListController.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Queries/GenericRequest.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIInterfaces/BlockListEditorWidgetFactory.h>
+
+namespace Swift {
+
+class BlockPayload;
+class UnblockPayload;
+class ClientBlockListManager;
+class EventController;
+
+class BlockListController {
+public:
+ BlockListController(ClientBlockListManager* blockListManager, UIEventStream* uiEventStream, BlockListEditorWidgetFactory* blockListEditorWidgetFactory, EventController* eventController);
+ ~BlockListController();
+
+private:
+ void blockListDifferences(const std::vector<JID> &newBlockList, std::vector<JID>& jidsToUnblock, std::vector<JID>& jidsToBlock) const;
+
+ void handleUIEvent(boost::shared_ptr<UIEvent> event);
+
+ void handleBlockResponse(GenericRequest<BlockPayload>::ref, boost::shared_ptr<BlockPayload>, ErrorPayload::ref error, const std::vector<JID>& jids, bool originEditor);
+ void handleUnblockResponse(GenericRequest<UnblockPayload>::ref, boost::shared_ptr<UnblockPayload>, ErrorPayload::ref error, const std::vector<JID>& jids, bool originEditor);
+
+ void handleSetNewBlockList(const std::vector<JID>& newBlockList);
+
+ void handleBlockListChanged();
+
+private:
+ ClientBlockListManager* blockListManager_;
+ BlockListEditorWidgetFactory* blockListEditorWidgetFactory_;
+ BlockListEditorWidget* blockListEditorWidget_;
+ EventController* eventController_;
+ std::vector<JID> blockListBeforeEdit;
+ int remainingRequests_;
+};
+
+}
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index 16b22fe..1ccd7c1 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -1,47 +1,54 @@
/*
- * Copyright (c) 2010-2012 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swift/Controllers/Chat/ChatController.h"
+#include <Swift/Controllers/Chat/ChatController.h>
#include <boost/bind.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <stdio.h>
-#include <Swift/Controllers/Intl.h>
#include <Swiften/Base/format.h>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Avatars/AvatarManager.h>
#include <Swiften/Chat/ChatStateNotifier.h>
#include <Swiften/Chat/ChatStateTracker.h>
#include <Swiften/Client/StanzaChannel.h>
-#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
#include <Swiften/Client/NickResolver.h>
+#include <Swiften/Disco/EntityCapsProvider.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/DateTime.h>
+#include <Swiften/Elements/DeliveryReceipt.h>
+#include <Swiften/Elements/DeliveryReceiptRequest.h>
+#include <Swiften/Elements/Idle.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Client/ClientBlockListManager.h>
+
+#include <Swift/Controllers/Intl.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
#include <Swift/Controllers/XMPPEvents/EventController.h>
#include <Swift/Controllers/FileTransfer/FileTransferController.h>
#include <Swift/Controllers/StatusUtil.h>
-#include <Swiften/Disco/EntityCapsProvider.h>
-#include <Swiften/Base/foreach.h>
#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
#include <Swift/Controllers/UIEvents/AcceptWhiteboardSessionUIEvent.h>
#include <Swift/Controllers/UIEvents/CancelWhiteboardSessionUIEvent.h>
#include <Swift/Controllers/UIEvents/ShowWhiteboardUIEvent.h>
-#include <Swiften/Elements/DeliveryReceipt.h>
-#include <Swiften/Elements/DeliveryReceiptRequest.h>
+#include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h>
#include <Swift/Controllers/SettingConstants.h>
+#include <Swift/Controllers/Highlighter.h>
+#include <Swift/Controllers/Chat/ChatMessageParser.h>
-#include <Swiften/Base/Log.h>
namespace Swift {
/**
* The controller does not gain ownership of the stanzaChannel, nor the factory.
*/
-ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry)
- : 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, ClientBlockListManager* clientBlockListManager, ChatMessageParser* chatMessageParser)
+ : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser), eventStream_(eventStream), userWantsReceipts_(userWantsReceipts), settings_(settings), clientBlockListManager_(clientBlockListManager) {
isInMUC_ = isInMUC;
lastWasPresence_ = false;
chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsProvider);
@@ -62,6 +69,10 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
startMessage = str(format(QT_TRANSLATE_NOOP("", "Starting chat with %1% - %2%")) % nick % contact.toBare().toString());
theirPresence = contact.isBare() ? presenceOracle->getHighestPriorityPresence(contact.toBare()) : presenceOracle->getLastPresence(contact);
}
+ Idle::ref idle;
+ if (theirPresence && (idle = theirPresence->getPayload<Idle>())) {
+ startMessage += str(format(QT_TRANSLATE_NOOP("", ", who has been idle since %1%")) % dateTimeToLocalString(idle->getSince()));
+ }
startMessage += ": " + statusShowTypeToFriendlyName(theirPresence ? theirPresence->getShow() : StatusShow::None);
if (theirPresence && !theirPresence->getStatus().empty()) {
startMessage += " (" + theirPresence->getStatus() + ")";
@@ -69,7 +80,7 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
lastShownStatus_ = theirPresence ? theirPresence->getShow() : StatusShow::None;
chatStateNotifier_->setContactIsOnline(theirPresence && theirPresence->getType() == Presence::Available);
startMessage += ".";
- chatWindow_->addSystemMessage(startMessage);
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(startMessage), ChatWindow::DefaultDirection);
chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_));
chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_));
chatWindow_->onFileTransferStart.connect(boost::bind(&ChatController::handleFileTransferStart, this, _1, _2));
@@ -79,6 +90,8 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
chatWindow_->onWhiteboardSessionAccept.connect(boost::bind(&ChatController::handleWhiteboardSessionAccept, this));
chatWindow_->onWhiteboardSessionCancel.connect(boost::bind(&ChatController::handleWhiteboardSessionCancel, this));
chatWindow_->onWhiteboardWindowShow.connect(boost::bind(&ChatController::handleWhiteboardWindowShow, this));
+ chatWindow_->onBlockUserRequest.connect(boost::bind(&ChatController::handleBlockUserRequest, this));
+ chatWindow_->onUnblockUserRequest.connect(boost::bind(&ChatController::handleUnblockUserRequest, this));
handleBareJIDCapsChanged(toJID_);
settings_->onSettingChanged.connect(boost::bind(&ChatController::handleSettingChanged, this, _1));
@@ -139,6 +152,19 @@ void ChatController::setToJID(const JID& jid) {
handleBareJIDCapsChanged(toJID_);
}
+void ChatController::setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info) {
+ ChatControllerBase::setAvailableServerFeatures(info);
+ if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::BlockingCommandFeature)) {
+ boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList();
+
+ blockingOnStateChangedConnection_ = blockList->onStateChanged.connect(boost::bind(&ChatController::handleBlockingStateChanged, this));
+ blockingOnItemAddedConnection_ = blockList->onItemAdded.connect(boost::bind(&ChatController::handleBlockingItemAdded, this, _1));
+ blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&ChatController::handleBlockingItemRemoved, this, _1));
+
+ handleBlockingStateChanged();
+ }
+}
+
bool ChatController::isIncomingMessageFromMe(boost::shared_ptr<Message>) {
return false;
}
@@ -174,8 +200,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);
+ }
}
@@ -207,13 +236,59 @@ void ChatController::checkForDisplayingDisplayReceiptsAlert() {
}
}
+void ChatController::handleBlockingStateChanged() {
+ boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList();
+ if (blockList->getState() == BlockList::Available) {
+ if (isInMUC_ ? blockList->isBlocked(toJID_) : blockList->isBlocked(toJID_.toBare())) {
+ chatWindow_->setAlert(QT_TRANSLATE_NOOP("", "You've currently blocked this contact. To continue your conversation you have to unblock the contact first."));
+ chatWindow_->setInputEnabled(false);
+ chatWindow_->setBlockingState(ChatWindow::IsBlocked);
+ } else {
+ chatWindow_->setBlockingState(ChatWindow::IsUnblocked);
+ }
+ }
+}
+
+void ChatController::handleBlockingItemAdded(const JID& jid) {
+ if (toJID_ == (isInMUC_ ? jid: jid.toBare())) {
+ chatWindow_->setAlert(QT_TRANSLATE_NOOP("", "You've currently blocked this contact. To continue your conversation you have to unblock the contact first."));
+ chatWindow_->setInputEnabled(false);
+ chatWindow_->setBlockingState(ChatWindow::IsBlocked);
+ }
+}
+
+void ChatController::handleBlockingItemRemoved(const JID& jid) {
+ if (toJID_ == (isInMUC_ ? jid: jid.toBare())) {
+ // FIXME: Support for different types of alerts.
+ chatWindow_->cancelAlert();
+ chatWindow_->setInputEnabled(true);
+ chatWindow_->setBlockingState(ChatWindow::IsUnblocked);
+ }
+}
+
+void ChatController::handleBlockUserRequest() {
+ if (isInMUC_) {
+ eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Blocked, toJID_));
+ } else {
+ eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Blocked, toJID_.toBare()));
+ }
+}
+
+void ChatController::handleUnblockUserRequest() {
+ if (isInMUC_) {
+ eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Unblocked, toJID_));
+ } else {
+ eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Unblocked, toJID_.toBare()));
+ }
+}
+
void ChatController::postSendMessage(const std::string& body, boost::shared_ptr<Stanza> sentStanza) {
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>(), avatarManager_->getAvatarPath(selfJID_), boost::posix_time::microsec_clock::universal_time(), HighlightAction());
}
if (stanzaChannel_->getStreamManagementEnabled() && !myLastMessageUIID_.empty() ) {
@@ -288,7 +363,7 @@ void ChatController::handleFileTransferStart(std::string id, std::string descrip
}
void ChatController::handleFileTransferAccept(std::string id, std::string filename) {
- SWIFT_LOG(debug) "handleFileTransferAccept(" << id << ", " << filename << ")" << std::endl;
+ SWIFT_LOG(debug) << "handleFileTransferAccept(" << id << ", " << filename << ")" << std::endl;
if (ftControllers.find(id) != ftControllers.end()) {
ftControllers[id]->accept(filename);
} else {
@@ -332,6 +407,11 @@ std::string ChatController::getStatusChangeString(boost::shared_ptr<Presence> pr
response = QT_TRANSLATE_NOOP("", "%1% is now busy");
}
}
+ Idle::ref idle;
+ if ((idle = presence->getPayload<Idle>())) {
+ response += str(format(QT_TRANSLATE_NOOP("", " and has been idle since %1%")) % dateTimeToLocalString(idle->getSince()));
+ }
+
if (!response.empty()) {
response = str(format(response) % nick);
}
@@ -366,9 +446,9 @@ void ChatController::handlePresenceChange(boost::shared_ptr<Presence> newPresenc
std::string newStatusChangeString = getStatusChangeString(newPresence);
if (newStatusChangeString != lastStatusChangeString_) {
if (lastWasPresence_) {
- chatWindow_->replaceLastMessage(newStatusChangeString);
+ chatWindow_->replaceLastMessage(chatMessageParser_->parseMessageBody(newStatusChangeString));
} else {
- chatWindow_->addPresenceMessage(newStatusChangeString);
+ chatWindow_->addPresenceMessage(chatMessageParser_->parseMessageBody(newStatusChangeString), ChatWindow::DefaultDirection);
}
lastStatusChangeString_ = newStatusChangeString;
lastWasPresence_ = true;
diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h
index 66ec37d..414af09 100644
--- a/Swift/Controllers/Chat/ChatController.h
+++ b/Swift/Controllers/Chat/ChatController.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2012 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -22,12 +22,15 @@ namespace Swift {
class FileTransferController;
class SettingsProvider;
class HistoryController;
+ class HighlightManager;
+ class ClientBlockListManager;
class ChatController : public ChatControllerBase {
public:
- ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry);
+ ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider, bool userWantsReceipts, SettingsProvider* settings, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, ChatMessageParser* chatMessageParser);
virtual ~ChatController();
virtual void setToJID(const JID& jid);
+ virtual void setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info);
virtual void setOnline(bool online);
virtual void handleNewFileTransferController(FileTransferController* ftc);
virtual void handleWhiteboardSessionRequest(bool senderIsSelf);
@@ -45,7 +48,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;
@@ -66,6 +69,13 @@ namespace Swift {
void handleSettingChanged(const std::string& settingPath);
void checkForDisplayingDisplayReceiptsAlert();
+ void handleBlockingStateChanged();
+ void handleBlockingItemAdded(const JID&);
+ void handleBlockingItemRemoved(const JID&);
+
+ void handleBlockUserRequest();
+ void handleUnblockUserRequest();
+
private:
NickResolver* nickResolver_;
ChatStateNotifier* chatStateNotifier_;
@@ -85,6 +95,11 @@ namespace Swift {
std::map<std::string, FileTransferController*> ftControllers;
SettingsProvider* settings_;
std::string lastWbID_;
+
+ ClientBlockListManager* clientBlockListManager_;
+ boost::bsignals::scoped_connection blockingOnStateChangedConnection_;
+ boost::bsignals::scoped_connection blockingOnItemAddedConnection_;
+ boost::bsignals::scoped_connection blockingOnItemRemovedConnection_;
};
}
diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp
index 50709f7..143e3d2 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.cpp
+++ b/Swift/Controllers/Chat/ChatControllerBase.cpp
@@ -1,10 +1,10 @@
/*
- * Copyright (c) 2010-2012 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swift/Controllers/Chat/ChatControllerBase.h"
+#include <Swift/Controllers/Chat/ChatControllerBase.h>
#include <sstream>
#include <map>
@@ -13,32 +13,39 @@
#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>
#include <Swiften/Base/format.h>
+#include <Swiften/Base/Path.h>
#include <Swiften/Base/String.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Elements/Delay.h>
#include <Swiften/Elements/MUCInvitationPayload.h>
#include <Swiften/Elements/MUCUserPayload.h>
#include <Swiften/Base/foreach.h>
-#include <Swift/Controllers/XMPPEvents/EventController.h>
#include <Swiften/Disco/EntityCapsProvider.h>
-#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
-#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
#include <Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h>
#include <Swiften/Avatars/AvatarManager.h>
+
+#include <Swift/Controllers/Intl.h>
+#include <Swift/Controllers/XMPPEvents/EventController.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
#include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h>
+#include <Swift/Controllers/HighlightManager.h>
+#include <Swift/Controllers/Highlighter.h>
+#include <Swift/Controllers/Chat/ChatMessageParser.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, ChatMessageParser* chatMessageParser) : 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), chatMessageParser_(chatMessageParser) {
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();
}
@@ -61,7 +68,7 @@ void ChatControllerBase::createDayChangeTimer() {
if (timerFactory_) {
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();
+ int millisecondsUntilMidnight = boost::numeric_cast<int>((midnight - now).total_milliseconds());
dateChangeTimer_ = boost::shared_ptr<Timer>(timerFactory_->createTimer(millisecondsUntilMidnight));
dateChangeTimer_->onTick.connect(boost::bind(&ChatControllerBase::handleDayChangeTick, this));
dateChangeTimer_->start();
@@ -71,7 +78,7 @@ void ChatControllerBase::createDayChangeTimer() {
void ChatControllerBase::handleDayChangeTick() {
dateChangeTimer_->stop();
boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
- chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "The day is now %1%")) % std::string(boost::posix_time::to_iso_extended_string(now)).substr(0,10)));
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "The day is now %1%")) % std::string(boost::posix_time::to_iso_extended_string(now)).substr(0,10))), ChatWindow::DefaultDirection);
dayTicked();
createDayChangeTimer();
}
@@ -112,7 +119,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 +182,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 boost::filesystem::path& 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(chatMessageParser_->parseMessageBody(String::getSplittedAtFirst(message, ' ').second), senderName, senderIsSelf, label, pathToString(avatarPath), time, highlight);
} else {
- return chatWindow_->addMessage(message, senderName, senderIsSelf, label, avatarPath, time);
+ return chatWindow_->addMessage(chatMessageParser_->parseMessageBody(message), senderName, senderIsSelf, label, pathToString(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(chatMessageParser_->parseMessageBody(String::getSplittedAtFirst(message, ' ').second), id, time, highlight);
} else {
- chatWindow_->replaceMessage(message, id, time);
+ chatWindow_->replaceMessage(chatMessageParser_->parseMessageBody(message), id, time, highlight);
}
}
@@ -205,9 +212,12 @@ 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);
+ if (!message->getTo().getResource().empty()) {
+ std::string errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't send message: %1%")) % getErrorMessage(message->getPayload<ErrorPayload>()));
+ chatWindow_->addErrorMessage(chatMessageParser_->parseMessageBody(errorMessage));
+ }
}
else if (messageEvent->getStanza()->getPayload<MUCInvitationPayload>()) {
handleMUCInvitation(messageEvent->getStanza());
@@ -231,7 +241,7 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m
boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
std::ostringstream s;
s << "The following message took " << (now - delayPayloads[i]->getStamp()).total_milliseconds() / 1000.0 << " seconds to be delivered from " << delayPayloads[i]->getFrom()->toString() << ".";
- chatWindow_->addSystemMessage(std::string(s.str()));
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(std::string(s.str())), ChatWindow::DefaultDirection);
}
boost::shared_ptr<SecurityLabel> label = message->getPayload<SecurityLabel>();
@@ -243,6 +253,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 +265,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, avatarManager_->getAvatarPath(from), 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 +311,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);
@@ -331,6 +347,4 @@ void ChatControllerBase::handleMediatedMUCInvitation(Message::ref message) {
handleGeneralMUCInvitation(inviteEvent);
}
-
-
}
diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h
index b26af02..129c75b 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.h
+++ b/Swift/Controllers/Chat/ChatControllerBase.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2012 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -8,50 +8,56 @@
#include <map>
#include <vector>
+#include <string>
+
#include <boost/shared_ptr.hpp>
-#include "Swiften/Base/boost_bsignals.h"
-#include <boost/filesystem.hpp>
+#include <boost/filesystem/path.hpp>
#include <boost/optional.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
-#include "Swiften/Network/Timer.h"
-#include "Swiften/Network/TimerFactory.h"
-#include "Swiften/Elements/Stanza.h"
-#include <string>
-#include "Swiften/Elements/DiscoInfo.h"
-#include "Swift/Controllers/XMPPEvents/MessageEvent.h"
+#include <Swiften/Network/Timer.h>
+#include <Swiften/Network/TimerFactory.h>
+#include <Swiften/Elements/Stanza.h>
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Elements/DiscoInfo.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/SecurityLabelsCatalog.h>
+#include <Swiften/Elements/ErrorPayload.h>
+#include <Swiften/Presence/PresenceOracle.h>
+#include <Swiften/Queries/IQRouter.h>
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/MUC/MUCRegistry.h>
+
+#include <Swift/Controllers/XMPPEvents/MessageEvent.h>
#include <Swift/Controllers/XMPPEvents/MUCInviteEvent.h>
-#include "Swiften/JID/JID.h"
-#include "Swiften/Elements/SecurityLabelsCatalog.h"
-#include "Swiften/Elements/ErrorPayload.h"
-#include "Swiften/Presence/PresenceOracle.h"
-#include "Swiften/Queries/IQRouter.h"
-#include "Swiften/Base/IDGenerator.h"
#include <Swift/Controllers/HistoryController.h>
-#include <Swiften/MUC/MUCRegistry.h>
+#include <Swift/Controllers/HighlightManager.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
namespace Swift {
class IQRouter;
class StanzaChannel;
- class ChatWindow;
class ChatWindowFactory;
class AvatarManager;
class UIEventStream;
class EventController;
class EntityCapsProvider;
+ class HighlightManager;
+ class Highlighter;
+ class ChatMessageParser;
class ChatControllerBase : public boost::bsignals::trackable {
public:
virtual ~ChatControllerBase();
void showChatWindow();
void activateChatWindow();
- void setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info);
+ virtual void setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info);
void handleIncomingMessage(boost::shared_ptr<MessageEvent> message);
- std::string addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time);
- 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 boost::filesystem::path& 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 +66,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, ChatMessageParser* chatMessageParser);
/**
* 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 +122,7 @@ namespace Swift {
SecurityLabelsCatalog::Item lastLabel_;
HistoryController* historyController_;
MUCRegistry* mucRegistry_;
+ Highlighter* highlighter_;
+ ChatMessageParser* chatMessageParser_;
};
}
diff --git a/Swift/Controllers/Chat/ChatMessageParser.cpp b/Swift/Controllers/Chat/ChatMessageParser.cpp
new file mode 100644
index 0000000..ce184ea
--- /dev/null
+++ b/Swift/Controllers/Chat/ChatMessageParser.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2013 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swift/Controllers/Chat/ChatMessageParser.h>
+
+#include <vector>
+#include <utility>
+
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include <Swiften/Base/Regex.h>
+#include <Swiften/Base/foreach.h>
+
+#include <SwifTools/Linkify.h>
+
+
+namespace Swift {
+
+ ChatMessageParser::ChatMessageParser(const std::map<std::string, std::string>& emoticons) : emoticons_(emoticons) {
+
+ }
+
+ typedef std::pair<std::string, std::string> StringPair;
+
+ ChatWindow::ChatMessage ChatMessageParser::parseMessageBody(const std::string& body) {
+ ChatWindow::ChatMessage parsedMessage;
+ std::string remaining = body;
+ /* Parse one, URLs */
+ while (!remaining.empty()) {
+ bool found = false;
+ std::pair<std::vector<std::string>, size_t> links = Linkify::splitLink(remaining);
+ remaining = "";
+ for (size_t i = 0; i < links.first.size(); i++) {
+ const std::string& part = links.first[i];
+ if (found) {
+ // Must be on the last part, then
+ remaining = part;
+ }
+ else {
+ if (i == links.second) {
+ found = true;
+ parsedMessage.append(boost::make_shared<ChatWindow::ChatURIMessagePart>(part));
+ }
+ else {
+ parsedMessage.append(boost::make_shared<ChatWindow::ChatTextMessagePart>(part));
+ }
+ }
+ }
+ }
+
+
+
+ std::string regexString;
+ /* Parse two, emoticons */
+ foreach (StringPair emoticon, emoticons_) {
+ /* Construct a regexp that finds an instance of any of the emoticons inside a group */
+ regexString += regexString.empty() ? "(" : "|";
+ regexString += Regex::escape(emoticon.first);
+ }
+ if (!regexString.empty()) {
+ regexString += ")";
+ boost::regex emoticonRegex(regexString);
+
+ ChatWindow::ChatMessage newMessage;
+ foreach (boost::shared_ptr<ChatWindow::ChatMessagePart> part, parsedMessage.getParts()) {
+ boost::shared_ptr<ChatWindow::ChatTextMessagePart> textPart;
+ if ((textPart = boost::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(part))) {
+ try {
+ boost::match_results<std::string::const_iterator> match;
+ const std::string& text = textPart->text;
+ std::string::const_iterator start = text.begin();
+ while (regex_search(start, text.end(), match, emoticonRegex)) {
+ std::string::const_iterator matchStart = match[0].first;
+ std::string::const_iterator matchEnd = match[0].second;
+ if (start != matchStart) {
+ /* If we're skipping over plain text since the previous emoticon, record it as plain text */
+ newMessage.append(boost::make_shared<ChatWindow::ChatTextMessagePart>(std::string(start, matchStart)));
+ }
+ boost::shared_ptr<ChatWindow::ChatEmoticonMessagePart> emoticonPart = boost::make_shared<ChatWindow::ChatEmoticonMessagePart>();
+ std::map<std::string, std::string>::const_iterator emoticonIterator = emoticons_.find(match.str());
+ assert (emoticonIterator != emoticons_.end());
+ const StringPair& emoticon = *emoticonIterator;
+ emoticonPart->imagePath = emoticon.second;
+ emoticonPart->alternativeText = emoticon.first;
+ newMessage.append(emoticonPart);
+ start = matchEnd;
+ }
+ if (start != text.end()) {
+ /* If there's plain text after the last emoticon, record it */
+ newMessage.append(boost::make_shared<ChatWindow::ChatTextMessagePart>(std::string(start, text.end())));
+ }
+
+ }
+ catch (std::runtime_error) {
+ /* Basically too expensive to compute the regex results and it gave up, so pass through as text */
+ newMessage.append(part);
+ }
+ }
+ else {
+ newMessage.append(part);
+ }
+ }
+ parsedMessage = newMessage;
+
+ }
+ return parsedMessage;
+ }
+}
diff --git a/Swift/Controllers/Chat/ChatMessageParser.h b/Swift/Controllers/Chat/ChatMessageParser.h
new file mode 100644
index 0000000..c9b9456
--- /dev/null
+++ b/Swift/Controllers/Chat/ChatMessageParser.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2013 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+
+namespace Swift {
+
+ class ChatMessageParser {
+ public:
+ ChatMessageParser(const std::map<std::string, std::string>& emoticons);
+ ChatWindow::ChatMessage parseMessageBody(const std::string& body);
+ private:
+ std::map<std::string, std::string> emoticons_;
+
+ };
+}
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 1e0e9c2..415931c 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -1,16 +1,30 @@
/*
- * Copyright (c) 2010-2011 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Swift/Controllers/Chat/ChatsManager.h"
+#include <Swift/Controllers/Chat/ChatsManager.h>
#include <boost/bind.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <Swiften/Base/foreach.h>
+#include <Swiften/Presence/PresenceSender.h>
+#include <Swiften/Client/NickResolver.h>
+#include <Swiften/MUC/MUCManager.h>
+#include <Swiften/Elements/ChatState.h>
+#include <Swiften/Elements/MUCUserPayload.h>
+#include <Swiften/Elements/DeliveryReceipt.h>
+#include <Swiften/Elements/DeliveryReceiptRequest.h>
+#include <Swiften/MUC/MUCBookmarkManager.h>
+#include <Swiften/Avatars/AvatarManager.h>
+#include <Swiften/Elements/MUCInvitationPayload.h>
+#include <Swiften/Roster/XMPPRoster.h>
+#include <Swiften/Client/ClientBlockListManager.h>
+#include <Swiften/Client/StanzaChannel.h>
+
#include <Swift/Controllers/Chat/ChatController.h>
#include <Swift/Controllers/Chat/ChatControllerBase.h>
#include <Swift/Controllers/Chat/MUCSearchController.h>
@@ -25,24 +39,13 @@
#include <Swift/Controllers/UIInterfaces/ChatListWindowFactory.h>
#include <Swift/Controllers/UIInterfaces/JoinMUCWindow.h>
#include <Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h>
-#include <Swiften/Presence/PresenceSender.h>
-#include <Swiften/Client/NickResolver.h>
-#include <Swiften/MUC/MUCManager.h>
-#include <Swiften/Elements/ChatState.h>
-#include <Swiften/Elements/MUCUserPayload.h>
-#include <Swiften/Elements/DeliveryReceipt.h>
-#include <Swiften/Elements/DeliveryReceiptRequest.h>
-#include <Swiften/MUC/MUCBookmarkManager.h>
#include <Swift/Controllers/FileTransfer/FileTransferController.h>
#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
#include <Swift/Controllers/ProfileSettingsProvider.h>
-#include <Swiften/Avatars/AvatarManager.h>
-#include <Swiften/Elements/MUCInvitationPayload.h>
-#include <Swiften/Roster/XMPPRoster.h>
#include <Swift/Controllers/Settings/SettingsProvider.h>
#include <Swift/Controllers/SettingConstants.h>
-#include <Swiften/Client/StanzaChannel.h>
#include <Swift/Controllers/WhiteboardManager.h>
+#include <Swift/Controllers/Chat/ChatMessageParser.h>
namespace Swift {
@@ -74,7 +77,10 @@ ChatsManager::ChatsManager(
bool eagleMode,
SettingsProvider* settings,
HistoryController* historyController,
- WhiteboardManager* whiteboardManager) :
+ WhiteboardManager* whiteboardManager,
+ HighlightManager* highlightManager,
+ ClientBlockListManager* clientBlockListManager,
+ const std::map<std::string, std::string>& emoticons) :
jid_(jid),
joinMUCWindowFactory_(joinMUCWindowFactory),
useDelayForLatency_(useDelayForLatency),
@@ -86,7 +92,9 @@ ChatsManager::ChatsManager(
eagleMode_(eagleMode),
settings_(settings),
historyController_(historyController),
- whiteboardManager_(whiteboardManager) {
+ whiteboardManager_(whiteboardManager),
+ highlightManager_(highlightManager),
+ clientBlockListManager_(clientBlockListManager) {
timerFactory_ = timerFactory;
eventController_ = eventController;
stanzaChannel_ = stanzaChannel;
@@ -100,6 +108,7 @@ ChatsManager::ChatsManager(
uiEventStream_ = uiEventStream;
mucBookmarkManager_ = NULL;
profileSettings_ = profileSettings;
+ chatMessageParser_ = new ChatMessageParser(emoticons);
presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1));
uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&ChatsManager::handleUIEvent, this, _1));
@@ -144,6 +153,7 @@ ChatsManager::~ChatsManager() {
}
delete mucBookmarkManager_;
delete mucSearchController_;
+ delete chatMessageParser_;
}
void ChatsManager::saveRecents() {
@@ -521,7 +531,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_, clientBlockListManager_, chatMessageParser_);
chatControllers_[contact] = controller;
controller->setAvailableServerFeatures(serverDiscoInfo_);
controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, contact, _1, false));
@@ -594,7 +604,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_, chatMessageParser_);
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..70c1ec8 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2011 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -7,19 +7,21 @@
#pragma once
#include <map>
+#include <string>
#include <boost/shared_ptr.hpp>
-#include <string>
#include <Swiften/Elements/DiscoInfo.h>
#include <Swiften/Elements/Message.h>
#include <Swiften/Elements/Presence.h>
#include <Swiften/JID/JID.h>
#include <Swiften/MUC/MUCRegistry.h>
+#include <Swiften/MUC/MUCBookmark.h>
+
#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <Swift/Controllers/UIInterfaces/ChatListWindow.h>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
-#include <Swiften/MUC/MUCBookmark.h>
+
namespace Swift {
class EventController;
@@ -50,10 +52,13 @@ namespace Swift {
class SettingsProvider;
class WhiteboardManager;
class HistoryController;
+ class HighlightManager;
+ class ClientBlockListManager;
+ class ChatMessageParser;
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, ClientBlockListManager* clientBlockListManager, const std::map<std::string, std::string>& emoticons);
virtual ~ChatsManager();
void setAvatarManager(AvatarManager* avatarManager);
void setOnline(bool enabled);
@@ -136,5 +141,8 @@ namespace Swift {
SettingsProvider* settings_;
HistoryController* historyController_;
WhiteboardManager* whiteboardManager_;
+ HighlightManager* highlightManager_;
+ ClientBlockListManager* clientBlockListManager_;
+ ChatMessageParser* chatMessageParser_;
};
}
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index 50eee68..c41c078 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -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,8 @@
#include <Swift/Controllers/Roster/SetPresence.h>
#include <Swiften/Disco/EntityCapsProvider.h>
#include <Swiften/Roster/XMPPRoster.h>
+#include <Swift/Controllers/Highlighter.h>
+#include <Swift/Controllers/Chat/ChatMessageParser.h>
#define MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS 60000
@@ -61,8 +64,10 @@ 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,
+ ChatMessageParser* chatMessageParser) :
+ ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser), muc_(muc), nick_(nick), desiredNick_(nick), password_(password), renameCounter_(0) {
parting_ = true;
joined_ = false;
lastWasPresence_ = false;
@@ -98,6 +103,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 +153,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 +172,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;
}
}
@@ -219,7 +228,7 @@ const std::string& MUCController::getNick() {
void MUCController::handleJoinTimeoutTick() {
receivedActivity();
- chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "Room %1% is not responding. This operation may never complete.")) % toJID_.toString()));
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "Room %1% is not responding. This operation may never complete.")) % toJID_.toString())), ChatWindow::DefaultDirection);
}
void MUCController::receivedActivity() {
@@ -228,6 +237,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");
@@ -267,20 +279,24 @@ void MUCController::handleJoinFailed(boost::shared_ptr<ErrorPayload> error) {
}
}
errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't join room: %1%.")) % errorMessage);
- chatWindow_->addErrorMessage(errorMessage);
+ chatWindow_->addErrorMessage(chatMessageParser_->parseMessageBody(errorMessage));
parting_ = true;
- if (!rejoinNick.empty()) {
- nick_ = rejoinNick;
+ if (!rejoinNick.empty() && renameCounter_ < 10) {
+ renameCounter_++;
+ setNick(rejoinNick);
rejoin();
}
}
+#pragma clang diagnostic pop
+
void MUCController::handleJoinComplete(const std::string& nick) {
receivedActivity();
+ renameCounter_ = 0;
joined_ = true;
std::string joinMessage = str(format(QT_TRANSLATE_NOOP("", "You have entered room %1% as %2%.")) % toJID_.toString() % nick);
- nick_ = nick;
- chatWindow_->addSystemMessage(joinMessage);
+ setNick(nick);
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(joinMessage), ChatWindow::DefaultDirection);
#ifdef SWIFT_EXPERIMENTAL_HISTORY
addRecentLogs();
@@ -298,8 +314,7 @@ void MUCController::handleAvatarChanged(const JID& jid) {
if (parting_ || !jid.equals(toJID_, JID::WithoutResource)) {
return;
}
- std::string path = avatarManager_->getAvatarPath(jid).string();
- roster_->applyOnItems(SetAvatar(jid, path, JID::WithResource));
+ roster_->applyOnItems(SetAvatar(jid, avatarManager_->getAvatarPath(jid), JID::WithResource));
}
void MUCController::handleWindowClosed() {
@@ -323,7 +338,7 @@ void MUCController::handleOccupantJoined(const MUCOccupant& occupant) {
NickJoinPart event(occupant.getNick(), Join);
appendToJoinParts(joinParts_, event);
std::string groupName(roleToGroupName(occupant.getRole()));
- roster_->addContact(jid, realJID, occupant.getNick(), groupName, avatarManager_->getAvatarPath(jid).string());
+ roster_->addContact(jid, realJID, occupant.getNick(), groupName, avatarManager_->getAvatarPath(jid));
roster_->getGroup(groupName)->setManualSort(roleToSortName(occupant.getRole()));
if (joined_) {
std::string joinString;
@@ -338,7 +353,6 @@ void MUCController::handleOccupantJoined(const MUCOccupant& occupant) {
updateJoinParts();
} else {
addPresenceMessage(joinString);
-
}
}
if (avatarManager_ != NULL) {
@@ -348,7 +362,7 @@ void MUCController::handleOccupantJoined(const MUCOccupant& occupant) {
void MUCController::addPresenceMessage(const std::string& message) {
lastWasPresence_ = true;
- chatWindow_->addPresenceMessage(message);
+ chatWindow_->addPresenceMessage(chatMessageParser_->parseMessageBody(message), ChatWindow::DefaultDirection);
}
@@ -386,6 +400,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 +411,7 @@ std::string MUCController::roleToSortName(MUCOccupant::Role role) {
case MUCOccupant::Visitor: return "3";
case MUCOccupant::NoRole: return "4";
}
+ assert(false);
return "5";
}
@@ -433,7 +449,7 @@ void MUCController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> mes
joined_ = true;
if (message->hasSubject() && message->getBody().empty()) {
- chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "The room subject is now: %1%")) % message->getSubject()));;
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "The room subject is now: %1%")) % message->getSubject())), ChatWindow::DefaultDirection);;
chatWindow_->setSubject(message->getSubject());
doneGettingHistory_ = true;
}
@@ -448,10 +464,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);
+ }
}
}
@@ -465,9 +484,9 @@ void MUCController::handleOccupantRoleChanged(const std::string& nick, const MUC
realJID = occupant.getRealJID().get();
}
std::string group(roleToGroupName(occupant.getRole()));
- roster_->addContact(jid, realJID, nick, group, avatarManager_->getAvatarPath(jid).string());
+ roster_->addContact(jid, realJID, nick, group, avatarManager_->getAvatarPath(jid));
roster_->getGroup(group)->setManualSort(roleToSortName(occupant.getRole()));
- chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "%1% is now a %2%")) % nick % roleToFriendlyName(occupant.getRole())));
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "%1% is now a %2%")) % nick % roleToFriendlyName(occupant.getRole()))), ChatWindow::DefaultDirection);
if (nick == nick_) {
setAvailableRoomActions(occupant.getAffiliation(), occupant.getRole());
}
@@ -487,7 +506,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;
}
@@ -500,11 +518,12 @@ void MUCController::setOnline(bool online) {
processUserPart();
} else {
if (shouldJoinOnReconnect_) {
- chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "Trying to enter room %1%")) % toJID_.toString()));
+ renameCounter_ = 0;
+ chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(str(format(QT_TRANSLATE_NOOP("", "Trying to enter room %1%")) % toJID_.toString())), ChatWindow::DefaultDirection);
if (loginCheckTimer_) {
loginCheckTimer_->start();
}
- nick_ = desiredNick_;
+ setNick(desiredNick_);
rejoin();
}
}
@@ -598,7 +617,7 @@ boost::optional<boost::posix_time::ptime> MUCController::getMessageTimestamp(boo
}
void MUCController::updateJoinParts() {
- chatWindow_->replaceLastMessage(generateJoinPartString(joinParts_));
+ chatWindow_->replaceLastMessage(chatMessageParser_->parseMessageBody(generateJoinPartString(joinParts_)));
}
void MUCController::appendToJoinParts(std::vector<NickJoinPart>& joinParts, const NickJoinPart& newEvent) {
@@ -611,7 +630,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;
@@ -717,13 +737,13 @@ void MUCController::handleConfigureRequest(Form::ref form) {
void MUCController::handleConfigurationFailed(ErrorPayload::ref error) {
std::string errorMessage = getErrorMessage(error);
errorMessage = str(format(QT_TRANSLATE_NOOP("", "Room configuration failed: %1%.")) % errorMessage);
- chatWindow_->addErrorMessage(errorMessage);
+ chatWindow_->addErrorMessage(chatMessageParser_->parseMessageBody(errorMessage));
}
void MUCController::handleOccupantRoleChangeFailed(ErrorPayload::ref error, const JID&, MUCOccupant::Role) {
std::string errorMessage = getErrorMessage(error);
errorMessage = str(format(QT_TRANSLATE_NOOP("", "Occupant role change failed: %1%.")) % errorMessage);
- chatWindow_->addErrorMessage(errorMessage);
+ chatWindow_->addErrorMessage(chatMessageParser_->parseMessageBody(errorMessage));
}
void MUCController::handleConfigurationFormReceived(Form::ref form) {
@@ -811,7 +831,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()), avatarManager_->getAvatarPath(message.getFromJID()), message.getTime() - boost::posix_time::hours(message.getOffset()), HighlightAction());
}
}
@@ -840,4 +860,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..cad0c94 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -1,24 +1,27 @@
/*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
+#include <set>
+#include <string>
+#include <map>
+
#include <boost/shared_ptr.hpp>
-#include <Swiften/Base/boost_bsignals.h>
#include <boost/signals/connection.hpp>
-#include <set>
-#include <string>
+#include <Swiften/Base/boost_bsignals.h>
#include <Swiften/Network/Timer.h>
-#include <Swift/Controllers/Chat/ChatControllerBase.h>
#include <Swiften/Elements/Message.h>
#include <Swiften/Elements/DiscoInfo.h>
#include <Swiften/JID/JID.h>
#include <Swiften/MUC/MUC.h>
#include <Swiften/Elements/MUCOccupant.h>
+
+#include <Swift/Controllers/Chat/ChatControllerBase.h>
#include <Swift/Controllers/Roster/RosterItem.h>
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
@@ -33,18 +36,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, ChatMessageParser* chatMessageParser);
~MUCController();
boost::signal<void ()> onUserLeft;
boost::signal<void ()> onUserJoined;
@@ -62,7 +66,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 +112,7 @@ namespace Swift {
void handleInviteToMUCWindowCompleted();
void addRecentLogs();
void checkDuplicates(boost::shared_ptr<Message> newMessage);
+ void setNick(const std::string& nick);
private:
MUC::ref muc_;
@@ -130,6 +135,7 @@ namespace Swift {
InviteToChatWindow* inviteWindow_;
XMPPRoster* xmppRoster_;
std::vector<HistoryMessage> joinContext_;
+ size_t renameCounter_;
};
}
diff --git a/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp
new file mode 100644
index 0000000..44d7834
--- /dev/null
+++ b/Swift/Controllers/Chat/UnitTest/ChatMessageParserTest.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <hippomocks.h>
+
+#include <Swift/Controllers/Chat/ChatMessageParser.h>
+
+using namespace Swift;
+
+class ChatMessageParserTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(ChatMessageParserTest);
+ CPPUNIT_TEST(testFullBody);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp() {
+ smile1_ = ":)";
+ smile1Path_ = "/blah/smile1.png";
+ smile2_ = ":(";
+ smile2Path_ = "/blah/smile2.jpg";
+ emoticons_[smile1_] = smile1Path_;
+ emoticons_[smile2_] = smile2Path_;
+ }
+
+ void tearDown() {
+ emoticons_.clear();
+ }
+
+ void assertText(const ChatWindow::ChatMessage& result, size_t index, const std::string& text) {
+ boost::shared_ptr<ChatWindow::ChatTextMessagePart> part = boost::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(result.getParts()[index]);
+ CPPUNIT_ASSERT_EQUAL(text, part->text);
+ }
+
+ void assertEmoticon(const ChatWindow::ChatMessage& result, size_t index, const std::string& text, const std::string& path) {
+ boost::shared_ptr<ChatWindow::ChatEmoticonMessagePart> part = boost::dynamic_pointer_cast<ChatWindow::ChatEmoticonMessagePart>(result.getParts()[index]);
+ CPPUNIT_ASSERT_EQUAL(text, part->alternativeText);
+ CPPUNIT_ASSERT_EQUAL(path, part->imagePath);
+ }
+
+ void assertURL(const ChatWindow::ChatMessage& result, size_t index, const std::string& text) {
+ boost::shared_ptr<ChatWindow::ChatURIMessagePart> part = boost::dynamic_pointer_cast<ChatWindow::ChatURIMessagePart>(result.getParts()[index]);
+ CPPUNIT_ASSERT_EQUAL(text, part->target);
+ }
+
+ void testFullBody() {
+ ChatMessageParser testling(emoticons_);
+ ChatWindow::ChatMessage result = testling.parseMessageBody(":) shiny :( :) http://wonderland.lit/blah http://denmark.lit boom boom");
+ assertEmoticon(result, 0, smile1_, smile1Path_);
+ assertText(result, 1, " shiny ");
+ assertEmoticon(result, 2, smile2_, smile2Path_);
+ assertText(result, 3, " ");
+ assertEmoticon(result, 4, smile1_, smile1Path_);
+ assertText(result, 5, " ");
+ assertURL(result, 6, "http://wonderland.lit/blah");
+ assertText(result, 7, " ");
+ assertURL(result, 8, "http://denmark.lit");
+ assertText(result, 9, " boom boom");
+ }
+
+private:
+ std::map<std::string, std::string> emoticons_;
+ std::string smile1_;
+ std::string smile1Path_;
+ std::string smile2_;
+ std::string smile2Path_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ChatMessageParserTest);
+
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index 482b19c..3c14bae 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -10,48 +10,49 @@
#include <boost/bind.hpp>
-#include "Swift/Controllers/Chat/ChatsManager.h"
-
-#include "Swift/Controllers/Chat/UnitTest/MockChatListWindow.h"
-#include "Swift/Controllers/UIInterfaces/ChatWindow.h"
-#include "Swift/Controllers/Settings/DummySettingsProvider.h"
-#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"
-#include "Swift/Controllers/UIInterfaces/ChatListWindowFactory.h"
-#include "Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h"
-#include "Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h"
-#include "Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h"
-#include "Swiften/Client/Client.h"
-#include "Swiften/Disco/EntityCapsManager.h"
-#include "Swiften/Disco/CapsProvider.h"
-#include "Swiften/MUC/MUCManager.h"
-#include "Swift/Controllers/Chat/ChatController.h"
-#include "Swift/Controllers/XMPPEvents/EventController.h"
-#include "Swift/Controllers/Chat/MUCController.h"
-#include "Swiften/Presence/StanzaChannelPresenceSender.h"
-#include "Swiften/Avatars/NullAvatarManager.h"
-#include "Swiften/Avatars/AvatarMemoryStorage.h"
-#include "Swiften/VCards/VCardManager.h"
-#include "Swiften/VCards/VCardMemoryStorage.h"
-#include "Swiften/Client/NickResolver.h"
-#include "Swiften/Presence/DirectedPresenceSender.h"
-#include "Swiften/Roster/XMPPRosterImpl.h"
-#include "Swift/Controllers/UnitTest/MockChatWindow.h"
-#include "Swiften/Client/DummyStanzaChannel.h"
-#include "Swiften/Queries/DummyIQChannel.h"
-#include "Swiften/Presence/PresenceOracle.h"
-#include "Swiften/Jingle/JingleSessionManager.h"
-#include "Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h"
-#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
-#include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"
-#include "Swift/Controllers/UIEvents/UIEventStream.h"
+#include <Swift/Controllers/Chat/ChatsManager.h>
+
+#include <Swift/Controllers/Chat/UnitTest/MockChatListWindow.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+#include <Swift/Controllers/Settings/DummySettingsProvider.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
+#include <Swift/Controllers/UIInterfaces/ChatListWindowFactory.h>
+#include <Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h>
+#include <Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h>
+#include <Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h>
+#include <Swiften/Client/Client.h>
+#include <Swiften/Disco/EntityCapsManager.h>
+#include <Swiften/Disco/CapsProvider.h>
+#include <Swiften/MUC/MUCManager.h>
+#include <Swift/Controllers/Chat/ChatController.h>
+#include <Swift/Controllers/XMPPEvents/EventController.h>
+#include <Swift/Controllers/Chat/MUCController.h>
+#include <Swiften/Presence/StanzaChannelPresenceSender.h>
+#include <Swiften/Avatars/NullAvatarManager.h>
+#include <Swiften/Avatars/AvatarMemoryStorage.h>
+#include <Swiften/VCards/VCardManager.h>
+#include <Swiften/VCards/VCardMemoryStorage.h>
+#include <Swiften/Client/NickResolver.h>
+#include <Swiften/Presence/DirectedPresenceSender.h>
+#include <Swiften/Roster/XMPPRosterImpl.h>
+#include <Swift/Controllers/UnitTest/MockChatWindow.h>
+#include <Swiften/Client/DummyStanzaChannel.h>
+#include <Swiften/Queries/DummyIQChannel.h>
+#include <Swiften/Presence/PresenceOracle.h>
+#include <Swiften/Jingle/JingleSessionManager.h>
+#include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h>
+#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
+#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <Swift/Controllers/ProfileSettingsProvider.h>
-#include "Swift/Controllers/FileTransfer/FileTransferOverview.h"
-#include "Swiften/Elements/DeliveryReceiptRequest.h"
-#include "Swiften/Elements/DeliveryReceipt.h"
+#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
+#include <Swiften/Elements/DeliveryReceiptRequest.h>
+#include <Swiften/Elements/DeliveryReceipt.h>
#include <Swiften/Base/Algorithm.h>
#include <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/WhiteboardManager.h>
#include <Swiften/Whiteboard/WhiteboardSessionManager.h>
+#include <Swiften/Client/ClientBlockListManager.h>
using namespace Swift;
@@ -106,18 +107,22 @@ 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_);
+ clientBlockListManager_ = new ClientBlockListManager(iqRouter_);
+ manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, xmppRoster_, false, settings_, NULL, wbManager_, highlightManager_, clientBlockListManager_, emoticons_);
manager_->setAvatarManager(avatarManager_);
- };
+ }
void tearDown() {
+ delete highlightManager_;
//delete chatListWindowFactory
delete profileSettings_;
delete avatarManager_;
delete manager_;
+ delete clientBlockListManager_;
delete ftOverview_;
delete ftManager_;
delete wbSessionManager_;
@@ -481,6 +486,9 @@ private:
FileTransferManager* ftManager_;
WhiteboardSessionManager* wbSessionManager_;
WhiteboardManager* wbManager_;
+ HighlightManager* highlightManager_;
+ ClientBlockListManager* clientBlockListManager_;
+ std::map<std::string, std::string> emoticons_;
};
CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest);
diff --git a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
index 4f37229..0fc6a18 100644
--- a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -26,6 +26,8 @@
#include "Swiften/Network/TimerFactory.h"
#include "Swiften/Elements/MUCUserPayload.h"
#include "Swiften/Disco/DummyEntityCapsProvider.h"
+#include <Swift/Controllers/Settings/DummySettingsProvider.h>
+#include <Swift/Controllers/Chat/ChatMessageParser.h>
using namespace Swift;
@@ -62,12 +64,17 @@ 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_);
- };
+ chatMessageParser_ = new ChatMessageParser(std::map<std::string, std::string>());
+ controller_ = new MUCController (self_, muc_, boost::optional<std::string>(), nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_, entityCapsProvider_, NULL, NULL, mucRegistry_, highlightManager_, chatMessageParser_);
+ }
void tearDown() {
+ delete highlightManager_;
+ delete settings_;
delete entityCapsProvider_;
delete controller_;
delete eventController_;
@@ -81,6 +88,7 @@ public:
delete iqChannel_;
delete mucRegistry_;
delete avatarManager_;
+ delete chatMessageParser_;
}
void finishJoin() {
@@ -338,6 +346,9 @@ private:
MockChatWindow* window_;
MUCRegistry* mucRegistry_;
DummyEntityCapsProvider* entityCapsProvider_;
+ DummySettingsProvider* settings_;
+ HighlightManager* highlightManager_;
+ ChatMessageParser* chatMessageParser_;
};
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..0160a7a 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>
@@ -24,7 +25,7 @@ FileTransferController::FileTransferController(const JID& receipient, const std:
}
FileTransferController::FileTransferController(IncomingFileTransfer::ref transfer) :
- sending(false), otherParty(transfer->getSender()), filename(transfer->filename), transfer(transfer), ftManager(0), ftProgressInfo(0), chatWindow(0), currentState(FileTransfer::State::WaitingForStart) {
+ sending(false), otherParty(transfer->getSender()), filename(transfer->getFileName()), transfer(transfer), ftManager(0), ftProgressInfo(0), chatWindow(0), currentState(FileTransfer::State::WaitingForStart) {
}
@@ -41,7 +42,7 @@ std::string FileTransferController::setChatWindow(ChatWindow* wnd, std::string n
if (sending) {
uiID = wnd->addFileTransfer(QT_TRANSLATE_NOOP("", "me"), true, filename, boost::filesystem::file_size(boost::filesystem::path(filename)));
} else {
- uiID = wnd->addFileTransfer(nickname, false, filename, transfer->fileSizeInBytes);
+ uiID = wnd->addFileTransfer(nickname, false, filename, transfer->getFileSizeInBytes());
}
return uiID;
}
@@ -64,7 +65,7 @@ int FileTransferController::getProgress() const {
boost::uintmax_t FileTransferController::getSize() const {
if (transfer) {
- return transfer->fileSizeInBytes;
+ return transfer->getFileSizeInBytes();
} else {
return 0;
}
@@ -75,9 +76,9 @@ void FileTransferController::start(std::string& description) {
fileReadStream = boost::make_shared<FileReadBytestream>(boost::filesystem::path(filename));
OutgoingFileTransfer::ref outgoingTransfer = ftManager->createOutgoingFileTransfer(otherParty, boost::filesystem::path(filename), description, fileReadStream);
if (outgoingTransfer) {
- ftProgressInfo = new FileTransferProgressInfo(outgoingTransfer->fileSizeInBytes);
+ ftProgressInfo = new FileTransferProgressInfo(outgoingTransfer->getFileSizeInBytes());
ftProgressInfo->onProgressPercentage.connect(boost::bind(&FileTransferController::handleProgressPercentageChange, this, _1));
- outgoingTransfer->onStateChange.connect(boost::bind(&FileTransferController::handleFileTransferStateChange, this, _1));
+ outgoingTransfer->onStateChanged.connect(boost::bind(&FileTransferController::handleFileTransferStateChange, this, _1));
outgoingTransfer->onProcessedBytes.connect(boost::bind(&FileTransferProgressInfo::setBytesProcessed, ftProgressInfo, _1));
outgoingTransfer->start();
transfer = outgoingTransfer;
@@ -92,9 +93,9 @@ void FileTransferController::accept(std::string& file) {
if (incomingTransfer) {
fileWriteStream = boost::make_shared<FileWriteBytestream>(boost::filesystem::path(file));
- ftProgressInfo = new FileTransferProgressInfo(transfer->fileSizeInBytes);
+ ftProgressInfo = new FileTransferProgressInfo(transfer->getFileSizeInBytes());
ftProgressInfo->onProgressPercentage.connect(boost::bind(&FileTransferController::handleProgressPercentageChange, this, _1));
- transfer->onStateChange.connect(boost::bind(&FileTransferController::handleFileTransferStateChange, this, _1));
+ transfer->onStateChanged.connect(boost::bind(&FileTransferController::handleFileTransferStateChange, this, _1));
transfer->onProcessedBytes.connect(boost::bind(&FileTransferProgressInfo::setBytesProcessed, ftProgressInfo, _1));
incomingTransfer->accept(fileWriteStream);
} else {
@@ -113,7 +114,10 @@ void FileTransferController::cancel() {
void FileTransferController::handleFileTransferStateChange(FileTransfer::State state) {
currentState = state;
onStateChage();
- switch(state.state) {
+ switch(state.type) {
+ case FileTransfer::State::Initial:
+ assert(false);
+ return;
case FileTransfer::State::Negotiating:
chatWindow->setFileTransferStatus(uiID, ChatWindow::Negotiating);
return;
@@ -138,7 +142,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..9ca7d86
--- /dev/null
+++ b/Swift/Controllers/HighlightRule.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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 <Swiften/Base/Regex.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
+{
+ std::string escaped = Regex::escape(s);
+ 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/HistoryViewController.cpp b/Swift/Controllers/HistoryViewController.cpp
index 9343017..cfa2482 100644
--- a/Swift/Controllers/HistoryViewController.cpp
+++ b/Swift/Controllers/HistoryViewController.cpp
@@ -4,6 +4,12 @@
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
+/*
+ * Copyright (c) 2013 Remko Tronçon
+ * Licensed under the GNU General Public License.
+ * See the COPYING file for more information.
+ */
+
#include <Swift/Controllers/HistoryViewController.h>
#include <Swift/Controllers/UIInterfaces/HistoryWindowFactory.h>
@@ -15,6 +21,7 @@
#include <Swiften/Avatars/AvatarManager.h>
#include <Swift/Controllers/Roster/SetPresence.h>
#include <Swift/Controllers/Roster/SetAvatar.h>
+#include <Swiften/Base/Path.h>
namespace Swift {
static const std::string category[] = { "Contacts", "MUC", "Contacts" };
@@ -146,7 +153,7 @@ void HistoryViewController::handleNewMessage(const HistoryMessage& message) {
// update contacts
if (!contacts_[message.getType()].count(displayJID)) {
- roster_->addContact(displayJID, displayJID, nickResolver_->jidToNick(displayJID), category[message.getType()], avatarManager_->getAvatarPath(displayJID).string());
+ roster_->addContact(displayJID, displayJID, nickResolver_->jidToNick(displayJID), category[message.getType()], avatarManager_->getAvatarPath(displayJID));
}
contacts_[message.getType()][displayJID].insert(message.getTime().date());
@@ -154,7 +161,7 @@ void HistoryViewController::handleNewMessage(const HistoryMessage& message) {
void HistoryViewController::addNewMessage(const HistoryMessage& message, bool addAtTheTop) {
bool senderIsSelf = message.getFromJID().toBare() == selfJID_;
- std::string avatarPath = avatarManager_->getAvatarPath(message.getFromJID()).string();
+ std::string avatarPath = pathToString(avatarManager_->getAvatarPath(message.getFromJID()));
std::string nick = message.getType() != HistoryMessage::Groupchat ? nickResolver_->jidToNick(message.getFromJID()) : message.getFromJID().getResource();
historyWindow_->addMessage(message.getMessage(), nick, senderIsSelf, avatarPath, message.getTime(), addAtTheTop);
@@ -177,7 +184,7 @@ void HistoryViewController::handleReturnPressed(const std::string& keyword) {
else {
nick = nickResolver_->jidToNick(jid);
}
- roster_->addContact(jid, jid, nick, category[type], avatarManager_->getAvatarPath(jid).string());
+ roster_->addContact(jid, jid, nick, category[type], avatarManager_->getAvatarPath(jid));
Presence::ref presence = getPresence(jid, type == HistoryMessage::Groupchat);
@@ -323,8 +330,7 @@ void HistoryViewController::handlePresenceChanged(Presence::ref presence) {
}
void HistoryViewController::handleAvatarChanged(const JID& jid) {
- std::string path = avatarManager_->getAvatarPath(jid).string();
- roster_->applyOnItems(SetAvatar(jid, path));
+ roster_->applyOnItems(SetAvatar(jid, avatarManager_->getAvatarPath(jid)));
}
Presence::ref HistoryViewController::getPresence(const JID& jid, bool isMUC) {
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 28d890d..1ae175c 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -1,83 +1,98 @@
/*
- * Copyright (c) 2010-2012 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * 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 <stdlib.h>
+
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
-#include <string>
-#include <stdlib.h>
+
#include <Swiften/Base/format.h>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Base/String.h>
#include <Swiften/StringCodecs/Base64.h>
+#include <Swiften/Network/TimerFactory.h>
+#include <Swiften/Client/Storages.h>
+#include <Swiften/VCards/VCardManager.h>
+#include <Swiften/Client/NickResolver.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Client/Client.h>
+#include <Swiften/Presence/PresenceSender.h>
+#include <Swiften/Elements/ChatState.h>
+#include <Swiften/Elements/Presence.h>
+#include <Swiften/Elements/VCardUpdate.h>
+#include <Swiften/Elements/DiscoInfo.h>
+#include <Swiften/Disco/CapsInfoGenerator.h>
+#include <Swiften/Disco/GetDiscoInfoRequest.h>
+#include <Swiften/Disco/ClientDiscoManager.h>
+#include <Swiften/VCards/GetVCardRequest.h>
+#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/Network/NetworkFactories.h>
+#include <Swiften/FileTransfer/FileTransferManager.h>
+#include <Swiften/Client/ClientXMLTracer.h>
+#include <Swiften/Client/StanzaChannel.h>
+#include <Swiften/Client/ClientBlockListManager.h>
+#include <Swiften/Crypto/CryptoProvider.h>
+
+#include <SwifTools/Dock/Dock.h>
+#include <SwifTools/Notifier/TogglableNotifier.h>
+#include <SwifTools/Idle/IdleDetector.h>
+
#include <Swift/Controllers/Intl.h>
#include <Swift/Controllers/UIInterfaces/UIFactory.h>
-#include "Swiften/Network/TimerFactory.h"
-#include "Swift/Controllers/BuildVersion.h"
-#include "Swiften/Client/Storages.h"
-#include "Swiften/VCards/VCardManager.h"
-#include "Swift/Controllers/Chat/UserSearchController.h"
-#include "Swift/Controllers/Chat/ChatsManager.h"
-#include "Swift/Controllers/XMPPEvents/EventController.h"
-#include "Swift/Controllers/EventWindowController.h"
-#include "Swift/Controllers/UIInterfaces/LoginWindow.h"
-#include "Swift/Controllers/UIInterfaces/LoginWindowFactory.h"
-#include "Swift/Controllers/UIInterfaces/MainWindow.h"
-#include "Swift/Controllers/Chat/MUCController.h"
-#include "Swiften/Client/NickResolver.h"
-#include "Swift/Controllers/Roster/RosterController.h"
-#include "Swift/Controllers/SoundEventController.h"
-#include "Swift/Controllers/SoundPlayer.h"
-#include "Swift/Controllers/StatusTracker.h"
-#include "Swift/Controllers/SystemTray.h"
-#include "Swift/Controllers/SystemTrayController.h"
-#include "Swift/Controllers/XMLConsoleController.h"
+#include <Swift/Controllers/BuildVersion.h>
+#include <Swift/Controllers/Chat/UserSearchController.h>
+#include <Swift/Controllers/Chat/ChatsManager.h>
+#include <Swift/Controllers/XMPPEvents/EventController.h>
+#include <Swift/Controllers/EventWindowController.h>
+#include <Swift/Controllers/UIInterfaces/LoginWindow.h>
+#include <Swift/Controllers/UIInterfaces/LoginWindowFactory.h>
+#include <Swift/Controllers/UIInterfaces/MainWindow.h>
+#include <Swift/Controllers/Chat/MUCController.h>
+#include <Swift/Controllers/Roster/RosterController.h>
+#include <Swift/Controllers/SoundEventController.h>
+#include <Swift/Controllers/SoundPlayer.h>
+#include <Swift/Controllers/StatusTracker.h>
+#include <Swift/Controllers/SystemTray.h>
+#include <Swift/Controllers/SystemTrayController.h>
+#include <Swift/Controllers/XMLConsoleController.h>
#include <Swift/Controllers/HistoryController.h>
#include <Swift/Controllers/HistoryViewController.h>
-#include "Swift/Controllers/FileTransferListController.h"
-#include "Swift/Controllers/UIEvents/UIEventStream.h"
-#include "Swift/Controllers/PresenceNotifier.h"
-#include "Swift/Controllers/EventNotifier.h"
-#include "Swift/Controllers/Storages/StoragesFactory.h"
-#include "Swift/Controllers/WhiteboardManager.h"
-#include "SwifTools/Dock/Dock.h"
-#include "SwifTools/Notifier/TogglableNotifier.h"
-#include "Swiften/Base/foreach.h"
-#include "Swiften/Client/Client.h"
-#include "Swiften/Presence/PresenceSender.h"
-#include "Swiften/Elements/ChatState.h"
-#include "Swiften/Elements/Presence.h"
-#include "Swiften/Elements/VCardUpdate.h"
-#include "Swift/Controllers/Settings/SettingsProvider.h"
-#include "Swiften/Elements/DiscoInfo.h"
-#include "Swiften/Disco/CapsInfoGenerator.h"
-#include "Swiften/Disco/GetDiscoInfoRequest.h"
-#include "Swiften/Disco/ClientDiscoManager.h"
-#include "Swiften/VCards/GetVCardRequest.h"
-#include "Swiften/StringCodecs/SHA1.h"
-#include "Swiften/StringCodecs/Hexify.h"
-#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
-#include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"
-#include "Swift/Controllers/Storages/CertificateStorageFactory.h"
-#include "Swift/Controllers/Storages/CertificateStorageTrustChecker.h"
-#include "Swiften/Network/NetworkFactories.h"
+#include <Swift/Controllers/FileTransferListController.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/PresenceNotifier.h>
+#include <Swift/Controllers/EventNotifier.h>
+#include <Swift/Controllers/Storages/StoragesFactory.h>
+#include <Swift/Controllers/WhiteboardManager.h>
+#include <Swift/Controllers/Settings/SettingsProvider.h>
+#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>
+#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
+#include <Swift/Controllers/Storages/CertificateStorageFactory.h>
+#include <Swift/Controllers/Storages/CertificateStorageTrustChecker.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"
-#include <SwifTools/Idle/IdleDetector.h>
+#include <Swift/Controllers/AdHocManager.h>
#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
-#include <Swiften/FileTransfer/FileTransferManager.h>
-#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>
+#include <Swift/Controllers/BlockListController.h>
+
namespace Swift {
@@ -98,6 +113,7 @@ MainController::MainController(
Notifier* notifier,
URIHandler* uriHandler,
IdleDetector* idleDetector,
+ const std::map<std::string, std::string>& emoticons,
bool useDelayForLatency) :
eventLoop_(eventLoop),
networkFactories_(networkFactories),
@@ -109,7 +125,8 @@ MainController::MainController(
idleDetector_(idleDetector),
loginWindow_(NULL) ,
useDelayForLatency_(useDelayForLatency),
- ftOverview_(NULL) {
+ ftOverview_(NULL),
+ emoticons_(emoticons) {
storages_ = NULL;
certificateStorage_ = NULL;
statusTracker_ = NULL;
@@ -121,6 +138,8 @@ MainController::MainController(
historyViewController_ = NULL;
eventWindowController_ = NULL;
profileController_ = NULL;
+ blockListController_ = NULL;
+ showProfileController_ = NULL;
contactEditController_ = NULL;
userSearchControllerChat_ = NULL;
userSearchControllerAdd_ = NULL;
@@ -142,7 +161,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 +225,8 @@ MainController::~MainController() {
eventController_->disconnectAll();
resetClient();
+ delete highlightEditorController_;
+ delete highlightManager_;
delete fileTransferListController_;
delete xmlConsoleController_;
delete xmppURIController_;
@@ -225,6 +250,8 @@ void MainController::resetClient() {
contactEditController_ = NULL;
delete profileController_;
profileController_ = NULL;
+ delete showProfileController_;
+ showProfileController_ = NULL;
delete eventWindowController_;
eventWindowController_ = NULL;
delete chatsManager_;
@@ -297,16 +324,16 @@ void MainController::handleConnected() {
myStatusLooksOnline_ = true;
if (freshLogin) {
profileController_ = new ProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_);
- srand(time(NULL));
- int randomPort = 10000 + rand() % 10000;
- client_->getFileTransferManager()->startListeningOnPort(randomPort);
+ showProfileController_ = new ShowProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_);
ftOverview_ = new FileTransferOverview(client_->getFileTransferManager());
fileTransferListController_->setFileTransferOverview(ftOverview_);
- rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_, client_->getEntityCapsProvider(), ftOverview_);
+ rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_, client_->getEntityCapsProvider(), ftOverview_, client_->getClientBlockListManager());
rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));
rosterController_->getWindow()->onShowCertificateRequest.connect(boost::bind(&MainController::handleShowCertificateRequest, this));
+ blockListController_ = new BlockListController(client_->getClientBlockListManager(), uiEventStream_, uiFactory_, eventController_);
+
contactEditController_ = new ContactEditController(rosterController_, client_->getVCardManager(), uiFactory_, uiEventStream_);
whiteboardManager_ = new WhiteboardManager(uiFactory_, uiEventStream_, client_->getNickResolver(), client_->getWhiteboardSessionManager());
@@ -318,9 +345,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_, client_->getClientBlockListManager(), emoticons_);
#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_, client_->getClientBlockListManager(), &emoticons_);
#endif
client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1));
@@ -369,7 +396,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);
}
@@ -440,7 +469,7 @@ void MainController::handleInputIdleChanged(bool idle) {
}
else {
if (idle) {
- if (statusTracker_->goAutoAway()) {
+ if (statusTracker_->goAutoAway(idleDetector_->getIdleTimeSeconds())) {
if (client_ && client_->isAvailable()) {
sendPresence(statusTracker_->getNextPresence());
}
@@ -710,6 +739,10 @@ void MainController::handleServerDiscoInfoResponse(boost::shared_ptr<DiscoInfo>
if (!error) {
chatsManager_->setServerDiscoInfo(info);
adHocManager_->setServerDiscoInfo(info);
+ if (info->hasFeature(DiscoInfo::BlockingCommandFeature)) {
+ rosterController_->getWindow()->setBlockingCommandAvailable(true);
+ rosterController_->initBlockingCommand();
+ }
}
}
@@ -717,7 +750,7 @@ void MainController::handleVCardReceived(const JID& jid, VCard::ref vCard) {
if (!jid.equals(jid_, JID::WithoutResource) || !vCard || vCard->getPhoto().empty()) {
return;
}
- std::string hash = Hexify::hexify(SHA1::getHash(vCard->getPhoto()));
+ std::string hash = Hexify::hexify(networkFactories_->getCryptoProvider()->getSHA1Hash(vCard->getPhoto()));
if (hash != vCardPhotoHash_) {
vCardPhotoHash_ = hash;
if (client_ && client_->isAvailable()) {
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 2e5bd05..ba132e7 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -1,29 +1,33 @@
/*
- * Copyright (c) 2010-2012 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
-#include <Swiften/Base/boost_bsignals.h>
-#include <boost/shared_ptr.hpp>
#include <vector>
-
-#include "Swiften/Network/Timer.h"
+#include <map>
#include <string>
-#include "Swiften/Client/ClientError.h"
-#include "Swiften/JID/JID.h"
-#include "Swiften/Elements/DiscoInfo.h"
-#include "Swiften/Elements/VCard.h"
-#include "Swiften/Elements/ErrorPayload.h"
-#include "Swiften/Elements/Presence.h"
-#include "Swift/Controllers/Settings/SettingsProvider.h"
-#include "Swift/Controllers/ProfileSettingsProvider.h"
-#include "Swiften/Elements/CapsInfo.h"
-#include "Swift/Controllers/XMPPEvents/ErrorEvent.h"
-#include "Swift/Controllers/UIEvents/UIEvent.h"
-#include "Swiften/Client/ClientXMLTracer.h"
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Network/Timer.h>
+#include <Swiften/Client/ClientError.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/DiscoInfo.h>
+#include <Swiften/Elements/VCard.h>
+#include <Swiften/Elements/ErrorPayload.h>
+#include <Swiften/Elements/Presence.h>
+#include <Swiften/Elements/CapsInfo.h>
+#include <Swiften/Client/ClientXMLTracer.h>
+
+#include <Swift/Controllers/Settings/SettingsProvider.h>
+#include <Swift/Controllers/ProfileSettingsProvider.h>
+#include <Swift/Controllers/XMPPEvents/ErrorEvent.h>
+#include <Swift/Controllers/UIEvents/UIEvent.h>
+
namespace Swift {
class IdleDetector;
@@ -43,6 +47,7 @@ namespace Swift {
class MUCController;
class Notifier;
class ProfileController;
+ class ShowProfileController;
class ContactEditController;
class TogglableNotifier;
class PresenceNotifier;
@@ -71,6 +76,9 @@ namespace Swift {
class AdHocCommandWindowFactory;
class FileTransferOverview;
class WhiteboardManager;
+ class HighlightManager;
+ class HighlightEditorController;
+ class BlockListController;
class MainController {
public:
@@ -87,6 +95,7 @@ namespace Swift {
Notifier* notifier,
URIHandler* uriHandler,
IdleDetector* idleDetector,
+ const std::map<std::string, std::string>& emoticons,
bool useDelayForLatency);
~MainController();
@@ -151,8 +160,10 @@ namespace Swift {
HistoryViewController* historyViewController_;
HistoryController* historyController_;
FileTransferListController* fileTransferListController_;
+ BlockListController* blockListController_;
ChatsManager* chatsManager_;
ProfileController* profileController_;
+ ShowProfileController* showProfileController_;
ContactEditController* contactEditController_;
JID jid_;
JID boundJID_;
@@ -176,5 +187,8 @@ namespace Swift {
static const int SecondsToWaitBeforeForceQuitting;
FileTransferOverview* ftOverview_;
WhiteboardManager* whiteboardManager_;
+ HighlightManager* highlightManager_;
+ HighlightEditorController* highlightEditorController_;
+ std::map<std::string, std::string> emoticons_;
};
}
diff --git a/Swift/Controllers/ProfileController.cpp b/Swift/Controllers/ProfileController.cpp
index 101e283..241cc2e 100644
--- a/Swift/Controllers/ProfileController.cpp
+++ b/Swift/Controllers/ProfileController.cpp
@@ -25,7 +25,7 @@ ProfileController::~ProfileController() {
if (profileWindow) {
vcardManager->onOwnVCardChanged.disconnect(boost::bind(&ProfileController::handleOwnVCardChanged, this, _1));
profileWindow->onVCardChangeRequest.disconnect(boost::bind(&ProfileController::handleVCardChangeRequest, this, _1));
- delete profileWindow;
+ profileWindow->onWindowAboutToBeClosed.disconnect(boost::bind(&ProfileController::handleProfileWindowAboutToBeClosed, this, _1));
}
uiEventStream->onUIEvent.disconnect(boost::bind(&ProfileController::handleUIEvent, this, _1));
}
@@ -37,7 +37,9 @@ void ProfileController::handleUIEvent(UIEvent::ref event) {
if (!profileWindow) {
profileWindow = profileWindowFactory->createProfileWindow();
+ profileWindow->setEditable(true);
profileWindow->onVCardChangeRequest.connect(boost::bind(&ProfileController::handleVCardChangeRequest, this, _1));
+ profileWindow->onWindowAboutToBeClosed.connect(boost::bind(&ProfileController::handleProfileWindowAboutToBeClosed, this, _1));
vcardManager->onOwnVCardChanged.connect(boost::bind(&ProfileController::handleOwnVCardChanged, this, _1));
}
gettingVCard = true;
@@ -75,6 +77,10 @@ void ProfileController::handleOwnVCardChanged(VCard::ref vcard) {
}
}
+void ProfileController::handleProfileWindowAboutToBeClosed(const JID&) {
+ profileWindow = NULL;
+}
+
void ProfileController::setAvailable(bool b) {
available = b;
if (!available) {
diff --git a/Swift/Controllers/ProfileController.h b/Swift/Controllers/ProfileController.h
index c1afcf9..538df36 100644
--- a/Swift/Controllers/ProfileController.h
+++ b/Swift/Controllers/ProfileController.h
@@ -29,6 +29,7 @@ namespace Swift {
void handleVCardChangeRequest(VCard::ref vcard);
void handleSetVCardResponse(ErrorPayload::ref);
void handleOwnVCardChanged(VCard::ref vcard);
+ void handleProfileWindowAboutToBeClosed(const JID&);
void updateDialogStatus();
private:
diff --git a/Swift/Controllers/Roster/ContactRosterItem.cpp b/Swift/Controllers/Roster/ContactRosterItem.cpp
index 8c388bf..70b4a1b 100644
--- a/Swift/Controllers/Roster/ContactRosterItem.cpp
+++ b/Swift/Controllers/Roster/ContactRosterItem.cpp
@@ -8,11 +8,15 @@
#include "Swift/Controllers/Roster/GroupRosterItem.h"
#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/DateTime.h>
+#include <Swiften/Elements/Idle.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
namespace Swift {
-ContactRosterItem::ContactRosterItem(const JID& jid, const JID& displayJID, const std::string& name, GroupRosterItem* parent) : RosterItem(name, parent), jid_(jid), displayJID_(displayJID) {
+ContactRosterItem::ContactRosterItem(const JID& jid, const JID& displayJID, const std::string& name, GroupRosterItem* parent) : RosterItem(name, parent), jid_(jid), displayJID_(displayJID), blockState_(BlockingNotSupported) {
}
ContactRosterItem::~ContactRosterItem() {
@@ -24,12 +28,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;
@@ -39,11 +43,20 @@ std::string ContactRosterItem::getStatusText() const {
return shownPresence_ ? shownPresence_->getStatus() : "";
}
-void ContactRosterItem::setAvatarPath(const std::string& path) {
+std::string ContactRosterItem::getIdleText() const {
+ Idle::ref idle = shownPresence_ ? shownPresence_->getPayload<Idle>() : Idle::ref();
+ if (!idle || idle->getSince().is_not_a_date_time()) {
+ return "";
+ } else {
+ return dateTimeToLocalString(idle->getSince());
+ }
+}
+
+void ContactRosterItem::setAvatarPath(const boost::filesystem::path& path) {
avatarPath_ = path;
onDataChanged();
}
-const std::string& ContactRosterItem::getAvatarPath() const {
+const boost::filesystem::path& ContactRosterItem::getAvatarPath() const {
return avatarPath_;
}
@@ -121,6 +134,14 @@ bool ContactRosterItem::supportsFeature(const Feature feature) const {
return features_.find(feature) != features_.end();
}
+void ContactRosterItem::setBlockState(BlockState state) {
+ blockState_ = state;
+}
+
+ContactRosterItem::BlockState ContactRosterItem::blockState() const {
+ return blockState_;
+}
+
}
diff --git a/Swift/Controllers/Roster/ContactRosterItem.h b/Swift/Controllers/Roster/ContactRosterItem.h
index 8389a44..67a9722 100644
--- a/Swift/Controllers/Roster/ContactRosterItem.h
+++ b/Swift/Controllers/Roster/ContactRosterItem.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -17,6 +17,8 @@
#include <boost/bind.hpp>
#include "Swiften/Base/boost_bsignals.h"
#include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/ptime.hpp>
+#include <boost/filesystem/path.hpp>
namespace Swift {
@@ -25,7 +27,13 @@ class ContactRosterItem : public RosterItem {
public:
enum Feature {
FileTransferFeature,
- WhiteboardFeature,
+ WhiteboardFeature
+ };
+
+ enum BlockState {
+ BlockingNotSupported,
+ IsBlocked,
+ IsUnblocked
};
public:
@@ -35,8 +43,9 @@ class ContactRosterItem : public RosterItem {
StatusShow::Type getStatusShow() const;
StatusShow::Type getSimplifiedStatusShow() const;
std::string getStatusText() const;
- void setAvatarPath(const std::string& path);
- const std::string& getAvatarPath() const;
+ std::string getIdleText() const;
+ void setAvatarPath(const boost::filesystem::path& path);
+ const boost::filesystem::path& getAvatarPath() const;
const JID& getJID() const;
void setDisplayJID(const JID& jid);
const JID& getDisplayJID() const;
@@ -50,15 +59,21 @@ class ContactRosterItem : public RosterItem {
void setSupportedFeatures(const std::set<Feature>& features);
bool supportsFeature(Feature feature) const;
+
+ void setBlockState(BlockState state);
+ BlockState blockState() const;
+
private:
JID jid_;
JID displayJID_;
- std::string avatarPath_;
+ boost::posix_time::ptime lastAvailableTime_;
+ boost::filesystem::path avatarPath_;
std::map<std::string, boost::shared_ptr<Presence> > presences_;
boost::shared_ptr<Presence> offlinePresence_;
boost::shared_ptr<Presence> shownPresence_;
std::vector<std::string> groups_;
std::set<Feature> features_;
+ BlockState blockState_;
};
}
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..9b45b63 100644
--- a/Swift/Controllers/Roster/Roster.cpp
+++ b/Swift/Controllers/Roster/Roster.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.
*/
@@ -22,7 +22,7 @@
namespace Swift {
-Roster::Roster(bool sortByStatus, bool fullJIDMapping) {
+Roster::Roster(bool sortByStatus, bool fullJIDMapping) : blockingSupported_(false) {
sortByStatus_ = sortByStatus;
fullJIDMapping_ = fullJIDMapping;
root_ = new GroupRosterItem("Dummy-Root", NULL, sortByStatus_);
@@ -71,6 +71,28 @@ void Roster::setAvailableFeatures(const JID& jid, const std::set<ContactRosterIt
}
}
+void Roster::setBlockedState(const std::vector<JID> &jids, ContactRosterItem::BlockState state) {
+ if (!blockingSupported_ ) {
+ foreach(ItemMap::value_type i, itemMap_) {
+ foreach(ContactRosterItem* item, i.second) {
+ item->setBlockState(ContactRosterItem::IsUnblocked);
+ }
+ }
+ }
+
+ foreach(const JID& jid, jids) {
+ ItemMap::const_iterator i = itemMap_.find(fullJIDMapping_ ? jid : jid.toBare());
+ if (i == itemMap_.end()) {
+ continue;
+ }
+ foreach(ContactRosterItem* item, i->second) {
+ item->setBlockState(state);
+ }
+ }
+
+ blockingSupported_ = true;
+}
+
void Roster::removeGroup(const std::string& group) {
root_->removeGroupChild(group);
}
@@ -83,10 +105,13 @@ void Roster::handleChildrenChanged(GroupRosterItem* item) {
onChildrenChanged(item);
}
-void Roster::addContact(const JID& jid, const JID& displayJID, const std::string& name, const std::string& groupName, const std::string& avatarPath) {
+void Roster::addContact(const JID& jid, const JID& displayJID, const std::string& name, const std::string& groupName, const boost::filesystem::path& avatarPath) {
GroupRosterItem* group(getGroup(groupName));
ContactRosterItem *item = new ContactRosterItem(jid, displayJID, name, group);
item->setAvatarPath(avatarPath);
+ if (blockingSupported_) {
+ item->setBlockState(ContactRosterItem::IsUnblocked);
+ }
group->addChild(item);
ItemMap::iterator i = itemMap_.insert(std::make_pair(fullJIDMapping_ ? jid : jid.toBare(), std::vector<ContactRosterItem*>())).first;
if (!i->second.empty()) {
@@ -198,13 +223,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..a4c8b99 100644
--- a/Swift/Controllers/Roster/Roster.h
+++ b/Swift/Controllers/Roster/Roster.h
@@ -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.
*/
@@ -28,7 +28,7 @@ class Roster {
Roster(bool sortByStatus = true, bool fullJIDMapping = false);
~Roster();
- void addContact(const JID& jid, const JID& displayJID, const std::string& name, const std::string& group, const std::string& avatarPath);
+ void addContact(const JID& jid, const JID& displayJID, const std::string& name, const std::string& group, const boost::filesystem::path& avatarPath);
void removeContact(const JID& jid);
void removeContactFromGroup(const JID& jid, const std::string& group);
void removeGroup(const std::string& group);
@@ -36,15 +36,16 @@ 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;
GroupRosterItem* getGroup(const std::string& groupName);
void setAvailableFeatures(const JID& jid, const std::set<ContactRosterItem::Feature>& features);
+ void setBlockedState(const std::vector<JID>& jids, ContactRosterItem::BlockState state);
private:
void handleDataChanged(RosterItem* item);
@@ -58,6 +59,7 @@ class Roster {
ItemMap itemMap_;
bool fullJIDMapping_;
bool sortByStatus_;
+ bool blockingSupported_;
};
}
diff --git a/Swift/Controllers/Roster/RosterController.cpp b/Swift/Controllers/Roster/RosterController.cpp
index ec52993..d277799 100644
--- a/Swift/Controllers/Roster/RosterController.cpp
+++ b/Swift/Controllers/Roster/RosterController.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2012 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -40,18 +40,20 @@
#include <Swiften/Client/NickManager.h>
#include <Swift/Controllers/Intl.h>
#include <Swiften/Base/format.h>
+#include <Swiften/Base/Path.h>
#include <Swiften/Elements/DiscoInfo.h>
#include <Swiften/Disco/EntityCapsManager.h>
#include <Swiften/Jingle/JingleSessionManager.h>
#include <Swift/Controllers/SettingConstants.h>
+#include <Swiften/Client/ClientBlockListManager.h>
namespace Swift {
/**
* The controller does not gain ownership of these parameters.
*/
-RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsManager, FileTransferOverview* fileTransferOverview)
- : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), nickManager_(nickManager), nickResolver_(nickResolver), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), ftOverview_(fileTransferOverview) {
+RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsManager, FileTransferOverview* fileTransferOverview, ClientBlockListManager* clientBlockListManager)
+ : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), nickManager_(nickManager), nickResolver_(nickResolver), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), ftOverview_(fileTransferOverview), clientBlockListManager_(clientBlockListManager) {
assert(fileTransferOverview);
iqRouter_ = iqRouter;
presenceOracle_ = presenceOracle;
@@ -72,7 +74,7 @@ RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, Avata
uiEventConnection_ = uiEventStream->onUIEvent.connect(boost::bind(&RosterController::handleUIEvent, this, _1));
avatarManager_ = avatarManager;
avatarManager_->onAvatarChanged.connect(boost::bind(&RosterController::handleAvatarChanged, this, _1));
- mainWindow_->setMyAvatarPath(avatarManager_->getAvatarPath(myJID_).string());
+ mainWindow_->setMyAvatarPath(pathToString(avatarManager_->getAvatarPath(myJID_)));
nickManager_->onOwnNickChanged.connect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1));
mainWindow_->setMyJID(jid);
@@ -124,11 +126,11 @@ void RosterController::handleOnJIDAdded(const JID& jid) {
std::string name = nickResolver_->jidToNick(jid);
if (!groups.empty()) {
foreach(const std::string& group, groups) {
- roster_->addContact(jid, jid, name, group, avatarManager_->getAvatarPath(jid).string());
+ roster_->addContact(jid, jid, name, group, avatarManager_->getAvatarPath(jid));
}
}
else {
- roster_->addContact(jid, jid, name, QT_TRANSLATE_NOOP("", "Contacts"), avatarManager_->getAvatarPath(jid).string());
+ roster_->addContact(jid, jid, name, QT_TRANSLATE_NOOP("", "Contacts"), avatarManager_->getAvatarPath(jid));
}
applyAllPresenceTo(jid);
}
@@ -163,7 +165,7 @@ void RosterController::handleOnJIDUpdated(const JID& jid, const std::string& old
}
foreach(const std::string& group, groups) {
if (std::find(oldGroups.begin(), oldGroups.end(), group) == oldGroups.end()) {
- roster_->addContact(jid, jid, name, group, avatarManager_->getAvatarPath(jid).string());
+ roster_->addContact(jid, jid, name, group, avatarManager_->getAvatarPath(jid));
}
}
foreach(const std::string& group, oldGroups) {
@@ -183,6 +185,20 @@ void RosterController::handleSettingChanged(const std::string& settingPath) {
}
}
+void RosterController::handleBlockingStateChanged() {
+ if (clientBlockListManager_->getBlockList()->getState() == BlockList::Available) {
+ roster_->setBlockedState(clientBlockListManager_->getBlockList()->getItems(), ContactRosterItem::IsBlocked);
+ }
+}
+
+void RosterController::handleBlockingItemAdded(const JID& jid) {
+ roster_->setBlockedState(std::vector<JID>(1, jid), ContactRosterItem::IsBlocked);
+}
+
+void RosterController::handleBlockingItemRemoved(const JID& jid) {
+ roster_->setBlockedState(std::vector<JID>(1, jid), ContactRosterItem::IsUnblocked);
+}
+
void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
if (boost::shared_ptr<AddContactUIEvent> addContactEvent = boost::dynamic_pointer_cast<AddContactUIEvent>(event)) {
RosterItemPayload item;
@@ -256,6 +272,18 @@ void RosterController::updateItem(const XMPPRosterItem& item) {
request->send();
}
+void RosterController::initBlockingCommand() {
+ boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList();
+
+ blockingOnStateChangedConnection_ = blockList->onStateChanged.connect(boost::bind(&RosterController::handleBlockingStateChanged, this));
+ blockingOnItemAddedConnection_ = blockList->onItemAdded.connect(boost::bind(&RosterController::handleBlockingItemAdded, this, _1));
+ blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&RosterController::handleBlockingItemRemoved, this, _1));
+
+ if (blockList->getState() == BlockList::Available) {
+ roster_->setBlockedState(blockList->getItems(), ContactRosterItem::IsBlocked);
+ }
+}
+
void RosterController::handleRosterSetError(ErrorPayload::ref error, boost::shared_ptr<RosterPayload> rosterPayload) {
if (!error) {
return;
@@ -299,10 +327,10 @@ void RosterController::handleSubscriptionRequestDeclined(SubscriptionRequestEven
}
void RosterController::handleAvatarChanged(const JID& jid) {
- std::string path = avatarManager_->getAvatarPath(jid).string();
+ boost::filesystem::path path = avatarManager_->getAvatarPath(jid);
roster_->applyOnItems(SetAvatar(jid, path));
if (jid.equals(myJID_, JID::WithoutResource)) {
- mainWindow_->setMyAvatarPath(path);
+ mainWindow_->setMyAvatarPath(pathToString(path));
}
}
diff --git a/Swift/Controllers/Roster/RosterController.h b/Swift/Controllers/Roster/RosterController.h
index 5e40124..06b551e 100644
--- a/Swift/Controllers/Roster/RosterController.h
+++ b/Swift/Controllers/Roster/RosterController.h
@@ -39,13 +39,14 @@ namespace Swift {
class NickManager;
class EntityCapsProvider;
class FileTransferManager;
-
+ class ClientBlockListManager;
+
class RosterController {
public:
- RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter_, SettingsProvider* settings, EntityCapsProvider* entityCapsProvider, FileTransferOverview* fileTransferOverview);
+ RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter_, SettingsProvider* settings, EntityCapsProvider* entityCapsProvider, FileTransferOverview* fileTransferOverview, ClientBlockListManager* clientBlockListManager);
~RosterController();
void showRosterWindow();
- MainWindow* getWindow() {return mainWindow_;};
+ MainWindow* getWindow() {return mainWindow_;}
boost::signal<void (StatusShow::Type, const std::string&)> onChangeStatusRequest;
boost::signal<void ()> onSignOutRequest;
void handleAvatarChanged(const JID& jid);
@@ -57,6 +58,8 @@ namespace Swift {
void setContactGroups(const JID& jid, const std::vector<std::string>& groups);
void updateItem(const XMPPRosterItem&);
+ void initBlockingCommand();
+
private:
void handleOnJIDAdded(const JID &jid);
void handleRosterCleared();
@@ -76,6 +79,10 @@ namespace Swift {
void handleOnCapsChanged(const JID& jid);
void handleSettingChanged(const std::string& settingPath);
+ void handleBlockingStateChanged();
+ void handleBlockingItemAdded(const JID& jid);
+ void handleBlockingItemRemoved(const JID& jid);
+
JID myJID_;
XMPPRoster* xmppRoster_;
MainWindowFactory* mainWindowFactory_;
@@ -94,7 +101,11 @@ namespace Swift {
UIEventStream* uiEventStream_;
EntityCapsProvider* entityCapsManager_;
FileTransferOverview* ftOverview_;
+ ClientBlockListManager* clientBlockListManager_;
+ boost::bsignals::scoped_connection blockingOnStateChangedConnection_;
+ boost::bsignals::scoped_connection blockingOnItemAddedConnection_;
+ boost::bsignals::scoped_connection blockingOnItemRemovedConnection_;
boost::bsignals::scoped_connection changeStatusConnection_;
boost::bsignals::scoped_connection signOutConnection_;
boost::bsignals::scoped_connection uiEventConnection_;
diff --git a/Swift/Controllers/Roster/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/SetAvatar.h b/Swift/Controllers/Roster/SetAvatar.h
index 241b741..424f0b3 100644
--- a/Swift/Controllers/Roster/SetAvatar.h
+++ b/Swift/Controllers/Roster/SetAvatar.h
@@ -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.
*/
@@ -10,6 +10,7 @@
#include "Swiften/JID/JID.h"
#include "Swift/Controllers/Roster/RosterItemOperation.h"
#include "Swift/Controllers/Roster/ContactRosterItem.h"
+#include <boost/filesystem/path.hpp>
namespace Swift {
@@ -17,7 +18,7 @@ class RosterItem;
class SetAvatar : public RosterItemOperation {
public:
- SetAvatar(const JID& jid, const std::string& path, JID::CompareType compareType = JID::WithoutResource) : RosterItemOperation(true, jid), jid_(jid), path_(path), compareType_(compareType) {
+ SetAvatar(const JID& jid, const boost::filesystem::path& path, JID::CompareType compareType = JID::WithoutResource) : RosterItemOperation(true, jid), jid_(jid), path_(path), compareType_(compareType) {
}
virtual void operator() (RosterItem* item) const {
@@ -29,7 +30,7 @@ class SetAvatar : public RosterItemOperation {
private:
JID jid_;
- std::string path_;
+ boost::filesystem::path path_;
JID::CompareType compareType_;
};
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/TableRoster.h b/Swift/Controllers/Roster/TableRoster.h
index d4612ed..f447760 100644
--- a/Swift/Controllers/Roster/TableRoster.h
+++ b/Swift/Controllers/Roster/TableRoster.h
@@ -12,6 +12,7 @@
#include <Swiften/JID/JID.h>
#include <Swiften/Elements/StatusShow.h>
+#include <boost/filesystem/path.hpp>
namespace Swift {
class Roster;
@@ -21,13 +22,13 @@ namespace Swift {
class TableRoster {
public:
struct Item {
- Item(const std::string& name, const std::string& description, const JID& jid, StatusShow::Type status, const std::string& avatarPath) : name(name), description(description), jid(jid), status(status), avatarPath(avatarPath) {
+ Item(const std::string& name, const std::string& description, const JID& jid, StatusShow::Type status, const boost::filesystem::path& avatarPath) : name(name), description(description), jid(jid), status(status), avatarPath(avatarPath) {
}
std::string name;
std::string description;
JID jid;
StatusShow::Type status;
- std::string avatarPath;
+ boost::filesystem::path avatarPath;
};
struct Index {
diff --git a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp
index fbee894..b0034e6 100644
--- a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp
+++ b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp
@@ -37,6 +37,7 @@
#include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/EventLoop/DummyEventLoop.h>
+#include <Swiften/Client/ClientBlockListManager.h>
using namespace Swift;
@@ -82,12 +83,14 @@ class RosterControllerTest : public CppUnit::TestFixture {
ftManager_ = new DummyFileTransferManager();
ftOverview_ = new FileTransferOverview(ftManager_);
- rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_, entityCapsManager_, ftOverview_);
+ clientBlockListManager_ = new ClientBlockListManager(router_);
+ rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_, entityCapsManager_, ftOverview_, clientBlockListManager_);
mainWindow_ = mainWindowFactory_->last;
- };
+ }
void tearDown() {
delete rosterController_;
+ delete clientBlockListManager_;
delete ftManager_;
delete jingleSessionManager_;
@@ -105,7 +108,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 +136,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 +156,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 +176,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 +218,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 +228,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 +245,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 +259,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 +285,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");
@@ -337,6 +340,7 @@ class RosterControllerTest : public CppUnit::TestFixture {
JingleSessionManager* jingleSessionManager_;
FileTransferManager* ftManager_;
FileTransferOverview* ftOverview_;
+ ClientBlockListManager* clientBlockListManager_;
};
CPPUNIT_TEST_SUITE_REGISTRATION(RosterControllerTest);
diff --git a/Swift/Controllers/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..9461a8c 100644
--- a/Swift/Controllers/SConscript
+++ b/Swift/Controllers/SConscript
@@ -28,8 +28,10 @@ if env["SCONS_STAGE"] == "build" :
"Chat/MUCController.cpp",
"Chat/MUCSearchController.cpp",
"Chat/UserSearchController.cpp",
+ "Chat/ChatMessageParser.cpp",
"MainController.cpp",
"ProfileController.cpp",
+ "ShowProfileController.cpp",
"ContactEditController.cpp",
"FileTransfer/FileTransferController.cpp",
"FileTransfer/FileTransferOverview.cpp",
@@ -48,6 +50,7 @@ if env["SCONS_STAGE"] == "build" :
"HistoryViewController.cpp",
"HistoryController.cpp",
"FileTransferListController.cpp",
+ "BlockListController.cpp",
"StatusTracker.cpp",
"PresenceNotifier.cpp",
"EventNotifier.cpp",
@@ -74,7 +77,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 = [
@@ -86,7 +95,9 @@ if env["SCONS_STAGE"] == "build" :
File("UnitTest/PresenceNotifierTest.cpp"),
File("Chat/UnitTest/ChatsManagerTest.cpp"),
File("Chat/UnitTest/MUCControllerTest.cpp"),
+ File("Chat/UnitTest/ChatMessageParserTest.cpp"),
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..0717fd5 100644
--- a/Swift/Controllers/SettingConstants.cpp
+++ b/Swift/Controllers/SettingConstants.cpp
@@ -19,4 +19,9 @@ 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", "@");
+const SettingsProvider::Setting<bool> SettingConstants::SPELL_CHECKER("spellChecker", false);
+const SettingsProvider::Setting<std::string> SettingConstants::DICT_PATH("dictPath", "/usr/share/myspell/dicts/");
+const SettingsProvider::Setting<std::string> SettingConstants::PERSONAL_DICT_PATH("personaldictPath", "/home/");
+const SettingsProvider::Setting<std::string> SettingConstants::DICT_FILE("dictFile", "en_US.dic");
}
diff --git a/Swift/Controllers/SettingConstants.h b/Swift/Controllers/SettingConstants.h
index ff1ed72..a3a1650 100644
--- a/Swift/Controllers/SettingConstants.h
+++ b/Swift/Controllers/SettingConstants.h
@@ -22,5 +22,10 @@ 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;
+ static const SettingsProvider::Setting<bool> SPELL_CHECKER;
+ static const SettingsProvider::Setting<std::string> DICT_PATH;
+ static const SettingsProvider::Setting<std::string> PERSONAL_DICT_PATH;
+ static const SettingsProvider::Setting<std::string> DICT_FILE;
};
}
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..15b7b26
--- /dev/null
+++ b/Swift/Controllers/ShowProfileController.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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->onWindowAboutToBeClosed.disconnect(boost::bind(&ShowProfileController::handleProfileWindowAboutToBeClosed, 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->onWindowAboutToBeClosed.connect(boost::bind(&ShowProfileController::handleProfileWindowAboutToBeClosed, 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::handleProfileWindowAboutToBeClosed(const JID& profileJid) {
+ openedProfileWindows.erase(profileJid);
+}
+
+}
diff --git a/Swift/Controllers/ShowProfileController.h b/Swift/Controllers/ShowProfileController.h
new file mode 100644
index 0000000..27a0cf4
--- /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 handleProfileWindowAboutToBeClosed(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..3444189
--- /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_);
+ 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/StatusTracker.cpp b/Swift/Controllers/StatusTracker.cpp
index 0c88f4d..6766c2e 100644
--- a/Swift/Controllers/StatusTracker.cpp
+++ b/Swift/Controllers/StatusTracker.cpp
@@ -8,6 +8,8 @@
#include <boost/smart_ptr/make_shared.hpp>
+#include <Swiften/Elements/Idle.h>
+
namespace Swift {
StatusTracker::StatusTracker() {
@@ -21,6 +23,7 @@ boost::shared_ptr<Presence> StatusTracker::getNextPresence() {
presence = boost::make_shared<Presence>();
presence->setShow(StatusShow::Away);
presence->setStatus(queuedPresence_->getStatus());
+ presence->addPayload(boost::make_shared<Idle>(isAutoAwaySince_));
} else {
presence = queuedPresence_;
}
@@ -35,11 +38,12 @@ void StatusTracker::setRequestedPresence(boost::shared_ptr<Presence> presence) {
// }
}
-bool StatusTracker::goAutoAway() {
+bool StatusTracker::goAutoAway(const int& seconds) {
if (queuedPresence_->getShow() != StatusShow::Online) {
return false;
}
isAutoAway_ = true;
+ isAutoAwaySince_ = boost::posix_time::second_clock::universal_time() - boost::posix_time::seconds(seconds);
return true;
}
diff --git a/Swift/Controllers/StatusTracker.h b/Swift/Controllers/StatusTracker.h
index 4f4e880..10a5c0c 100644
--- a/Swift/Controllers/StatusTracker.h
+++ b/Swift/Controllers/StatusTracker.h
@@ -10,6 +10,8 @@
#include "Swiften/Elements/Presence.h"
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
namespace Swift {
class StatusTracker {
@@ -17,10 +19,11 @@ namespace Swift {
StatusTracker();
boost::shared_ptr<Presence> getNextPresence();
void setRequestedPresence(boost::shared_ptr<Presence> presence);
- bool goAutoAway();
+ bool goAutoAway(const int& seconds);
bool goAutoUnAway();
private:
boost::shared_ptr<Presence> queuedPresence_;
bool isAutoAway_;
+ boost::posix_time::ptime isAutoAwaySince_;
};
}
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/AvatarFileStorage.cpp b/Swift/Controllers/Storages/AvatarFileStorage.cpp
index b39e586..671e0cb 100644
--- a/Swift/Controllers/Storages/AvatarFileStorage.cpp
+++ b/Swift/Controllers/Storages/AvatarFileStorage.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.
*/
@@ -12,12 +12,12 @@
#include <Swiften/Base/foreach.h>
#include <Swiften/Base/String.h>
-#include <Swiften/StringCodecs/SHA1.h>
#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/Crypto/CryptoProvider.h>
namespace Swift {
-AvatarFileStorage::AvatarFileStorage(const boost::filesystem::path& avatarsDir, const boost::filesystem::path& avatarsFile) : avatarsDir(avatarsDir), avatarsFile(avatarsFile) {
+AvatarFileStorage::AvatarFileStorage(const boost::filesystem::path& avatarsDir, const boost::filesystem::path& avatarsFile, CryptoProvider* crypto) : avatarsDir(avatarsDir), avatarsFile(avatarsFile), crypto(crypto) {
if (boost::filesystem::exists(avatarsFile)) {
try {
boost::filesystem::ifstream file(avatarsFile);
@@ -47,7 +47,7 @@ bool AvatarFileStorage::hasAvatar(const std::string& hash) const {
}
void AvatarFileStorage::addAvatar(const std::string& hash, const ByteArray& avatar) {
- assert(Hexify::hexify(SHA1::getHash(avatar)) == hash);
+ assert(Hexify::hexify(crypto->getSHA1Hash(avatar)) == hash);
boost::filesystem::path avatarPath = getAvatarPath(hash);
if (!boost::filesystem::exists(avatarPath.parent_path())) {
@@ -69,7 +69,7 @@ boost::filesystem::path AvatarFileStorage::getAvatarPath(const std::string& hash
ByteArray AvatarFileStorage::getAvatar(const std::string& hash) const {
ByteArray data;
- readByteArrayFromFile(data, getAvatarPath(hash).string());
+ readByteArrayFromFile(data, getAvatarPath(hash));
return data;
}
diff --git a/Swift/Controllers/Storages/AvatarFileStorage.h b/Swift/Controllers/Storages/AvatarFileStorage.h
index b7e73f5..85a6463 100644
--- a/Swift/Controllers/Storages/AvatarFileStorage.h
+++ b/Swift/Controllers/Storages/AvatarFileStorage.h
@@ -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.
*/
@@ -15,9 +15,11 @@
#include "Swiften/Avatars/AvatarStorage.h"
namespace Swift {
+ class CryptoProvider;
+
class AvatarFileStorage : public AvatarStorage {
public:
- AvatarFileStorage(const boost::filesystem::path& avatarsDir, const boost::filesystem::path& avatarsFile);
+ AvatarFileStorage(const boost::filesystem::path& avatarsDir, const boost::filesystem::path& avatarsFile, CryptoProvider* crypto);
virtual bool hasAvatar(const std::string& hash) const;
virtual void addAvatar(const std::string& hash, const ByteArray& avatar);
@@ -34,6 +36,7 @@ namespace Swift {
private:
boost::filesystem::path avatarsDir;
boost::filesystem::path avatarsFile;
+ CryptoProvider* crypto;
typedef std::map<JID, std::string> JIDAvatarMap;
JIDAvatarMap jidAvatars;
};
diff --git a/Swift/Controllers/Storages/CertificateFileStorage.cpp b/Swift/Controllers/Storages/CertificateFileStorage.cpp
index a4a95c7..34d1f76 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,22 +8,23 @@
#include <iostream>
#include <boost/filesystem/fstream.hpp>
+#include <boost/numeric/conversion/cast.hpp>
-#include <Swiften/StringCodecs/SHA1.h>
#include <Swiften/StringCodecs/Hexify.h>
#include <Swiften/TLS/CertificateFactory.h>
#include <Swiften/Base/Log.h>
+#include <Swiften/Crypto/CryptoProvider.h>
namespace Swift {
-CertificateFileStorage::CertificateFileStorage(const boost::filesystem::path& path, CertificateFactory* certificateFactory) : path(path), certificateFactory(certificateFactory) {
+CertificateFileStorage::CertificateFileStorage(const boost::filesystem::path& path, CertificateFactory* certificateFactory, CryptoProvider* crypto) : path(path), certificateFactory(certificateFactory), crypto(crypto) {
}
bool CertificateFileStorage::hasCertificate(Certificate::ref certificate) const {
boost::filesystem::path certificatePath = getCertificatePath(certificate);
if (boost::filesystem::exists(certificatePath)) {
ByteArray data;
- readByteArrayFromFile(data, certificatePath.string());
+ readByteArrayFromFile(data, certificatePath);
Certificate::ref storedCertificate = certificateFactory->createCertificateFromDER(data);
if (storedCertificate && storedCertificate->toDER() == certificate->toDER()) {
return true;
@@ -50,12 +51,12 @@ 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();
}
boost::filesystem::path CertificateFileStorage::getCertificatePath(Certificate::ref certificate) const {
- return path / Hexify::hexify(SHA1::getHash(certificate->toDER()));
+ return path / Hexify::hexify(crypto->getSHA1Hash(certificate->toDER()));
}
}
diff --git a/Swift/Controllers/Storages/CertificateFileStorage.h b/Swift/Controllers/Storages/CertificateFileStorage.h
index f7a60b9..12151d0 100644
--- a/Swift/Controllers/Storages/CertificateFileStorage.h
+++ b/Swift/Controllers/Storages/CertificateFileStorage.h
@@ -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.
*/
@@ -12,10 +12,11 @@
namespace Swift {
class CertificateFactory;
+ class CryptoProvider;
class CertificateFileStorage : public CertificateStorage {
public:
- CertificateFileStorage(const boost::filesystem::path& path, CertificateFactory* certificateFactory);
+ CertificateFileStorage(const boost::filesystem::path& path, CertificateFactory* certificateFactory, CryptoProvider* crypto);
virtual bool hasCertificate(Certificate::ref certificate) const;
virtual void addCertificate(Certificate::ref certificate);
@@ -26,6 +27,7 @@ namespace Swift {
private:
boost::filesystem::path path;
CertificateFactory* certificateFactory;
+ CryptoProvider* crypto;
};
}
diff --git a/Swift/Controllers/Storages/CertificateFileStorageFactory.h b/Swift/Controllers/Storages/CertificateFileStorageFactory.h
index b215165..6834619 100644
--- a/Swift/Controllers/Storages/CertificateFileStorageFactory.h
+++ b/Swift/Controllers/Storages/CertificateFileStorageFactory.h
@@ -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.
*/
@@ -11,18 +11,20 @@
namespace Swift {
class CertificateFactory;
+ class CryptoProvider;
class CertificateFileStorageFactory : public CertificateStorageFactory {
public:
- CertificateFileStorageFactory(const boost::filesystem::path& basePath, CertificateFactory* certificateFactory) : basePath(basePath), certificateFactory(certificateFactory) {}
+ CertificateFileStorageFactory(const boost::filesystem::path& basePath, CertificateFactory* certificateFactory, CryptoProvider* crypto) : basePath(basePath), certificateFactory(certificateFactory), crypto(crypto) {}
virtual CertificateStorage* createCertificateStorage(const JID& profile) const {
boost::filesystem::path profilePath = basePath / profile.toString();
- return new CertificateFileStorage(profilePath / "certificates", certificateFactory);
+ return new CertificateFileStorage(profilePath / "certificates", certificateFactory, crypto);
}
private:
boost::filesystem::path basePath;
CertificateFactory* certificateFactory;
+ CryptoProvider* crypto;
};
}
diff --git a/Swift/Controllers/Storages/FileStorages.cpp b/Swift/Controllers/Storages/FileStorages.cpp
index cff87d3..52a5e00 100644
--- a/Swift/Controllers/Storages/FileStorages.cpp
+++ b/Swift/Controllers/Storages/FileStorages.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.
*/
@@ -10,17 +10,18 @@
#include "Swift/Controllers/Storages/CapsFileStorage.h"
#include "Swift/Controllers/Storages/RosterFileStorage.h"
#include <Swiften/History/SQLiteHistoryStorage.h>
+#include <Swiften/Base/Path.h>
namespace Swift {
-FileStorages::FileStorages(const boost::filesystem::path& baseDir, const JID& jid) {
- std::string profile = jid.toBare();
- vcardStorage = new VCardFileStorage(baseDir / profile / "vcards");
+FileStorages::FileStorages(const boost::filesystem::path& baseDir, const JID& jid, CryptoProvider* crypto) {
+ boost::filesystem::path profile = stringToPath(jid.toBare());
+ vcardStorage = new VCardFileStorage(baseDir / profile / "vcards", crypto);
capsStorage = new CapsFileStorage(baseDir / "caps");
- avatarStorage = new AvatarFileStorage(baseDir / "avatars", baseDir / profile / "avatars");
+ avatarStorage = new AvatarFileStorage(baseDir / "avatars", baseDir / profile / "avatars", crypto);
rosterStorage = new RosterFileStorage(baseDir / profile / "roster.xml");
#ifdef SWIFT_EXPERIMENTAL_HISTORY
- historyStorage = new SQLiteHistoryStorage((baseDir / "history.db").string());
+ historyStorage = new SQLiteHistoryStorage(baseDir / "history.db");
#endif
}
diff --git a/Swift/Controllers/Storages/FileStorages.h b/Swift/Controllers/Storages/FileStorages.h
index 5e89db8..1e914b9 100644
--- a/Swift/Controllers/Storages/FileStorages.h
+++ b/Swift/Controllers/Storages/FileStorages.h
@@ -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,7 +8,7 @@
#include <boost/filesystem/path.hpp>
-#include "Swiften/Client/Storages.h"
+#include <Swiften/Client/Storages.h>
namespace Swift {
class VCardFileStorage;
@@ -17,6 +17,7 @@ namespace Swift {
class RosterFileStorage;
class HistoryStorage;
class JID;
+ class CryptoProvider;
/**
* A storages implementation that stores all controller data on disk.
@@ -37,7 +38,7 @@ namespace Swift {
* \param jid the subdir in which profile-specific data will be stored.
* The bare JID will be used as the subdir name.
*/
- FileStorages(const boost::filesystem::path& baseDir, const JID& jid);
+ FileStorages(const boost::filesystem::path& baseDir, const JID& jid, CryptoProvider*);
~FileStorages();
virtual VCardStorage* getVCardStorage() const;
diff --git a/Swift/Controllers/Storages/FileStoragesFactory.h b/Swift/Controllers/Storages/FileStoragesFactory.h
index 0676bc3..c119dcf 100644
--- a/Swift/Controllers/Storages/FileStoragesFactory.h
+++ b/Swift/Controllers/Storages/FileStoragesFactory.h
@@ -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.
*/
@@ -10,15 +10,18 @@
#include "Swift/Controllers/Storages/FileStorages.h"
namespace Swift {
+ class CryptoProvider;
+
class FileStoragesFactory : public StoragesFactory {
public:
- FileStoragesFactory(const boost::filesystem::path& basePath) : basePath(basePath) {}
+ FileStoragesFactory(const boost::filesystem::path& basePath, CryptoProvider* crypto) : basePath(basePath), crypto(crypto) {}
virtual Storages* createStorages(const JID& profile) const {
- return new FileStorages(basePath, profile);
+ return new FileStorages(basePath, profile, crypto);
}
private:
boost::filesystem::path basePath;
+ CryptoProvider* crypto;
};
}
diff --git a/Swift/Controllers/Storages/VCardFileStorage.cpp b/Swift/Controllers/Storages/VCardFileStorage.cpp
index d799a90..b22e235 100644
--- a/Swift/Controllers/Storages/VCardFileStorage.cpp
+++ b/Swift/Controllers/Storages/VCardFileStorage.cpp
@@ -13,8 +13,9 @@
#include <Swiften/Entity/GenericPayloadPersister.h>
#include <Swiften/Base/String.h>
#include <Swiften/StringCodecs/Hexify.h>
-#include <Swiften/StringCodecs/SHA1.h>
#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Path.h>
+#include <Swiften/Crypto/CryptoProvider.h>
#include "Swiften/JID/JID.h"
#include "Swiften/Elements/VCard.h"
#include "Swiften/Serializer/PayloadSerializers/VCardSerializer.h"
@@ -25,7 +26,7 @@ using namespace Swift;
typedef GenericPayloadPersister<VCard, VCardParser, VCardSerializer> VCardPersister;
-VCardFileStorage::VCardFileStorage(boost::filesystem::path dir) : vcardsPath(dir) {
+VCardFileStorage::VCardFileStorage(boost::filesystem::path dir, CryptoProvider* crypto) : VCardStorage(crypto), vcardsPath(dir), crypto(crypto) {
cacheFile = vcardsPath / "phashes";
if (boost::filesystem::exists(cacheFile)) {
try {
@@ -66,7 +67,7 @@ boost::filesystem::path VCardFileStorage::getVCardPath(const JID& jid) const {
try {
std::string file(jid.toString());
String::replaceAll(file, '/', "%2f");
- return boost::filesystem::path(vcardsPath / (file + ".xml"));
+ return boost::filesystem::path(vcardsPath / stringToPath(file + ".xml"));
}
catch (const boost::filesystem::filesystem_error& e) {
std::cerr << "ERROR: " << e.what() << std::endl;
@@ -88,7 +89,7 @@ std::string VCardFileStorage::getPhotoHash(const JID& jid) const {
std::string VCardFileStorage::getAndUpdatePhotoHash(const JID& jid, VCard::ref vCard) const {
std::string hash;
if (vCard && !vCard->getPhoto().empty()) {
- hash = Hexify::hexify(SHA1::getHash(vCard->getPhoto()));
+ hash = Hexify::hexify(crypto->getSHA1Hash(vCard->getPhoto()));
}
std::pair<PhotoHashMap::iterator, bool> r = photoHashes.insert(std::make_pair(jid, hash));
if (r.second) {
diff --git a/Swift/Controllers/Storages/VCardFileStorage.h b/Swift/Controllers/Storages/VCardFileStorage.h
index ba422f4..2c3af3d 100644
--- a/Swift/Controllers/Storages/VCardFileStorage.h
+++ b/Swift/Controllers/Storages/VCardFileStorage.h
@@ -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.
*/
@@ -14,9 +14,11 @@
#include "Swiften/VCards/VCardStorage.h"
namespace Swift {
+ class CryptoProvider;
+
class VCardFileStorage : public VCardStorage {
public:
- VCardFileStorage(boost::filesystem::path dir);
+ VCardFileStorage(boost::filesystem::path dir, CryptoProvider* crypto);
virtual VCard::ref getVCard(const JID& jid) const;
virtual void setVCard(const JID& jid, VCard::ref v);
@@ -31,6 +33,7 @@ namespace Swift {
private:
boost::filesystem::path vcardsPath;
+ CryptoProvider* crypto;
boost::filesystem::path cacheFile;
typedef std::map<JID, std::string> PhotoHashMap;
mutable PhotoHashMap photoHashes;
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/RequestBlockListDialogUIEvent.h b/Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h
new file mode 100644
index 0000000..d29cb4f
--- /dev/null
+++ b/Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swift/Controllers/UIEvents/UIEvent.h>
+
+namespace Swift {
+
+class RequestBlockListDialogUIEvent : public UIEvent {
+};
+
+}
diff --git a/Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h b/Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h
new file mode 100644
index 0000000..9b7abcb
--- /dev/null
+++ b/Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swift/Controllers/UIEvents/UIEvent.h>
+
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+
+class RequestChangeBlockStateUIEvent : public UIEvent {
+ public:
+ enum BlockState {
+ Blocked,
+ Unblocked
+ };
+
+ public:
+ RequestChangeBlockStateUIEvent(BlockState newState, const JID& contact) : state_(newState), contact_(contact) {}
+
+ BlockState getBlockState() const {
+ return state_;
+ }
+
+ JID getContact() const {
+ return contact_;
+ }
+ private:
+ BlockState state_;
+ JID contact_;
+};
+
+}
diff --git a/Swift/Controllers/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/BlockListEditorWidget.h b/Swift/Controllers/UIInterfaces/BlockListEditorWidget.h
new file mode 100644
index 0000000..60a1c11
--- /dev/null
+++ b/Swift/Controllers/UIInterfaces/BlockListEditorWidget.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <Swiften/JID/JID.h>
+#include <Swiften/Base/boost_bsignals.h>
+
+namespace Swift {
+
+ class ClientBlockListManager;
+
+ class BlockListEditorWidget {
+ public:
+ virtual ~BlockListEditorWidget() {}
+
+ virtual void show() = 0;
+
+ virtual void setCurrentBlockList(const std::vector<JID>& blockedJIDs) = 0;
+ virtual void setBusy(bool isBusy) = 0;
+
+ virtual std::vector<JID> getCurrentBlockList() const = 0;
+
+ boost::signal<void (const std::vector<JID>& /* blockedJID */)> onSetNewBlockList;
+ };
+
+}
diff --git a/Swift/Controllers/UIInterfaces/BlockListEditorWidgetFactory.h b/Swift/Controllers/UIInterfaces/BlockListEditorWidgetFactory.h
new file mode 100644
index 0000000..eb91ac1
--- /dev/null
+++ b/Swift/Controllers/UIInterfaces/BlockListEditorWidgetFactory.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+namespace Swift {
+
+ class BlockListEditorWidget;
+
+ class BlockListEditorWidgetFactory {
+ public:
+ virtual ~BlockListEditorWidgetFactory() {}
+
+ virtual BlockListEditorWidget* createBlockListEditorWidget() = 0;
+ };
+
+}
diff --git a/Swift/Controllers/UIInterfaces/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..50f2f26 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -6,17 +6,20 @@
#pragma once
+#include <vector>
+#include <string>
+
#include <boost/optional.hpp>
-#include <Swiften/Base/boost_bsignals.h>
#include <boost/shared_ptr.hpp>
+#include <boost/make_shared.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
-#include <vector>
-#include <string>
+#include <Swiften/Base/boost_bsignals.h>
#include <Swiften/Elements/SecurityLabelsCatalog.h>
#include <Swiften/Elements/ChatState.h>
#include <Swiften/Elements/Form.h>
#include <Swiften/Elements/MUCOccupant.h>
+#include <Swift/Controllers/HighlightManager.h>
namespace Swift {
@@ -29,31 +32,85 @@ namespace Swift {
class FileTransferController;
class InviteToChatWindow;
+
class ChatWindow {
public:
+ class ChatMessagePart {
+ public:
+ virtual ~ChatMessagePart() {}
+ };
+
+ class ChatMessage {
+ public:
+ ChatMessage() {}
+ ChatMessage(const std::string& text) {
+ append(boost::make_shared<ChatTextMessagePart>(text));
+ }
+ void append(const boost::shared_ptr<ChatMessagePart>& part) {
+ parts_.push_back(part);
+ }
+
+ const std::vector<boost::shared_ptr<ChatMessagePart> >& getParts() const {
+ return parts_;
+ }
+ private:
+ std::vector<boost::shared_ptr<ChatMessagePart> > parts_;
+ };
+
+ class ChatTextMessagePart : public ChatMessagePart {
+ public:
+ ChatTextMessagePart(const std::string& text) : text(text) {}
+ std::string text;
+ };
+
+ class ChatURIMessagePart : public ChatMessagePart {
+ public:
+ ChatURIMessagePart(const std::string& target) : target(target) {}
+ std::string target;
+ };
+
+ class ChatEmoticonMessagePart : public ChatMessagePart {
+ public:
+ std::string imagePath;
+ std::string alternativeText;
+ };
+
+ class ChatHighlightingMessagePart : public ChatMessagePart {
+ public:
+ std::string foregroundColor;
+ std::string backgroundColor;
+ std::string text;
+ };
+
+
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};
+ enum BlockingState {BlockingUnsupported, IsBlocked, IsUnblocked};
+ enum Direction { UnknownDirection, DefaultDirection };
+
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 ChatMessage& 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 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 std::string addAction(const ChatMessage& 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 ChatMessage& message, Direction direction) = 0;
+ virtual void addPresenceMessage(const ChatMessage& message, Direction direction) = 0;
+
+ virtual void addErrorMessage(const ChatMessage& message) = 0;
+ virtual void replaceMessage(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) = 0;
+ virtual void replaceWithAction(const ChatMessage& 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;
@@ -82,12 +139,13 @@ namespace Swift {
virtual void setInputEnabled(bool enabled) = 0;
virtual void setRosterModel(Roster* model) = 0;
virtual void setTabComplete(TabComplete* completer) = 0;
- virtual void replaceLastMessage(const std::string& message) = 0;
+ virtual void replaceLastMessage(const ChatMessage& message) = 0;
virtual void setAckState(const std::string& id, AckState state) = 0;
virtual void flash() = 0;
virtual void setSubject(const std::string& subject) = 0;
virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&) = 0;
virtual void setAvailableRoomActions(const std::vector<RoomAction> &actions) = 0;
+ virtual void setBlockingState(BlockingState state) = 0;
/**
* Set an alert on the window.
* @param alertText Description of alert (required).
@@ -141,6 +199,10 @@ namespace Swift {
boost::signal<void ()> onWhiteboardSessionAccept;
boost::signal<void ()> onWhiteboardSessionCancel;
boost::signal<void ()> onWhiteboardWindowShow;
+
+ // Blocking Command related
+ boost::signal<void ()> onBlockUserRequest;
+ boost::signal<void ()> onUnblockUserRequest;
};
}
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..3b10041 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_;
@@ -34,6 +34,7 @@ namespace Swift {
/** Must be able to cope with NULL to clear the roster */
virtual void setRosterModel(Roster* roster) = 0;
virtual void setConnecting() = 0;
+ virtual void setBlockingCommandAvailable(bool isAvailable) = 0;
virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands) = 0;
virtual void setStreamEncryptionStatus(bool tlsInPlaceAndValid) = 0;
virtual void openCertificateDialog(const std::vector<Certificate::ref>& chain) = 0;
diff --git a/Swift/Controllers/UIInterfaces/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..5c158e1 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&)> onWindowAboutToBeClosed;
};
}
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..990dc98 100644
--- a/Swift/Controllers/UIInterfaces/UIFactory.h
+++ b/Swift/Controllers/UIInterfaces/UIFactory.h
@@ -21,6 +21,8 @@
#include <Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h>
#include <Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h>
#include <Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h>
+#include <Swift/Controllers/UIInterfaces/HighlightEditorWidgetFactory.h>
+#include <Swift/Controllers/UIInterfaces/BlockListEditorWidgetFactory.h>
namespace Swift {
class UIFactory :
@@ -38,7 +40,9 @@ namespace Swift {
public ContactEditWindowFactory,
public AdHocCommandWindowFactory,
public FileTransferListWidgetFactory,
- public WhiteboardWindowFactory {
+ public WhiteboardWindowFactory,
+ public HighlightEditorWidgetFactory,
+ public BlockListEditorWidgetFactory {
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..74478d5 100644
--- a/Swift/Controllers/UnitTest/MockChatWindow.h
+++ b/Swift/Controllers/UnitTest/MockChatWindow.h
@@ -6,60 +6,81 @@
#pragma once
-#include "Swift/Controllers/UIInterfaces/ChatWindow.h"
+#include <boost/shared_ptr.hpp>
+
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+#include <Swiften/Base/foreach.h>
+
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 ChatMessage& 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*/) {
+ lastMessageBody_ = bodyFromMessage(message); return "id";}
+
+ virtual std::string addAction(const ChatMessage& /*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 "id";}
+
+ virtual void addSystemMessage(const ChatMessage& /*message*/, Direction /*direction*/) {}
+ virtual void addPresenceMessage(const ChatMessage& /*message*/, Direction /*direction*/) {}
+
+ virtual void addErrorMessage(const ChatMessage& /*message*/) {}
+ virtual void replaceMessage(const ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {}
+ virtual void replaceWithAction(const ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {}
+ virtual void replaceLastMessage(const ChatMessage& /*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*) {}
+
+ 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;}
+ virtual void setBlockingState(BlockingState) {}
+
+ std::string bodyFromMessage(const ChatMessage& message) {
+ boost::shared_ptr<ChatTextMessagePart> text;
+ foreach (boost::shared_ptr<ChatMessagePart> part, message.getParts()) {
+ if ((text = boost::dynamic_pointer_cast<ChatTextMessagePart>(part))) {
+ return text->text;
+ }
+ }
+ return "";
+ }
+
std::string name_;
std::string lastMessageBody_;
std::vector<SecurityLabelsCatalog::Item> labels_;
diff --git a/Swift/Controllers/UnitTest/MockMainWindow.h b/Swift/Controllers/UnitTest/MockMainWindow.h
index be1a932..69a4e25 100644
--- a/Swift/Controllers/UnitTest/MockMainWindow.h
+++ b/Swift/Controllers/UnitTest/MockMainWindow.h
@@ -12,18 +12,19 @@ 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*/) {}
+ virtual void setBlockingCommandAvailable(bool /*isAvailable*/) {}
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/Packaging/Debian/debian/control.in b/Swift/Packaging/Debian/debian/control.in
index c42d1d0..dd7be88 100644
--- a/Swift/Packaging/Debian/debian/control.in
+++ b/Swift/Packaging/Debian/debian/control.in
@@ -3,7 +3,7 @@ Section: net
Priority: optional
Maintainer: Swift Package Maintainer <packages@swift.im>
Uploaders: Remko Tronçon <dev@el-tramo.be>, Kevin Smith <kevin@kismith.co.uk>
-Build-Depends: debhelper (>= 7), scons (>= 1.2.0), libssl-dev (>= 0.9.8g), libqt4-dev (>= 4.5.0), libxml2-dev (>= 2.7.6), libxss-dev (>= 1.2.0), libboost-dev (>= 1.34.1), libboost-filesystem-dev (>= 1.34.1), libboost-program-options-dev (>= 1.34.1), libboost-regex-dev (>= 1.34.1), libboost-signals-dev (>= 1.34.1), libboost-system-dev (>= 1.34.1), libboost-thread-dev (>= 1.34.1), libboost-date-time-dev (>= 1.34.1), libidn11-dev (>= 1.10), docbook-xsl (>= 1.75.0), docbook-xml (>= 4.5), xsltproc, libxml2-utils %WEBKIT_DEPENDENCY%
+Build-Depends: debhelper (>= 7), scons (>= 1.2.0), libssl-dev (>= 0.9.8g), libqt4-dev (>= 4.5.0), libxml2-dev (>= 2.7.6), libxss-dev (>= 1.2.0), libboost-dev (>= 1.34.1), libboost-filesystem-dev (>= 1.34.1), libboost-program-options-dev (>= 1.34.1), libboost-regex-dev (>= 1.34.1), libboost-signals-dev (>= 1.34.1), libboost-system-dev (>= 1.34.1), libboost-thread-dev (>= 1.34.1), libboost-date-time-dev (>= 1.34.1), libidn11-dev (>= 1.10), docbook-xsl (>= 1.75.0), docbook-xml (>= 4.5), xsltproc, libxml2-utils, libhunspell-dev, libnatpmp-dev, libminiupnpc-dev, libsqlite3-dev %WEBKIT_DEPENDENCY%
Standards-Version: 3.9.3
Vcs-Git: git://swift.im/swift
Vcs-Browser: http://swift.im/git/swift
diff --git a/Swift/Packaging/Debian/package.sh b/Swift/Packaging/Debian/package.sh
index d4b6484..8c7c89f 100755
--- a/Swift/Packaging/Debian/package.sh
+++ b/Swift/Packaging/Debian/package.sh
@@ -50,9 +50,9 @@ else
rm -rf $DIRNAME/.git
find $DIRNAME -name .gitignore | xargs rm -f
if [ -z "$SWIFT_COPY_UUID" ]; then
- find $DIRNAME/3rdParty -type f | grep -v SConscript | xargs rm -f
+ find $DIRNAME/3rdParty -type f | grep -v SConscript | grep -v miniupnp | grep -v natpmp |xargs rm -f
else
- find $DIRNAME/3rdParty -type f | grep -v uuid | grep -v SConscript | xargs rm -f
+ find $DIRNAME/3rdParty -type f | grep -v uuid | grep -v SConscript | grep -v miniupnp | grep -v natpmp || xargs rm -f
fi
find $DIRNAME/3rdParty -depth -empty -type d -exec rmdir {} \;
rm -rf $DIRNAME/3rdParty/SCons
diff --git a/Swift/QtUI/ChatList/ChatListDelegate.cpp b/Swift/QtUI/ChatList/ChatListDelegate.cpp
index 5b879df..5b03ac5 100644
--- a/Swift/QtUI/ChatList/ChatListDelegate.cpp
+++ b/Swift/QtUI/ChatList/ChatListDelegate.cpp
@@ -120,7 +120,7 @@ void ChatListDelegate::paintRecent(QPainter* painter, const QStyleOptionViewItem
QString name = item->data(Qt::DisplayRole).toString();
//qDebug() << "Avatar for " << name << " = " << avatarPath;
QString statusText = item->data(ChatListRecentItem::DetailTextRole).toString();
- common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, item->getChat().unreadCount, compact_);
+ common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, false, item->getChat().unreadCount, compact_);
}
void ChatListDelegate::paintWhiteboard(QPainter* painter, const QStyleOptionViewItem& option, ChatListWhiteboardItem* item) const {
@@ -135,7 +135,8 @@ void ChatListDelegate::paintWhiteboard(QPainter* painter, const QStyleOptionView
QString name = item->data(Qt::DisplayRole).toString();
//qDebug() << "Avatar for " << name << " = " << avatarPath;
QString statusText = item->data(ChatListWhiteboardItem::DetailTextRole).toString();
- common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, item->getChat().unreadCount, compact_);
+ common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, false, item->getChat().unreadCount, compact_);
+
}
}
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/ChatList/ChatListRecentItem.cpp b/Swift/QtUI/ChatList/ChatListRecentItem.cpp
index 6c9807f..5a4d1e1 100644
--- a/Swift/QtUI/ChatList/ChatListRecentItem.cpp
+++ b/Swift/QtUI/ChatList/ChatListRecentItem.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 Kevin Smith
+ * Copyright (c) 2011-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -7,6 +7,7 @@
#include <Swift/QtUI/ChatList/ChatListRecentItem.h>
#include <Swift/QtUI/QtSwiftUtil.h>
+#include <Swiften/Base/Path.h>
namespace Swift {
ChatListRecentItem::ChatListRecentItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent) : ChatListItem(parent), chat_(chat) {
@@ -25,7 +26,7 @@ QVariant ChatListRecentItem::data(int role) const {
case Qt::BackgroundColorRole: return backgroundColor_;
case Qt::ToolTipRole: return isContact() ? toolTipString() : QVariant();
case StatusTextRole: return statusText_;*/
- case AvatarRole: return QVariant(QString(chat_.avatarPath.string().c_str()));
+ case AvatarRole: return QVariant(P2QSTRING(pathToString(chat_.avatarPath)));
case PresenceIconRole: return getPresenceIcon();
default: return QVariant();
}
diff --git a/Swift/QtUI/ChatList/ChatListRecentItem.h b/Swift/QtUI/ChatList/ChatListRecentItem.h
index 4e7bc3e..3f27a68 100644
--- a/Swift/QtUI/ChatList/ChatListRecentItem.h
+++ b/Swift/QtUI/ChatList/ChatListRecentItem.h
@@ -23,7 +23,8 @@ namespace Swift {
DetailTextRole = Qt::UserRole,
AvatarRole = Qt::UserRole + 1,
PresenceIconRole = Qt::UserRole + 2/*,
- StatusShowTypeRole = Qt::UserRole + 3*/
+ StatusShowTypeRole = Qt::UserRole + 3,
+ IdleRole = Qt::UserRole + 4*/
};
ChatListRecentItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent);
const ChatListWindow::Chat& getChat() const;
diff --git a/Swift/QtUI/ChatList/ChatListWhiteboardItem.cpp b/Swift/QtUI/ChatList/ChatListWhiteboardItem.cpp
index 41648b6..6791aa5 100644
--- a/Swift/QtUI/ChatList/ChatListWhiteboardItem.cpp
+++ b/Swift/QtUI/ChatList/ChatListWhiteboardItem.cpp
@@ -4,9 +4,16 @@
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
+/*
+ * Copyright (c) 2013 Remko Tronçon
+ * Licensed under the GNU General Public License.
+ * See the COPYING file for more information.
+ */
+
#include <Swift/QtUI/ChatList/ChatListWhiteboardItem.h>
#include <Swift/QtUI/QtSwiftUtil.h>
+#include <Swiften/Base/Path.h>
namespace Swift {
ChatListWhiteboardItem::ChatListWhiteboardItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent) : ChatListItem(parent), chat_(chat) {
@@ -25,7 +32,7 @@ namespace Swift {
case Qt::BackgroundColorRole: return backgroundColor_;
case Qt::ToolTipRole: return isContact() ? toolTipString() : QVariant();
case StatusTextRole: return statusText_;*/
- case AvatarRole: return QVariant(QString(chat_.avatarPath.string().c_str()));
+ case AvatarRole: return QVariant(P2QSTRING(pathToString(chat_.avatarPath)));
case PresenceIconRole: return getPresenceIcon();
default: return QVariant();
}
diff --git a/Swift/QtUI/ChatSnippet.cpp b/Swift/QtUI/ChatSnippet.cpp
index ab31d29..3436531 100644
--- a/Swift/QtUI/ChatSnippet.cpp
+++ b/Swift/QtUI/ChatSnippet.cpp
@@ -1,12 +1,14 @@
/*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
+#include <Swift/QtUI/ChatSnippet.h>
+
#include <QFile>
-#include "ChatSnippet.h"
+#include <Swift/QtUI/QtSwiftUtil.h>
namespace Swift {
@@ -39,4 +41,58 @@ QString ChatSnippet::wrapResizable(const QString& text) {
return "<span class='swift_resizable'>" + text + "</span>";
}
-};
+QString ChatSnippet::directionToCSS(Direction direction) {
+ return direction == RTL ? QString("rtl") : QString("ltr");
+}
+
+ChatSnippet::Direction ChatSnippet::getDirection(const ChatWindow::ChatMessage& message) {
+ boost::shared_ptr<ChatWindow::ChatTextMessagePart> textPart;
+ std::string text = "";
+ foreach (boost::shared_ptr<ChatWindow::ChatMessagePart> part, message.getParts()) {
+ if ((textPart = boost::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(part))) {
+ text = textPart->text;
+ break;
+ }
+ }
+ return getDirection(text);
+}
+
+ChatSnippet::Direction ChatSnippet::getDirection(const std::string& message) {
+ return getDirection(P2QSTRING(message));
+}
+
+ChatSnippet::Direction ChatSnippet::getDirection(const QString& message) {
+ /*
+ for (int i = 0; i < message.size(); ++i) {
+ switch (message.at(i).direction()) {
+ case QChar::DirL:
+ case QChar::DirLRE:
+ case QChar::DirLRO:
+ return ChatSnippet::LTR;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirRLE:
+ case QChar::DirRLO:
+ return ChatSnippet::RTL;
+ case QChar::DirEN:
+ case QChar::DirES:
+ case QChar::DirET:
+ case QChar::DirAN:
+ case QChar::DirCS:
+ case QChar::DirB:
+ case QChar::DirWS:
+ case QChar::DirON:
+ case QChar::DirS:
+ case QChar::DirPDF:
+ case QChar::DirNSM:
+ case QChar::DirBN:
+ break;
+ }
+ }
+ return ChatSnippet::LTR;
+ */
+ return message.isRightToLeft() ? ChatSnippet::RTL : ChatSnippet::LTR;
+}
+
+
+}
diff --git a/Swift/QtUI/ChatSnippet.h b/Swift/QtUI/ChatSnippet.h
index 78e0b88..f60d486 100644
--- a/Swift/QtUI/ChatSnippet.h
+++ b/Swift/QtUI/ChatSnippet.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -10,11 +10,20 @@
#include <QString>
#include <QDateTime>
-#include "QtChatTheme.h"
+
+#include <Swiften/Base/foreach.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+#include <Swift/QtUI/QtChatTheme.h>
+
namespace Swift {
class ChatSnippet {
public:
+ enum Direction {
+ RTL,
+ LTR
+ };
+
ChatSnippet(bool appendToPrevious);
virtual ~ChatSnippet();
@@ -42,7 +51,13 @@ namespace Swift {
static QString timeToEscapedString(const QDateTime& time);
+ static Direction getDirection(const std::string& message);
+ static Direction getDirection(const ChatWindow::ChatMessage& message);
+ static Direction getDirection(const QString& message);
+
protected:
+ static QString directionToCSS(Direction direction);
+
QString wrapResizable(const QString& text);
void setContinuationFallbackSnippet(boost::shared_ptr<ChatSnippet> continuationFallback) {
continuationFallback_ = continuationFallback;
diff --git a/Swift/QtUI/EventViewer/EventDelegate.cpp b/Swift/QtUI/EventViewer/EventDelegate.cpp
index 9ecdd34..c0904b3 100644
--- a/Swift/QtUI/EventViewer/EventDelegate.cpp
+++ b/Swift/QtUI/EventViewer/EventDelegate.cpp
@@ -25,12 +25,13 @@ QSize EventDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIn
return QStyledItemDelegate::sizeHint(option, index);
}
switch (getEventType(item->getEvent())) {
- case MessageEventType: return messageDelegate_.sizeHint(option, item);
- 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);
+ case MessageEventType: return messageDelegate_.sizeHint(option, item);
+ case SubscriptionEventType: return subscriptionDelegate_.sizeHint(option, item);
+ case ErrorEventType: return errorDelegate_.sizeHint(option, item);
+ case MUCInviteEventType: return mucInviteDelegate_.sizeHint(option, item);
}
+ assert(false);
+ return QSize();
}
void EventDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
@@ -40,23 +41,30 @@ void EventDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
return;
}
switch (getEventType(item->getEvent())) {
- case MessageEventType: messageDelegate_.paint(painter, option, item);break;
- 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);
+ case MessageEventType: messageDelegate_.paint(painter, option, item);break;
+ case SubscriptionEventType: subscriptionDelegate_.paint(painter, option, item);break;
+ case ErrorEventType: errorDelegate_.paint(painter, option, item);break;
+ case MUCInviteEventType: mucInviteDelegate_.paint(painter, option, item);break;
}
}
EventType EventDelegate::getEventType(boost::shared_ptr<StanzaEvent> event) const {
boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(event);
- if (messageEvent) return MessageEventType;
+ if (messageEvent) {
+ return MessageEventType;
+ }
boost::shared_ptr<SubscriptionRequestEvent> subscriptionEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(event);
- if (subscriptionEvent) return SubscriptionEventType;
+ if (subscriptionEvent) {
+ return SubscriptionEventType;
+ }
boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(event);
- if (errorEvent) return ErrorEventType;
+ if (errorEvent) {
+ return ErrorEventType;
+ }
boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event);
- if (mucInviteEvent) return MUCInviteEventType;
+ if (mucInviteEvent) {
+ return MUCInviteEventType;
+ }
//I don't know what this is.
assert(false);
return MessageEventType;
diff --git a/Swift/QtUI/EventViewer/QtEvent.cpp b/Swift/QtUI/EventViewer/QtEvent.cpp
index 3c6f16c..c3ff944 100644
--- a/Swift/QtUI/EventViewer/QtEvent.cpp
+++ b/Swift/QtUI/EventViewer/QtEvent.cpp
@@ -7,6 +7,7 @@
#include "Swift/QtUI/EventViewer/QtEvent.h"
#include <QDateTime>
+#include <QColor>
#include "Swift/Controllers/XMPPEvents/MessageEvent.h"
#include "Swift/Controllers/XMPPEvents/ErrorEvent.h"
@@ -25,8 +26,8 @@ QVariant QtEvent::data(int role) {
switch (role) {
case Qt::ToolTipRole: return QVariant(text()).toString() + "\n" + B2QDATE(event_->getTime()).toString();
case Qt::DisplayRole: return QVariant(text());
- case Qt::TextColorRole: return active_ ? Qt::black : Qt::darkGray;
- case Qt::BackgroundColorRole: return active_ ? Qt::white : Qt::lightGray;
+ case Qt::TextColorRole: return QColor(active_ ? Qt::black : Qt::darkGray);
+ case Qt::BackgroundColorRole: return QColor(active_ ? Qt::white : Qt::lightGray);
case SenderRole: return QVariant(sender());
/*case StatusTextRole: return statusText_;
case AvatarRole: return avatar_;
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.cpp b/Swift/QtUI/FreeDesktopNotifier.cpp
index 2393340..1f1ccda 100644
--- a/Swift/QtUI/FreeDesktopNotifier.cpp
+++ b/Swift/QtUI/FreeDesktopNotifier.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.
*/
@@ -13,6 +13,9 @@
#include <QStringList>
#include <QtDBus/QtDBus>
#include <algorithm>
+#include <Swiften/Base/Path.h>
+
+#include <Swift/QtUI/QtSwiftUtil.h>
namespace Swift {
@@ -43,7 +46,7 @@ void FreeDesktopNotifier::showMessage(Type type, const std::string& subject, con
hints["x-canonical-append"] = QString("allowed");
msg << applicationName.c_str();
msg << quint32(0); // ID of previous notification to replace
- msg << imageScaler.getScaledImage(picture, 48).string().c_str(); // Icon to display
+ msg << P2QSTRING(pathToString(imageScaler.getScaledImage(picture, 48))); // Icon to display
msg << subject.c_str(); // Summary / Header of the message to display
msg << body; // Body of the message to display
msg << actions; // Actions from which the user may choose
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/MUCSearch/QtMUCSearchWindow.cpp b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp
index 2fa24c2..7bd16e3 100644
--- a/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp
+++ b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp
@@ -23,7 +23,7 @@ namespace Swift {
QtMUCSearchWindow::QtMUCSearchWindow() {
ui_.setupUi(this);
-#ifndef Q_WS_MAC
+#ifndef Q_OS_MAC
setWindowIcon(QIcon(":/logo-icon-16.png"));
#endif
setModal(true);
diff --git a/Swift/QtUI/MessageSnippet.cpp b/Swift/QtUI/MessageSnippet.cpp
index 47aa9f8..28c44c4 100644
--- a/Swift/QtUI/MessageSnippet.cpp
+++ b/Swift/QtUI/MessageSnippet.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -11,9 +11,9 @@
namespace Swift {
-MessageSnippet::MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id) : ChatSnippet(appendToPrevious) {
+MessageSnippet::MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id, Direction direction) : ChatSnippet(appendToPrevious) {
if (appendToPrevious) {
- setContinuationFallbackSnippet(boost::shared_ptr<ChatSnippet>(new MessageSnippet(message, sender, time, iconURI, isIncoming, false, theme, id)));
+ setContinuationFallbackSnippet(boost::shared_ptr<ChatSnippet>(new MessageSnippet(message, sender, time, iconURI, isIncoming, false, theme, id, direction)));
}
if (isIncoming) {
if (appendToPrevious) {
@@ -32,12 +32,13 @@ MessageSnippet::MessageSnippet(const QString& message, const QString& sender, co
}
}
+ content_.replace("%direction%", directionToCSS(direction));
content_.replace("%message%", wrapResizable("<span class='swift_message'>" + escape(message) + "</span><span class='swift_ack'></span><span class='swift_receipt'></span>"));
content_.replace("%wrapped_sender%", wrapResizable(escape(sender)));
content_.replace("%sender%", escape(sender));
content_.replace("%time%", wrapResizable("<span class='swift_time'>" + timeToEscapedString(time) + "</span>"));
content_.replace("%userIconPath%", escape(iconURI));
- content_ = "<div id='" + id + "'>" + content_ + "</div>";
+ content_ = QString("<div id='%1'>%2</div>").arg(id).arg(content_);
content_ = "<span class='date" + time.date().toString(Qt::ISODate) + "'>" + content_ + "</span>";
}
diff --git a/Swift/QtUI/MessageSnippet.h b/Swift/QtUI/MessageSnippet.h
index c7425e9..8186d19 100644
--- a/Swift/QtUI/MessageSnippet.h
+++ b/Swift/QtUI/MessageSnippet.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -15,7 +15,7 @@ class QDateTime;
namespace Swift {
class MessageSnippet : public ChatSnippet {
public:
- MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id);
+ MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id, Direction direction);
virtual ~MessageSnippet();
const QString& getContent() const {
return content_;
@@ -23,7 +23,7 @@ namespace Swift {
QString getContinuationElementID() const {
return "insert";
- };
+ }
private:
QString content_;
diff --git a/Swift/QtUI/QtAboutWidget.cpp b/Swift/QtUI/QtAboutWidget.cpp
index acdc61e..c00acf7 100644
--- a/Swift/QtUI/QtAboutWidget.cpp
+++ b/Swift/QtUI/QtAboutWidget.cpp
@@ -19,7 +19,7 @@
namespace Swift {
QtAboutWidget::QtAboutWidget() : QDialog() {
-#ifndef Q_WS_MAC
+#ifndef Q_OS_MAC
setWindowTitle(QString(tr("About %1")).arg("Swift"));
#endif
setWindowIcon(QIcon(":/logo-icon-16.png"));
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..015c2da 100644
--- a/Swift/QtUI/QtAvatarWidget.cpp
+++ b/Swift/QtUI/QtAvatarWidget.cpp
@@ -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.
*/
@@ -19,6 +19,7 @@
#include <QPainter>
#include <QtSwiftUtil.h>
+#include <Swiften/Base/Path.h>
namespace Swift {
@@ -68,6 +69,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);
@@ -81,7 +85,7 @@ void QtAvatarWidget::mousePressEvent(QMouseEvent* event) {
QString fileName = QFileDialog::getOpenFileName(this, tr("Select picture"), "", tr("Image Files (*.png *.jpg *.jpeg *.gif)"));
if (!fileName.isEmpty()) {
ByteArray data;
- readByteArrayFromFile(data, Q2PSTRING(fileName));
+ readByteArrayFromFile(data, stringToPath(Q2PSTRING(fileName)));
QBuffer buffer;
buffer.setData(reinterpret_cast<const char*>(vecptr(data)), data.size());
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/QtBlockListEditorWindow.cpp b/Swift/QtUI/QtBlockListEditorWindow.cpp
new file mode 100644
index 0000000..63e8d1f
--- /dev/null
+++ b/Swift/QtUI/QtBlockListEditorWindow.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <QtBlockListEditorWindow.h>
+#include <ui_QtBlockListEditorWindow.h>
+
+#include <boost/bind.hpp>
+
+#include <QLineEdit>
+#include <QMovie>
+#include <QShortcut>
+#include <QStyledItemDelegate>
+#include <QValidator>
+
+#include <Swift/QtUI/QtUtilities.h>
+#include <Swiften/Client/ClientBlockListManager.h>
+#include <Swiften/Base/foreach.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+
+class QtJIDValidator : public QValidator {
+ public:
+ QtJIDValidator(QObject* parent) : QValidator(parent) {}
+ virtual ~QtJIDValidator() {}
+ virtual QValidator::State validate(QString& input, int&) const {
+ return JID(Q2PSTRING(input)).isValid() ? Acceptable : Intermediate;
+ }
+};
+
+class QtJIDValidatedItemDelegate : public QItemDelegate {
+ public:
+ QtJIDValidatedItemDelegate(QObject* parent) : QItemDelegate(parent) {}
+ virtual ~QtJIDValidatedItemDelegate() {}
+
+ virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const {
+ QLineEdit *editor = new QLineEdit(parent);
+ editor->setValidator(new QtJIDValidator(editor));
+ return editor;
+ }
+
+ void setEditorData(QWidget *editor, const QModelIndex &index) const {
+ QString value = index.model()->data(index, Qt::EditRole).toString();
+
+ QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
+ lineEdit->setText(value);
+ }
+
+ void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
+ QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
+ QString currentValue = lineEdit->text();
+ int pos = 0;
+ if (lineEdit->validator()->validate(currentValue, pos) == QValidator::Acceptable) {
+ model->setData(index, lineEdit->text(), Qt::EditRole);
+ } else {
+ model->setData(index, QString(), Qt::EditRole);
+ }
+ }
+
+ void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const {
+ editor->setGeometry(option.rect);
+ }
+};
+
+QtBlockListEditorWindow::QtBlockListEditorWindow() : QWidget(), ui(new Ui::QtBlockListEditorWindow) {
+ ui->setupUi(this);
+ new QShortcut(QKeySequence::Close, this, SLOT(close()));
+ ui->throbberLabel->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this));
+
+ itemDelegate = new QtRemovableItemDelegate(style());
+
+ connect(ui->savePushButton, SIGNAL(clicked()), SLOT(applyChanges()));
+
+ ui->blockListTreeWidget->setColumnCount(2);
+ ui->blockListTreeWidget->header()->setStretchLastSection(false);
+ int closeIconWidth = ui->blockListTreeWidget->fontMetrics().height();
+ ui->blockListTreeWidget->header()->resizeSection(1, closeIconWidth);
+
+#if QT_VERSION >= 0x050000
+ ui->blockListTreeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch);
+#else
+ ui->blockListTreeWidget->header()->setResizeMode(0, QHeaderView::Stretch);
+#endif
+
+ ui->blockListTreeWidget->setHeaderHidden(true);
+ ui->blockListTreeWidget->setRootIsDecorated(false);
+ ui->blockListTreeWidget->setEditTriggers(QAbstractItemView::DoubleClicked);
+ ui->blockListTreeWidget->setItemDelegateForColumn(0, new QtJIDValidatedItemDelegate(this));
+ ui->blockListTreeWidget->setItemDelegateForColumn(1, itemDelegate);
+ connect(ui->blockListTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), SLOT(handleItemChanged(QTreeWidgetItem*,int)));
+
+ QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << "");
+ item->setFlags(item->flags() | Qt::ItemIsEditable);
+ ui->blockListTreeWidget->addTopLevelItem(item);
+}
+
+QtBlockListEditorWindow::~QtBlockListEditorWindow() {
+}
+
+void QtBlockListEditorWindow::show() {
+ QWidget::show();
+ QWidget::activateWindow();
+}
+
+void QtBlockListEditorWindow::handleItemChanged(QTreeWidgetItem *, int) {
+ bool hasEmptyRow = false;
+ QList<QTreeWidgetItem*> rows = ui->blockListTreeWidget->findItems("", Qt::MatchFixedString);
+ foreach(QTreeWidgetItem* row, rows) {
+ if (row->text(0).isEmpty()) {
+ hasEmptyRow = true;
+ }
+ }
+
+ if (!hasEmptyRow) {
+ QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << "");
+ item->setFlags(item->flags() | Qt::ItemIsEditable);
+ ui->blockListTreeWidget->addTopLevelItem(item);
+ }
+}
+
+void QtBlockListEditorWindow::applyChanges() {
+ onSetNewBlockList(getCurrentBlockList());
+}
+
+void Swift::QtBlockListEditorWindow::setCurrentBlockList(const std::vector<JID> &blockedJIDs) {
+ ui->blockListTreeWidget->clear();
+
+ foreach(const JID& jid, blockedJIDs) {
+ QTreeWidgetItem* item = new QTreeWidgetItem(QStringList(P2QSTRING(jid.toString())) << "");
+ item->setFlags(item->flags() | Qt::ItemIsEditable);
+ ui->blockListTreeWidget->addTopLevelItem(item);
+ }
+ handleItemChanged(0,0);
+}
+
+void Swift::QtBlockListEditorWindow::setBusy(bool isBusy) {
+ if (isBusy) {
+ ui->throbberLabel->movie()->start();
+ ui->throbberLabel->show();
+ } else {
+ ui->throbberLabel->movie()->stop();
+ ui->throbberLabel->hide();
+ }
+}
+
+std::vector<JID> Swift::QtBlockListEditorWindow::getCurrentBlockList() const {
+ std::vector<JID> futureBlockedJIDs;
+
+ for(int i=0; i < ui->blockListTreeWidget->topLevelItemCount(); ++i) {
+ QTreeWidgetItem* row = ui->blockListTreeWidget->topLevelItem(i);
+ if (!row->text(0).isEmpty()) {
+ futureBlockedJIDs.push_back(JID(Q2PSTRING(row->text(0))));
+ }
+ }
+ return futureBlockedJIDs;
+}
+
+}
diff --git a/Swift/QtUI/QtBlockListEditorWindow.h b/Swift/QtUI/QtBlockListEditorWindow.h
new file mode 100644
index 0000000..4b124a3
--- /dev/null
+++ b/Swift/QtUI/QtBlockListEditorWindow.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swift/Controllers/UIInterfaces/BlockListEditorWidget.h>
+#include <Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h>
+
+#include <QWidget>
+#include <QTreeWidgetItem>
+
+namespace Ui {
+ class QtBlockListEditorWindow;
+}
+
+namespace Swift {
+
+class QtBlockListEditorWindow : public QWidget, public BlockListEditorWidget {
+ Q_OBJECT
+
+ public:
+ QtBlockListEditorWindow();
+ virtual ~QtBlockListEditorWindow();
+
+ virtual void show();
+ virtual void setCurrentBlockList(const std::vector<JID>& blockedJIDs);
+ virtual void setBusy(bool isBusy);
+ virtual std::vector<JID> getCurrentBlockList() const;
+
+ private slots:
+ void handleItemChanged(QTreeWidgetItem*, int);
+ void applyChanges();
+
+ private:
+ Ui::QtBlockListEditorWindow* ui;
+ QtRemovableItemDelegate* itemDelegate;
+};
+
+}
diff --git a/Swift/QtUI/QtBlockListEditorWindow.ui b/Swift/QtUI/QtBlockListEditorWindow.ui
new file mode 100644
index 0000000..f71bbae
--- /dev/null
+++ b/Swift/QtUI/QtBlockListEditorWindow.ui
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtBlockListEditorWindow</class>
+ <widget class="QWidget" name="QtBlockListEditorWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>348</width>
+ <height>262</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Edit Block List</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ <item>
+ <widget class="QTreeWidget" name="blockListTreeWidget">
+ <attribute name="headerVisible">
+ <bool>false</bool>
+ </attribute>
+ <column>
+ <property name="text">
+ <string notr="true">1</string>
+ </property>
+ </column>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="errorLabel">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="throbberLabel">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="savePushButton">
+ <property name="text">
+ <string>Save</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Swift/QtUI/QtCachedImageScaler.cpp b/Swift/QtUI/QtCachedImageScaler.cpp
index 7307577..45375e7 100644
--- a/Swift/QtUI/QtCachedImageScaler.cpp
+++ b/Swift/QtUI/QtCachedImageScaler.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,8 @@
#include <QImage>
#include <boost/lexical_cast.hpp>
+#include <Swiften/Base/Path.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
namespace Swift {
@@ -15,16 +17,18 @@ QtCachedImageScaler::QtCachedImageScaler() {
}
boost::filesystem::path QtCachedImageScaler::getScaledImage(const boost::filesystem::path& imagePath, int size) {
- boost::filesystem::path scaledImagePath(imagePath.string() + "." + boost::lexical_cast<std::string>(size));
+ boost::filesystem::path scaledImagePath(imagePath);
+ std::string suffix = "." + boost::lexical_cast<std::string>(size);
+ scaledImagePath = stringToPath(pathToString(scaledImagePath) + suffix);
if (!boost::filesystem::exists(scaledImagePath)) {
- QImage image(imagePath.string().c_str());
+ QImage image(P2QSTRING(pathToString(imagePath)));
if (!image.isNull()) {
if (image.width() > size || image.height() > size) {
QImage scaledImage = image.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
- scaledImage.save(QString::fromUtf8(scaledImagePath.string().c_str()), "PNG");
+ scaledImage.save(P2QSTRING(pathToString(scaledImagePath)), "PNG");
}
else {
- image.save(QString::fromUtf8(scaledImagePath.string().c_str()), "PNG");
+ image.save(P2QSTRING(pathToString(scaledImagePath)), "PNG");
}
}
else {
diff --git a/Swift/QtUI/QtChatTabs.cpp b/Swift/QtUI/QtChatTabs.cpp
index d3a5676..a119043 100644
--- a/Swift/QtUI/QtChatTabs.cpp
+++ b/Swift/QtUI/QtChatTabs.cpp
@@ -22,8 +22,8 @@
#include <qdebug.h>
namespace Swift {
-QtChatTabs::QtChatTabs() : QWidget() {
-#ifndef Q_WS_MAC
+QtChatTabs::QtChatTabs(bool singleWindow) : QWidget(), singleWindow_(singleWindow) {
+#ifndef Q_OS_MAC
setWindowIcon(QIcon(":/logo-chat-16.png"));
#else
setAttribute(Qt::WA_ShowWithoutActivating);
@@ -46,7 +46,6 @@ QtChatTabs::QtChatTabs() : QWidget() {
layout->setContentsMargins(0, 3, 0, 0);
layout->addWidget(tabs_);
setLayout(layout);
- //resize(400, 300);
}
void QtChatTabs::closeEvent(QCloseEvent* event) {
@@ -114,7 +113,13 @@ void QtChatTabs::handleTabClosing() {
if (widget && ((index = tabs_->indexOf(widget)) >= 0)) {
tabs_->removeTab(index);
if (tabs_->count() == 0) {
- hide();
+ if (!singleWindow_) {
+ hide();
+ }
+ else {
+ setWindowTitle("");
+ onTitleChanged("");
+ }
}
else {
handleTabTitleUpdated(tabs_->currentWidget());
@@ -237,7 +242,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);
@@ -251,7 +256,9 @@ void QtChatTabs::handleTabTitleUpdated(QWidget* widget) {
std::string current(Q2PSTRING(qobject_cast<QtTabbable*>(tabs_->currentWidget())->windowTitle()));
ChatMessageSummarizer summary;
- setWindowTitle(summary.getSummary(current, unreads).c_str());
+ QString title = summary.getSummary(current, unreads).c_str();
+ setWindowTitle(title);
+ emit onTitleChanged(title);
}
void QtChatTabs::flash() {
@@ -270,7 +277,7 @@ void QtChatTabs::moveEvent(QMoveEvent*) {
void QtChatTabs::checkForFirstShow() {
if (!isVisible()) {
-#ifndef Q_WS_MAC
+#ifndef Q_OS_MAC
showMinimized();
#else
/* https://bugreports.qt-project.org/browse/QTBUG-19194
diff --git a/Swift/QtUI/QtChatTabs.h b/Swift/QtUI/QtChatTabs.h
index 233c574..f9cd685 100644
--- a/Swift/QtUI/QtChatTabs.h
+++ b/Swift/QtUI/QtChatTabs.h
@@ -17,12 +17,13 @@ namespace Swift {
class QtChatTabs : public QWidget {
Q_OBJECT
public:
- QtChatTabs();
+ QtChatTabs(bool singleWindow);
void addTab(QtTabbable* tab);
void minimise();
QtTabbable* getCurrentTab();
signals:
void geometryChanged();
+ void onTitleChanged(const QString& title);
protected slots:
void closeEvent(QCloseEvent* event);
@@ -44,6 +45,7 @@ namespace Swift {
private:
void checkForFirstShow();
QtTabWidget* tabs_;
+ bool singleWindow_;
};
}
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..2536d39 100644
--- a/Swift/QtUI/QtChatView.cpp
+++ b/Swift/QtUI/QtChatView.cpp
@@ -41,7 +41,7 @@ QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent, bool disableAutoScro
connect(webView_, SIGNAL(clearRequested()), SLOT(handleClearRequested()));
connect(webView_, SIGNAL(fontGrowRequested()), SLOT(increaseFontSize()));
connect(webView_, SIGNAL(fontShrinkRequested()), SLOT(decreaseFontSize()));
-#ifdef Q_WS_X11
+#if defined (Q_OS_UNIX) && !defined(Q_OS_MAC)
/* To give a border on Linux, where it looks bad without */
QStackedWidget* stack = new QStackedWidget(this);
stack->addWidget(webView_);
@@ -128,7 +128,7 @@ void QtChatView::addMessageTop(boost::shared_ptr<ChatSnippet> snippet) {
double size = 1.0 + 0.2 * fontSizeSteps_;
QString sizeString(QString().setNum(size, 'g', 3) + "em");
const QWebElementCollection spans = firstElement_.findAll("span.swift_resizable");
- foreach (QWebElement span, spans) {
+ Q_FOREACH (QWebElement span, spans) {
span.setStyleProperty("font-size", sizeString);
}
}
@@ -142,6 +142,7 @@ QWebElement QtChatView::snippetToDOM(boost::shared_ptr<ChatSnippet> snippet) {
}
void QtChatView::addToDOM(boost::shared_ptr<ChatSnippet> snippet) {
+ //qDebug() << snippet->getContent();
rememberScrolledToBottom();
bool insert = snippet->getAppendToPrevious();
QWebElement continuationElement = lastElement_.findFirst("#insert");
@@ -160,10 +161,12 @@ void QtChatView::addToDOM(boost::shared_ptr<ChatSnippet> snippet) {
double size = 1.0 + 0.2 * fontSizeSteps_;
QString sizeString(QString().setNum(size, 'g', 3) + "em");
const QWebElementCollection spans = lastElement_.findAll("span.swift_resizable");
- foreach (QWebElement span, spans) {
+ Q_FOREACH (QWebElement span, spans) {
span.setStyleProperty("font-size", sizeString);
}
}
+ //qDebug() << "-----------------";
+ //qDebug() << webPage_->mainFrame()->toHtml();
}
void QtChatView::addLastSeenLine() {
@@ -226,13 +229,13 @@ void QtChatView::replaceMessage(const QString& newMessage, const QString& id, co
void QtChatView::showEmoticons(bool show) {
{
const QWebElementCollection spans = document_.findAll("span.swift_emoticon_image");
- foreach (QWebElement span, spans) {
+ Q_FOREACH (QWebElement span, spans) {
span.setStyleProperty("display", show ? "inline" : "none");
}
}
{
const QWebElementCollection spans = document_.findAll("span.swift_emoticon_text");
- foreach (QWebElement span, spans) {
+ Q_FOREACH (QWebElement span, spans) {
span.setStyleProperty("display", show ? "none" : "inline");
}
}
@@ -321,7 +324,7 @@ void QtChatView::resizeFont(int fontSizeSteps) {
QString sizeString(QString().setNum(size, 'g', 3) + "em");
//qDebug() << "Setting to " << sizeString;
const QWebElementCollection spans = document_.findAll("span.swift_resizable");
- foreach (QWebElement span, spans) {
+ Q_FOREACH (QWebElement span, spans) {
span.setStyleProperty("font-size", sizeString);
}
webView_->setFontSizeIsMinimal(size == 1.0);
@@ -365,9 +368,9 @@ 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) {
+ Q_FOREACH(QWebElement element, elements) {
if (element.attribute("id") == id) {
return element;
}
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index 28549f8..d81de61 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2012 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -25,14 +25,17 @@
#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
#include "QtChatWindowJSBridge.h"
+#include "QtUtilities.h"
#include <boost/cstdint.hpp>
#include <boost/format.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
#include <boost/lexical_cast.hpp>
#include <QLabel>
#include <qdebug.h>
#include <QMessageBox>
+#include <QMimeData>
#include <QInputDialog>
#include <QApplication>
#include <QBoxLayout>
@@ -45,6 +48,7 @@
#include <QTextEdit>
#include <QTime>
#include <QUrl>
+#include <QToolButton>
#include <QPushButton>
#include <QFileDialog>
#include <QMenu>
@@ -64,7 +68,7 @@ const QString QtChatWindow::ButtonFileTransferAcceptRequest = QString("filetrans
const QString QtChatWindow::ButtonMUCInvite = QString("mucinvite");
-QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), emoticons_(emoticons) {
+QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), blockingState_(BlockingUnsupported) {
settings_ = settings;
unreadCount_ = 0;
idCounter_ = 0;
@@ -103,21 +107,18 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
alertLabel_->setStyleSheet(alertStyleSheet_);
alertWidget_->hide();
- QBoxLayout* subjectLayout = new QBoxLayout(QBoxLayout::LeftToRight);
+ subjectLayout_ = new QBoxLayout(QBoxLayout::LeftToRight);
subject_ = new QLineEdit(this);
- subjectLayout->addWidget(subject_);
+ subjectLayout_->addWidget(subject_);
setSubject("");
subject_->setReadOnly(true);
- actionButton_ = new QPushButton(this);
+ QPushButton* actionButton_ = new QPushButton(this);
actionButton_->setIcon(QIcon(":/icons/actions.png"));
connect(actionButton_, SIGNAL(clicked()), this, SLOT(handleActionButtonClicked()));
- subjectLayout->addWidget(actionButton_);
-
subject_->hide();
- actionButton_->hide();
- layout->addLayout(subjectLayout);
+ layout->addLayout(subjectLayout_);
logRosterSplitter_ = new QSplitter(this);
logRosterSplitter_->setAutoFillBackground(true);
@@ -150,13 +151,19 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
QHBoxLayout* inputBarLayout = new QHBoxLayout();
inputBarLayout->setContentsMargins(0,0,0,0);
inputBarLayout->setSpacing(2);
- input_ = new QtTextEdit(this);
+ input_ = new QtTextEdit(settings_, this);
input_->setAcceptRichText(false);
inputBarLayout->addWidget(midBar_);
inputBarLayout->addWidget(input_);
correctingLabel_ = new QLabel(tr("Correcting"), this);
inputBarLayout->addWidget(correctingLabel_);
correctingLabel_->hide();
+
+ // using an extra layout to work around Qt margin glitches on OS X
+ QHBoxLayout* actionLayout = new QHBoxLayout();
+ actionLayout->addWidget(actionButton_);
+
+ inputBarLayout->addLayout(actionLayout);
layout->addLayout(inputBarLayout);
inputClearing_ = false;
@@ -215,6 +222,15 @@ bool QtChatWindow::appendToPreviousCheck(QtChatWindow::PreviousMessageKind messa
return previousMessageKind_ == messageKind && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_&& previousSenderName_ == P2QSTRING(senderName)));
}
+ChatSnippet::Direction QtChatWindow::getActualDirection(const ChatMessage& message, Direction direction) {
+ if (direction == DefaultDirection) {
+ return QCoreApplication::translate("QApplication", "QT_LAYOUT_DIRECTION") == "RTL" ? ChatSnippet::RTL : ChatSnippet::LTR;
+ }
+ else {
+ return ChatSnippet::getDirection(message);
+ }
+}
+
void QtChatWindow::handleFontResized(int fontSizeSteps) {
messageLog_->resizeFont(fontSizeSteps);
}
@@ -413,7 +429,6 @@ void QtChatWindow::convertToMUC() {
setAcceptDrops(false);
treeWidget_->show();
subject_->show();
- actionButton_->show();
}
void QtChatWindow::qAppFocusChanged(QWidget* /*old*/, QWidget* /*now*/) {
@@ -481,28 +496,95 @@ 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 ChatMessage& 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(chatMessageToHTML(message), senderName, senderIsSelf, label, avatarPath, "", time, highlight, ChatSnippet::getDirection(message));
+}
+
+QString QtChatWindow::chatMessageToHTML(const ChatMessage& message) {
+ QString result;
+ foreach (boost::shared_ptr<ChatMessagePart> part, message.getParts()) {
+ boost::shared_ptr<ChatTextMessagePart> textPart;
+ boost::shared_ptr<ChatURIMessagePart> uriPart;
+ boost::shared_ptr<ChatEmoticonMessagePart> emoticonPart;
+ boost::shared_ptr<ChatHighlightingMessagePart> highlightPart;
+
+ if ((textPart = boost::dynamic_pointer_cast<ChatTextMessagePart>(part))) {
+ QString text = QtUtilities::htmlEscape(P2QSTRING(textPart->text));
+ text.replace("\n","<br/>");
+ result += text;
+ continue;
+ }
+ if ((uriPart = boost::dynamic_pointer_cast<ChatURIMessagePart>(part))) {
+ QString uri = QtUtilities::htmlEscape(P2QSTRING(uriPart->target));
+ result += "<a href='" + uri + "' >" + uri + "</a>";
+ continue;
+ }
+ if ((emoticonPart = boost::dynamic_pointer_cast<ChatEmoticonMessagePart>(part))) {
+ QString textStyle = showEmoticons_ ? "style='display:none'" : "";
+ QString imageStyle = showEmoticons_ ? "" : "style='display:none'";
+ result += "<span class='swift_emoticon_image' " + imageStyle + "><img src='" + P2QSTRING(emoticonPart->imagePath) + "'/></span><span class='swift_emoticon_text' " + textStyle + ">" + QtUtilities::htmlEscape(P2QSTRING(emoticonPart->alternativeText)) + "</span>";
+ continue;
+ }
+ if ((highlightPart = boost::dynamic_pointer_cast<ChatHighlightingMessagePart>(part))) {
+ //FIXME: Maybe do something here. Anything, really.
+ continue;
+ }
+
+ }
+ return result;
+}
+
+/*QString QtChatWindow::linkimoticonify(const std::string& message) const {
+ return linkimoticonify(P2QSTRING(message));
}
QString QtChatWindow::linkimoticonify(const QString& message) const {
QString messageHTML(message);
- messageHTML = Qt::escape(messageHTML);
+ messageHTML = QtUtilities::htmlEscape(messageHTML);
QMapIterator<QString, QString> it(emoticons_);
- QString textStyle = showEmoticons_ ? "style='display:none'" : "";
- QString imageStyle = showEmoticons_ ? "" : "style='display:none'";
+
if (messageHTML.length() < 500) {
while (it.hasNext()) {
it.next();
- messageHTML.replace(it.key(), "<span class='swift_emoticon_image' " + imageStyle + "><img src='" + it.value() + "'/></span><span class='swift_emoticon_text' " + textStyle + ">"+it.key() + "</span>");
+ messageHTML.replace(it.key(), );
}
messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML)));
}
messageHTML.replace("\n","<br/>");
return messageHTML;
+}*/
+
+QString QtChatWindow::getHighlightSpanStart(const HighlightAction& highlight) {
+ QString color = QtUtilities::htmlEscape(P2QSTRING(highlight.getTextColor()));
+ QString background = QtUtilities::htmlEscape(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) {
+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,
+ ChatSnippet::Direction direction) {
+
if (isWidgetSelected()) {
onAllMessagesRead();
}
@@ -510,13 +592,15 @@ std::string QtChatWindow::addMessage(const QString &message, const std::string &
QString htmlString;
if (label) {
- htmlString = QString("<span style=\"border: thin dashed grey; padding-left: .5em; padding-right: .5em; color: %1; background-color: %2; font-size: 90%; margin-right: .5em; \" class='swift_label'>").arg(Qt::escape(P2QSTRING(label->getForegroundColor()))).arg(Qt::escape(P2QSTRING(label->getBackgroundColor())));
- htmlString += QString("%1</span> ").arg(Qt::escape(P2QSTRING(label->getDisplayMarking())));
+ htmlString = QString("<span style=\"border: thin dashed grey; padding-left: .5em; padding-right: .5em; color: %1; background-color: %2; font-size: 90%; margin-right: .5em; \" class='swift_label'>").arg(QtUtilities::htmlEscape(P2QSTRING(label->getForegroundColor()))).arg(QtUtilities::htmlEscape(P2QSTRING(label->getBackgroundColor())));
+ htmlString += QString("%1</span> ").arg(QtUtilities::htmlEscape(P2QSTRING(label->getDisplayMarking())));
}
- 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 + message + highlightSpanEnd + styleSpanEnd + "</span>" ;
bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf);
if (lastLineTracker_.getShouldMoveLastLine()) {
@@ -527,7 +611,7 @@ std::string QtChatWindow::addMessage(const QString &message, const std::string &
}
QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded();
std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++);
- messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
+ messageLog_->addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), direction));
previousMessageWasSelf_ = senderIsSelf;
previousSenderName_ = P2QSTRING(senderName);
@@ -572,10 +656,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 ChatMessage& 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(" *" + chatMessageToHTML(message) + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time, highlight, ChatSnippet::getDirection(message));
}
+// 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 +672,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) {
- return Qt::escape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str)))));
+static QString encodeButtonArgument(const QString& str) {
+ return QtUtilities::htmlEscape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str)))));
}
-QString decodeButtonArgument(const QString& str) {
+static QString decodeButtonArgument(const QString& str) {
return P2QSTRING(byteArrayToString(Base64::decode(Q2PSTRING(str))));
}
@@ -606,11 +691,13 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se
SWIFT_LOG(debug) << "addFileTransfer" << std::endl;
QString ft_id = QString("ft%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++)));
+ QString actionText;
QString htmlString;
QString formattedFileSize = P2QSTRING(formatSize(sizeInBytes));
if (senderIsSelf) {
// outgoing
- htmlString = tr("Send file") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" +
+ actionText = tr("Send file");
+ htmlString = actionText + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" +
"<div id='" + ft_id + "'>" +
buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) +
buildChatWindowButton(tr("Set Description"), ButtonFileTransferSetDescription, ft_id) +
@@ -618,7 +705,8 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se
"</div>";
} else {
// incoming
- htmlString = tr("Receiving file") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" +
+ actionText = tr("Receiving file");
+ htmlString = actionText + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" +
"<div id='" + ft_id + "'>" +
buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) +
buildChatWindowButton(tr("Accept"), ButtonFileTransferAcceptRequest, ft_id, P2QSTRING(filename)) +
@@ -636,7 +724,7 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se
}
QString qAvatarPath = "qrc:/icons/avatar.png";
std::string id = "ftmessage" + boost::lexical_cast<std::string>(idCounter_++);
- messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
+ messageLog_->addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), ChatSnippet::getDirection(actionText)));
previousMessageWasSelf_ = senderIsSelf;
previousSenderName_ = P2QSTRING(senderName);
@@ -645,22 +733,25 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se
}
void QtChatWindow::setFileTransferProgress(std::string id, const int percentageDone) {
- messageLog_->setFileTransferProgress(QString::fromStdString(id), percentageDone);
+ messageLog_->setFileTransferProgress(P2QSTRING(id), percentageDone);
}
void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg) {
- messageLog_->setFileTransferStatus(QString::fromStdString(id), state, QString::fromStdString(msg));
+ messageLog_->setFileTransferStatus(P2QSTRING(id), state, P2QSTRING(msg));
}
std::string QtChatWindow::addWhiteboardRequest(bool senderIsSelf) {
QString wb_id = QString("wb%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++)));
QString htmlString;
+ QString actionText;
if (senderIsSelf) {
- htmlString = "<div id='" + wb_id + "'>" + tr("Starting whiteboard chat") + "<br />"+
+ actionText = tr("Starting whiteboard chat");
+ htmlString = "<div id='" + wb_id + "'>" + actionText + "<br />"+
buildChatWindowButton(tr("Cancel"), ButtonWhiteboardSessionCancel, wb_id) +
"</div>";
} else {
- htmlString = "<div id='" + wb_id + "'>" + tr("%1 would like to start a whiteboard chat").arg(Qt::escape(contact_)) + ": <br/>" +
+ actionText = tr("%1 would like to start a whiteboard chat");
+ htmlString = "<div id='" + wb_id + "'>" + actionText.arg(QtUtilities::htmlEscape(contact_)) + ": <br/>" +
buildChatWindowButton(tr("Cancel"), ButtonWhiteboardSessionCancel, wb_id) +
buildChatWindowButton(tr("Accept"), ButtonWhiteboardSessionAcceptRequest, wb_id) +
"</div>";
@@ -674,7 +765,7 @@ std::string QtChatWindow::addWhiteboardRequest(bool senderIsSelf) {
}
QString qAvatarPath = "qrc:/icons/avatar.png";
std::string id = "wbmessage" + boost::lexical_cast<std::string>(idCounter_++);
- messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(contact_), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, false, theme_, P2QSTRING(id))));
+ messageLog_->addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(contact_), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, false, theme_, P2QSTRING(id), ChatSnippet::getDirection(actionText)));
previousMessageWasSelf_ = false;
previousSenderName_ = contact_;
@@ -682,7 +773,7 @@ std::string QtChatWindow::addWhiteboardRequest(bool senderIsSelf) {
}
void QtChatWindow::setWhiteboardSessionStatus(std::string id, const ChatWindow::WhiteboardSessionState state) {
- messageLog_->setWhiteboardSessionStatus(QString::fromStdString(id), state);
+ messageLog_->setWhiteboardSessionStatus(P2QSTRING(id), state);
}
void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1, QString encodedArgument2, QString encodedArgument3) {
@@ -719,12 +810,12 @@ void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1,
}
else if (id.startsWith(ButtonWhiteboardSessionAcceptRequest)) {
QString id = arg1;
- messageLog_->setWhiteboardSessionStatus(QString::fromStdString(Q2PSTRING(id)), ChatWindow::WhiteboardAccepted);
+ messageLog_->setWhiteboardSessionStatus(id, ChatWindow::WhiteboardAccepted);
onWhiteboardSessionAccept();
}
else if (id.startsWith(ButtonWhiteboardSessionCancel)) {
QString id = arg1;
- messageLog_->setWhiteboardSessionStatus(QString::fromStdString(Q2PSTRING(id)), ChatWindow::WhiteboardTerminated);
+ messageLog_->setWhiteboardSessionStatus(id, ChatWindow::WhiteboardTerminated);
onWhiteboardSessionCancel();
}
else if (id.startsWith(ButtonWhiteboardShowWindow)) {
@@ -744,40 +835,39 @@ void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1,
}
}
-void QtChatWindow::addErrorMessage(const std::string& errorMessage) {
+void QtChatWindow::addErrorMessage(const ChatMessage& errorMessage) {
if (isWidgetSelected()) {
onAllMessagesRead();
}
- QString errorMessageHTML(Qt::escape(P2QSTRING(errorMessage)));
- errorMessageHTML.replace("\n","<br/>");
- messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_)));
+ QString errorMessageHTML(chatMessageToHTML(errorMessage));
+
+ messageLog_->addMessageBottom(boost::make_shared<SystemMessageSnippet>("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_, ChatSnippet::getDirection(errorMessage)));
previousMessageWasSelf_ = false;
previousMessageKind_ = PreviousMessageWasSystem;
}
-void QtChatWindow::addSystemMessage(const std::string& message) {
+void QtChatWindow::addSystemMessage(const ChatMessage& message, Direction direction) {
if (isWidgetSelected()) {
onAllMessagesRead();
}
- QString messageHTML(P2QSTRING(message));
- messageHTML = linkimoticonify(messageHTML);
- messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
+ QString messageHTML = chatMessageToHTML(message);
+ messageLog_->addMessageBottom(boost::make_shared<SystemMessageSnippet>(messageHTML, QDateTime::currentDateTime(), false, theme_, getActualDirection(message, direction)));
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 ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) {
+ replaceMessage(" *" + chatMessageToHTML(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 ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) {
+ replaceMessage(chatMessageToHTML(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 +877,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));
}
@@ -796,14 +888,13 @@ void QtChatWindow::replaceMessage(const QString& message, const std::string& id,
}
}
-void QtChatWindow::addPresenceMessage(const std::string& message) {
+void QtChatWindow::addPresenceMessage(const ChatMessage& message, Direction direction) {
if (isWidgetSelected()) {
onAllMessagesRead();
}
- QString messageHTML(P2QSTRING(message));
- messageHTML = linkimoticonify(messageHTML);
- messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_)));
+ QString messageHTML = chatMessageToHTML(message);
+ messageLog_->addMessageBottom(boost::make_shared<SystemMessageSnippet>(messageHTML, QDateTime::currentDateTime(), false, theme_, getActualDirection(message, direction)));
previousMessageKind_ = PreviousMessageWasPresence;
}
@@ -874,12 +965,15 @@ void QtChatWindow::dropEvent(QDropEvent *event) {
if (event->mimeData()->urls().size() == 1) {
onSendFileRequest(Q2PSTRING(event->mimeData()->urls().at(0).toLocalFile()));
} else {
- addSystemMessage("Sending of multiple files at once isn't supported at this time.");
+ std::string messageText(Q2PSTRING(tr("Sending of multiple files at once isn't supported at this time.")));
+ ChatMessage message;
+ message.append(boost::make_shared<ChatTextMessagePart>(messageText));
+ addSystemMessage(message, DefaultDirection);
}
}
-void QtChatWindow::replaceLastMessage(const std::string& message) {
- messageLog_->replaceLastMessage(linkimoticonify(P2QSTRING(message)));
+void QtChatWindow::replaceLastMessage(const ChatMessage& message) {
+ messageLog_->replaceLastMessage(chatMessageToHTML(message));
}
void QtChatWindow::setAvailableOccupantActions(const std::vector<OccupantAction>& actions) {
@@ -901,15 +995,26 @@ void QtChatWindow::handleActionButtonClicked() {
QAction* destroy = NULL;
QAction* invite = NULL;
- foreach(ChatWindow::RoomAction availableAction, availableRoomActions_)
- {
- switch(availableAction)
+ QAction* block = NULL;
+ QAction* unblock = NULL;
+
+ if (availableRoomActions_.empty()) {
+ if (blockingState_ == IsBlocked) {
+ unblock = contextMenu.addAction(tr("Unblock"));
+ } else if (blockingState_ == IsUnblocked) {
+ block = contextMenu.addAction(tr("Block"));
+ }
+ } else {
+ foreach(ChatWindow::RoomAction availableAction, availableRoomActions_)
{
- case ChatWindow::ChangeSubject: changeSubject = contextMenu.addAction(tr("Change subject…")); break;
- case ChatWindow::Configure: configure = contextMenu.addAction(tr("Configure room…")); break;
- case ChatWindow::Affiliations: affiliations = contextMenu.addAction(tr("Edit affiliations…")); break;
- case ChatWindow::Destroy: destroy = contextMenu.addAction(tr("Destroy room")); break;
- case ChatWindow::Invite: invite = contextMenu.addAction(tr("Invite person to this room…")); break;
+ switch(availableAction)
+ {
+ case ChatWindow::ChangeSubject: changeSubject = contextMenu.addAction(tr("Change subject…")); break;
+ case ChatWindow::Configure: configure = contextMenu.addAction(tr("Configure room…")); break;
+ case ChatWindow::Affiliations: affiliations = contextMenu.addAction(tr("Edit affiliations…")); break;
+ case ChatWindow::Destroy: destroy = contextMenu.addAction(tr("Destroy room")); break;
+ case ChatWindow::Invite: invite = contextMenu.addAction(tr("Invite person to this room…")); break;
+ }
}
}
@@ -949,6 +1054,12 @@ void QtChatWindow::handleActionButtonClicked() {
else if (result == invite) {
onInvitePersonToThisMUCRequest();
}
+ else if (result == block) {
+ onBlockUserRequest();
+ }
+ else if (result == unblock) {
+ onUnblockUserRequest();
+ }
}
void QtChatWindow::handleAffiliationEditorAccepted() {
@@ -960,11 +1071,14 @@ void QtChatWindow::setAffiliations(MUCOccupant::Affiliation affiliation, const s
affiliationEditor_->setAffiliations(affiliation, jids);
}
-void QtChatWindow::setAvailableRoomActions(const std::vector<RoomAction> &actions)
-{
+void QtChatWindow::setAvailableRoomActions(const std::vector<RoomAction>& actions) {
availableRoomActions_ = actions;
}
+void QtChatWindow::setBlockingState(BlockingState state) {
+ blockingState_ = state;
+}
+
void QtChatWindow::showRoomConfigurationForm(Form::ref form) {
if (mucConfigurationWindow_) {
delete mucConfigurationWindow_.data();
@@ -979,18 +1093,20 @@ void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& ji
onAllMessagesRead();
}
- QString htmlString = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + " <br/>";
+ QString message = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + "\n";
+ QString htmlString = message;
if (!reason.empty()) {
- htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "<br/>";
+ htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "\n";
}
if (!direct) {
- htmlString += QObject::tr("This person may not have really sent this invitation!") + "<br/>";
+ htmlString += QObject::tr("This person may not have really sent this invitation!") + "\n";
}
+ htmlString = chatMessageToHTML(ChatMessage(Q2PSTRING(htmlString)));
- QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++)));
+ QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++)));
htmlString += "<div id='" + id + "'>" +
- buildChatWindowButton(tr("Accept Invite"), ButtonMUCInvite, Qt::escape(P2QSTRING(jid.toString())), Qt::escape(P2QSTRING(password)), id) +
+ buildChatWindowButton(chatMessageToHTML(ChatMessage(Q2PSTRING((tr("Accept Invite"))))), ButtonMUCInvite, QtUtilities::htmlEscape(P2QSTRING(jid.toString())), QtUtilities::htmlEscape(P2QSTRING(password)), id) +
"</div>";
bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMUCInvite, senderName, false);
@@ -1002,7 +1118,7 @@ void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& ji
}
QString qAvatarPath = "qrc:/icons/avatar.png";
- messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id)));
+ messageLog_->addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id, ChatSnippet::getDirection(message)));
previousMessageWasSelf_ = false;
previousSenderName_ = P2QSTRING(senderName);
previousMessageKind_ = PreviousMessageWasMUCInvite;
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index 3416b42..a29edad 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2012 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -10,6 +10,7 @@
#include <Swift/QtUI/QtMUCConfigurationWindow.h>
#include <Swift/QtUI/QtAffiliationEditor.h>
#include <Swift/QtUI/QtSwiftUtil.h>
+#include <Swift/QtUI/ChatSnippet.h>
#include <QtTabbable.h>
@@ -37,6 +38,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:
@@ -83,15 +87,17 @@ namespace Swift {
static const QString ButtonMUCInvite;
public:
- QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons);
+ QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings);
~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);
- 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);
+ std::string addMessage(const ChatMessage& 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 ChatMessage& 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 ChatMessage& message, Direction direction);
+ void addPresenceMessage(const ChatMessage& message, Direction direction);
+ void addErrorMessage(const ChatMessage& message);
+
+ void replaceMessage(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight);
+ void replaceWithAction(const ChatMessage& 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);
@@ -116,7 +122,7 @@ namespace Swift {
void setRosterModel(Roster* roster);
void setTabComplete(TabComplete* completer);
int getCount();
- void replaceLastMessage(const std::string& message);
+ void replaceLastMessage(const ChatMessage& message);
void setAckState(const std::string& id, AckState state);
// message receipts
@@ -129,7 +135,8 @@ namespace Swift {
void showRoomConfigurationForm(Form::ref);
void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true);
void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&);
- void setAvailableRoomActions(const std::vector<RoomAction> &actions);
+ void setAvailableRoomActions(const std::vector<RoomAction>& actions);
+ void setBlockingState(BlockingState state);
InviteToChatWindow* createInviteToChatWindow();
@@ -189,11 +196,27 @@ 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,
+ ChatSnippet::Direction direction);
+ 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;
+ static ChatSnippet::Direction getActualDirection(const ChatMessage& message, Direction direction);
+ QString chatMessageToHTML(const ChatMessage& message);
+ QString getHighlightSpanStart(const HighlightAction& highlight);
int unreadCount_;
bool contactIsTyping_;
@@ -205,6 +228,7 @@ namespace Swift {
QtChatTheme* theme_;
QtTextEdit* input_;
QWidget* midBar_;
+ QBoxLayout* subjectLayout_;
QComboBox* labelsWidget_;
QtOccupantListWidget* treeWidget_;
QLabel* correctingLabel_;
@@ -213,7 +237,6 @@ namespace Swift {
QPushButton* alertButton_;
TabComplete* completer_;
QLineEdit* subject_;
- QPushButton* actionButton_;
bool isCorrection_;
bool previousMessageWasSelf_;
PreviousMessageKind previousMessageKind_;
@@ -232,9 +255,9 @@ namespace Swift {
int idCounter_;
SettingsProvider* settings_;
std::vector<ChatWindow::RoomAction> availableRoomActions_;
- QMap<QString, QString> emoticons_;
bool showEmoticons_;
QPalette defaultLabelsPalette_;
LabelModel* labelModel_;
+ BlockingState blockingState_;
};
}
diff --git a/Swift/QtUI/QtChatWindowFactory.cpp b/Swift/QtUI/QtChatWindowFactory.cpp
index 5f91ff8..78c04c9 100644
--- a/Swift/QtUI/QtChatWindowFactory.cpp
+++ b/Swift/QtUI/QtChatWindowFactory.cpp
@@ -4,23 +4,24 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "QtChatWindowFactory.h"
+#include <Swift/QtUI/QtChatWindowFactory.h>
#include <QDesktopWidget>
-
-#include "QtChatTabs.h"
-#include "QtChatWindow.h"
-#include "QtSwiftUtil.h"
-#include "QtChatTheme.h"
#include <qdebug.h>
+#include <Swift/QtUI/QtChatTabs.h>
+#include <Swift/QtUI/QtChatWindow.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+#include <Swift/QtUI/QtChatTheme.h>
+#include <Swift/QtUI/QtSingleWindow.h>
+
namespace Swift {
static const QString SPLITTER_STATE = "mucSplitterState";
static const QString CHAT_TABS_GEOMETRY = "chatTabsGeometry";
-QtChatWindowFactory::QtChatWindowFactory(QSplitter* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabs* tabs, const QString& themePath, QMap<QString, QString> emoticons) : themePath_(themePath), emoticons_(emoticons) {
+QtChatWindowFactory::QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabs* tabs, const QString& themePath) : themePath_(themePath) {
qtOnlySettings_ = qtSettings;
settings_ = settings;
tabs_ = tabs;
@@ -49,7 +50,7 @@ ChatWindow* QtChatWindowFactory::createChatWindow(const JID &contact,UIEventStre
}
}
- QtChatWindow *chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), theme_, eventStream, settings_, emoticons_);
+ QtChatWindow *chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), theme_, eventStream, settings_);
connect(chatWindow, SIGNAL(splitterMoved()), this, SLOT(handleSplitterMoved()));
connect(this, SIGNAL(changeSplitterState(QByteArray)), chatWindow, SLOT(handleChangeSplitterState(QByteArray)));
diff --git a/Swift/QtUI/QtChatWindowFactory.h b/Swift/QtUI/QtChatWindowFactory.h
index 4f59961..63da514 100644
--- a/Swift/QtUI/QtChatWindowFactory.h
+++ b/Swift/QtUI/QtChatWindowFactory.h
@@ -6,21 +6,24 @@
#pragma once
-#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"
-#include "Swiften/JID/JID.h"
-#include "QtSettingsProvider.h"
+#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
#include <QObject>
#include <QSplitter>
+
+#include <Swiften/JID/JID.h>
+#include <Swift/QtUI/QtSettingsProvider.h>
+
namespace Swift {
class QtChatTabs;
class QtChatTheme;
class UIEventStream;
class QtUIPreferences;
+ class QtSingleWindow;
class QtChatWindowFactory : public QObject, public ChatWindowFactory {
Q_OBJECT
public:
- QtChatWindowFactory(QSplitter* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabs* tabs, const QString& themePath, QMap<QString, QString> emoticons);
+ QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabs* tabs, const QString& themePath);
~QtChatWindowFactory();
ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream);
signals:
@@ -34,7 +37,6 @@ namespace Swift {
QtSettingsProvider* qtOnlySettings_;
QtChatTabs* tabs_;
QtChatTheme* theme_;
- QMap<QString, QString> emoticons_;
};
}
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..00afacb 100644
--- a/Swift/QtUI/QtFileTransferListItemModel.cpp
+++ b/Swift/QtUI/QtFileTransferListItemModel.cpp
@@ -9,14 +9,15 @@
#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>
+#include "QtSwiftUtil.h"
namespace Swift {
-extern std::string formatSize(const boost::uintmax_t bytes);
-
QtFileTransferListItemModel::QtFileTransferListItemModel(QObject *parent) : QAbstractItemModel(parent), fileTransferOverview(0) {
}
@@ -65,11 +66,14 @@ QVariant QtFileTransferListItemModel::data(const QModelIndex &index, int role) c
return controller->isIncoming() ? QVariant(QObject::tr("Incoming")) : QVariant(QObject::tr("Outgoing"));
}
if (index.column() == OtherParty) {
- return QVariant(QString::fromStdString(controller->getOtherParty().toString()));
+ return QVariant(P2QSTRING(controller->getOtherParty().toString()));
}
if (index.column() == State) {
FileTransfer::State state = controller->getState();
- switch(state.state) {
+ switch(state.type) {
+ case FileTransfer::State::Initial:
+ assert(false);
+ return QVariant("");
case FileTransfer::State::WaitingForStart:
return QVariant(QObject::tr("Waiting for start"));
case FileTransfer::State::WaitingForAccept:
@@ -91,7 +95,7 @@ QVariant QtFileTransferListItemModel::data(const QModelIndex &index, int role) c
return QVariant(QString::number(controller->getProgress()));
}
if (index.column() == OverallSize) {
- return QVariant(QString::fromStdString(formatSize((controller->getSize()))));
+ return QVariant(P2QSTRING(formatSize((controller->getSize()))));
}
return QVariant();
}
@@ -105,7 +109,7 @@ int QtFileTransferListItemModel::rowCount(const QModelIndex& /* parent */) const
}
QModelIndex QtFileTransferListItemModel::index(int row, int column, const QModelIndex& /* parent */) const {
- return createIndex(row, column, 0);
+ return createIndex(row, column, (void*) 0);
}
}
diff --git a/Swift/QtUI/QtFileTransferListItemModel.h b/Swift/QtUI/QtFileTransferListItemModel.h
index 1d892a5..28f13f8 100644
--- a/Swift/QtUI/QtFileTransferListItemModel.h
+++ b/Swift/QtUI/QtFileTransferListItemModel.h
@@ -34,7 +34,7 @@ private:
State,
Progress,
OverallSize,
- NoOfColumns,
+ NoOfColumns
};
private:
diff --git a/Swift/QtUI/QtFormResultItemModel.cpp b/Swift/QtUI/QtFormResultItemModel.cpp
index 5461f05..b052334 100644
--- a/Swift/QtUI/QtFormResultItemModel.cpp
+++ b/Swift/QtUI/QtFormResultItemModel.cpp
@@ -35,7 +35,7 @@ QVariant QtFormResultItemModel::headerData(int section, Qt::Orientation /*orient
if (!formResult_) return QVariant();
if (role != Qt::DisplayRole) return QVariant();
if (static_cast<size_t>(section) >= formResult_->getReportedFields().size()) return QVariant();
- return QVariant(QString::fromStdString(formResult_->getReportedFields().at(section)->getLabel()));
+ return QVariant(P2QSTRING(formResult_->getReportedFields().at(section)->getLabel()));
}
int QtFormResultItemModel::rowCount(const QModelIndex &/*parent*/) const {
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>&amp;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>&amp;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>&amp;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 &amp;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 &amp;words</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QCheckBox" name="matchCase">
+ <property name="text">
+ <string>Match &amp;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>&amp;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&amp;olors:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="Swift::QtColorToolButton" name="foreground">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;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>&amp;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>&amp;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&amp;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..4efa712
--- /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, (void*) 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, (void*) 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..6f22b76 100644
--- a/Swift/QtUI/QtHistoryWindow.cpp
+++ b/Swift/QtUI/QtHistoryWindow.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Catalin Badea
+ * Copyright (c) 2012-2013 Catalin Badea
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
@@ -13,6 +13,7 @@
#include <string>
#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
#include <QTime>
#include <QUrl>
@@ -20,9 +21,12 @@
#include <QTextDocument>
#include <QDateTime>
#include <Swift/QtUI/QtScaledAvatarCache.h>
+#include <Swift/QtUI/ChatSnippet.h>
#include <QLineEdit>
+#include "QtUtilities.h"
#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/numeric/conversion/cast.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
namespace Swift {
@@ -107,7 +111,7 @@ void QtHistoryWindow::addMessage(const std::string &message, const std::string &
QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str());
QString messageHTML(P2QSTRING(message));
- messageHTML = Qt::escape(messageHTML);
+ messageHTML = QtUtilities::htmlEscape(messageHTML);
QString searchTerm = ui_.searchBox_->lineEdit()->text();
if (searchTerm.length()) {
messageHTML.replace(searchTerm, "<span style='background-color: yellow'>" + searchTerm + "</span>");
@@ -124,14 +128,14 @@ void QtHistoryWindow::addMessage(const std::string &message, const std::string &
if (addAtTheTop) {
bool appendToPrevious = ((senderIsSelf && previousTopMessageWasSelf_) || (!senderIsSelf && !previousTopMessageWasSelf_&& previousTopSenderName_ == P2QSTRING(senderName)));
- conversation_->addMessageTop(boost::shared_ptr<ChatSnippet>(new MessageSnippet(messageHTML, Qt::escape(P2QSTRING(senderName)), qTime, qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
+ conversation_->addMessageTop(boost::shared_ptr<ChatSnippet>(new MessageSnippet(messageHTML, QtUtilities::htmlEscape(P2QSTRING(senderName)), qTime, qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), ChatSnippet::getDirection(message))));
previousTopMessageWasSelf_ = senderIsSelf;
previousTopSenderName_ = P2QSTRING(senderName);
}
else {
bool appendToPrevious = ((senderIsSelf && previousBottomMessageWasSelf_) || (!senderIsSelf && !previousBottomMessageWasSelf_&& previousBottomSenderName_ == P2QSTRING(senderName)));
- conversation_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(messageHTML, Qt::escape(P2QSTRING(senderName)), qTime, qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
+ conversation_->addMessageBottom(boost::make_shared<MessageSnippet>(messageHTML, QtUtilities::htmlEscape(P2QSTRING(senderName)), qTime, qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), ChatSnippet::getDirection(message)));
previousBottomMessageWasSelf_ = senderIsSelf;
previousBottomSenderName_ = P2QSTRING(senderName);
}
@@ -183,7 +187,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 +198,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 +209,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 +246,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/QtJoinMUCWindow.ui b/Swift/QtUI/QtJoinMUCWindow.ui
index 5a69292..9225f6f 100644
--- a/Swift/QtUI/QtJoinMUCWindow.ui
+++ b/Swift/QtUI/QtJoinMUCWindow.ui
@@ -43,9 +43,6 @@
</property>
</widget>
</item>
- <item row="1" column="1" colspan="2">
- <widget class="QLineEdit" name="nickName"/>
- </item>
<item row="0" column="1">
<widget class="QLineEdit" name="room">
<property name="text">
@@ -63,6 +60,9 @@
<item row="2" column="1">
<widget class="QLineEdit" name="password"/>
</item>
+ <item row="1" column="1" colspan="2">
+ <widget class="QLineEdit" name="nickName"/>
+ </item>
</layout>
</item>
<item>
diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp
index c27edfb..188e55f 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>
@@ -54,7 +55,7 @@ QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream, SettingsProvider* set
uiEventStream_ = uiEventStream;
setWindowTitle("Swift");
-#ifndef Q_WS_MAC
+#ifndef Q_OS_MAC
setWindowIcon(QIcon(":/logo-icon-16.png"));
#endif
QtUtilities::setX11Resource(this, "Main");
@@ -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..572b06f 100644
--- a/Swift/QtUI/QtMainWindow.cpp
+++ b/Swift/QtUI/QtMainWindow.cpp
@@ -33,6 +33,7 @@
#include <Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h>
#include <Swift/QtUI/QtUISettingConstants.h>
#include <Swift/Controllers/SettingConstants.h>
#include <Swiften/Base/Platform.h>
@@ -47,14 +48,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 +96,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);
@@ -130,6 +139,10 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr
connect(viewLogsAction, SIGNAL(triggered()), SLOT(handleViewLogsAction()));
actionsMenu->addAction(viewLogsAction);
#endif
+ openBlockingListEditor_ = new QAction(tr("Edit &Blocking List…"), this);
+ connect(openBlockingListEditor_, SIGNAL(triggered()), SLOT(handleEditBlockingList()));
+ actionsMenu->addAction(openBlockingListEditor_);
+ openBlockingListEditor_->setVisible(false);
addUserAction_ = new QAction(tr("&Add Contact…"), this);
connect(addUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleAddUserActionTriggered(bool)));
actionsMenu->addAction(addUserAction_);
@@ -182,6 +195,10 @@ void QtMainWindow::handleShowCertificateInfo() {
onShowCertificateRequest();
}
+void QtMainWindow::handleEditBlockingList() {
+ uiEventStream_->send(boost::make_shared<RequestBlockListDialogUIEvent>());
+}
+
QtEventWindow* QtMainWindow::getEventWindow() {
return eventWindow_;
}
@@ -261,6 +278,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) {
@@ -343,5 +368,9 @@ void QtMainWindow::setAvailableAdHocCommands(const std::vector<DiscoItems::Item>
}
}
+void QtMainWindow::setBlockingCommandAvailable(bool isAvailable) {
+ openBlockingListEditor_->setVisible(isAvailable);
+}
+
}
diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h
index 26d25e1..3e6e1d3 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);
@@ -52,9 +53,11 @@ namespace Swift {
QtChatListWindow* getChatListWindow();
void setRosterModel(Roster* roster);
void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands);
+ void setBlockingCommandAvailable(bool isAvailable);
private slots:
void handleStatusChanged(StatusShow::Type showType, const QString &statusMessage);
void handleSettingChanged(const std::string& settingPath);
+ void handleCompactRosterToggled(bool);
void handleShowOfflineToggled(bool);
void handleShowEmoticonsToggled(bool);
void handleJoinMUCAction();
@@ -70,6 +73,7 @@ namespace Swift {
void handleTabChanged(int index);
void handleToggleRequestDeliveryReceipts(bool enabled);
void handleShowCertificateInfo();
+ void handleEditBlockingList();
private:
SettingsProvider* settings_;
@@ -81,7 +85,9 @@ namespace Swift {
QAction* editUserAction_;
QAction* chatUserAction_;
QAction* showOfflineAction_;
+ QAction* compactRosterAction_;
QAction* showEmoticonsAction_;
+ QAction* openBlockingListEditor_;
QAction* toggleRequestDeliveryReceipts_;
QMenu* serverAdHocMenu_;
QtTabWidget* tabs_;
diff --git a/Swift/QtUI/QtNameWidget.h b/Swift/QtUI/QtNameWidget.h
index 0f00c41..3225879 100644
--- a/Swift/QtUI/QtNameWidget.h
+++ b/Swift/QtUI/QtNameWidget.h
@@ -31,7 +31,7 @@ namespace Swift {
private:
enum Mode {
ShowNick,
- ShowJID,
+ ShowJID
};
SettingsProvider* settings;
diff --git a/Swift/QtUI/QtProfileWindow.cpp b/Swift/QtUI/QtProfileWindow.cpp
index 0faa78f..b1cdd19 100644
--- a/Swift/QtUI/QtProfileWindow.cpp
+++ b/Swift/QtUI/QtProfileWindow.cpp
@@ -4,97 +4,82 @@
* 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>
+#include <Swift/QtUI/QtUtilities.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(),
+ 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);
+ setAttribute(Qt::WA_DeleteOnClose);
+}
- 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'>" + QtUtilities::htmlEscape(P2QSTRING(error)) + "</font>");
}
else {
- throbberLabel->hide();
- throbberLabel->movie()->stop();
+ ui->errorLabel->setText("");
}
}
@@ -103,31 +88,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) {
+ event->accept();
+ onWindowAboutToBeClosed(jid);
+}
+void QtProfileWindow::handleSave() {
+ onVCardChangeRequest(ui->vcard->getVCard());
+}
}
diff --git a/Swift/QtUI/QtProfileWindow.h b/Swift/QtUI/QtProfileWindow.h
index edb9cce..a2af63a 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:
+ QtProfileWindow();
+ 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/QtSettingsProvider.cpp b/Swift/QtUI/QtSettingsProvider.cpp
index 64e88d4..a98cdf3 100644
--- a/Swift/QtUI/QtSettingsProvider.cpp
+++ b/Swift/QtUI/QtSettingsProvider.cpp
@@ -108,7 +108,7 @@ QSettings* QtSettingsProvider::getQSettings() {
}
void QtSettingsProvider::updatePermissions() {
-#if !defined(Q_WS_WIN) && !defined(Q_WS_MAC)
+#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC)
QFile file(settings_.fileName());
if (file.exists()) {
file.setPermissions(QFile::ReadOwner|QFile::WriteOwner);
diff --git a/Swift/QtUI/QtSingleWindow.cpp b/Swift/QtUI/QtSingleWindow.cpp
new file mode 100644
index 0000000..c970296
--- /dev/null
+++ b/Swift/QtUI/QtSingleWindow.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 <Swiften/Base/foreach.h>
+#include <Swift/QtUI/QtSettingsProvider.h>
+#include <Swift/QtUI/QtChatTabs.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::addWidget(QWidget* widget) {
+ QtChatTabs* tabs = dynamic_cast<QtChatTabs*>(widget);
+ if (tabs) {
+ connect(tabs, SIGNAL(onTitleChanged(const QString&)), this, SLOT(handleTabsTitleChanged(const QString&)));
+ }
+ QSplitter::addWidget(widget);
+}
+
+void QtSingleWindow::handleTabsTitleChanged(const QString& title) {
+ setWindowTitle(title);
+}
+
+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..b55b3c9
--- /dev/null
+++ b/Swift/QtUI/QtSingleWindow.h
@@ -0,0 +1,37 @@
+/*
+ * 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);
+ void addWidget(QWidget* widget);
+ protected:
+ void resizeEvent(QResizeEvent*);
+ void moveEvent(QMoveEvent*);
+ private slots:
+ void handleSplitterMoved(int, int);
+ void handleTabsTitleChanged(const QString& title);
+ private:
+ void handleGeometryChanged();
+ void restoreSplitters();
+
+ private:
+
+ QtSettingsProvider* settings_;
+ };
+
+}
+
diff --git a/Swift/QtUI/QtSoundPlayer.cpp b/Swift/QtUI/QtSoundPlayer.cpp
index 387c6f3..3f3782d 100644
--- a/Swift/QtUI/QtSoundPlayer.cpp
+++ b/Swift/QtUI/QtSoundPlayer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -9,17 +9,20 @@
#include <QSound>
#include <iostream>
-#include "SwifTools/Application/ApplicationPathProvider.h"
+#include <SwifTools/Application/ApplicationPathProvider.h>
+#include <QtSwiftUtil.h>
+#include <Swiften/Base/Path.h>
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;
}
}
@@ -27,7 +30,10 @@ void QtSoundPlayer::playSound(SoundEffect sound) {
void QtSoundPlayer::playSound(const std::string& soundResource) {
boost::filesystem::path resourcePath = applicationPathProvider->getResourcePath(soundResource);
if (boost::filesystem::exists(resourcePath)) {
- QSound::play(resourcePath.string().c_str());
+ QSound::play(P2QSTRING(pathToString(resourcePath)));
+ }
+ else if (boost::filesystem::exists(soundResource)) {
+ QSound::play(P2QSTRING(soundResource));
}
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/QtSpellCheckerWindow.cpp b/Swift/QtUI/QtSpellCheckerWindow.cpp
new file mode 100644
index 0000000..db2b1e7
--- /dev/null
+++ b/Swift/QtUI/QtSpellCheckerWindow.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2011 Vlad Voicu
+ * Licensed under the Simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "Swift/QtUI/QtSpellCheckerWindow.h"
+
+#include <Swift/Controllers/Settings/SettingsProvider.h>
+#include <Swift/Controllers/SettingConstants.h>
+#include <Swift/QtUI/QtUISettingConstants.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+
+#include <QCoreApplication>
+#include <QFileDialog>
+#include <QDir>
+#include <QStringList>
+#include <QTimer>
+
+namespace Swift {
+
+QtSpellCheckerWindow::QtSpellCheckerWindow(SettingsProvider* settings, QWidget* parent) : QDialog(parent) {
+ settings_ = settings;
+ ui_.setupUi(this);
+#ifdef HAVE_HUNSPELL
+ ui_.hunspellOptions->show();
+#else
+ ui_.hunspellOptions->hide();
+ QTimer::singleShot(0, this, SLOT(shrinkWindow()));
+#endif
+ connect(ui_.spellChecker, SIGNAL(toggled(bool)), this, SLOT(handleChecker(bool)));
+ connect(ui_.cancel, SIGNAL(clicked()), this, SLOT(handleCancel()));
+ connect(ui_.apply, SIGNAL(clicked()), this, SLOT(handleApply()));
+ connect(ui_.pathButton, SIGNAL(clicked()), this, SLOT(handlePathButton()));
+ setFromSettings();
+}
+
+void QtSpellCheckerWindow::shrinkWindow() {
+ resize(0,0);
+}
+
+void QtSpellCheckerWindow::setFromSettings() {
+ ui_.spellChecker->setChecked(settings_->getSetting(SettingConstants::SPELL_CHECKER));
+ ui_.pathContent->setText(P2QSTRING(settings_->getSetting(SettingConstants::DICT_PATH)));
+ ui_.currentLanguageValue->setText(P2QSTRING(settings_->getSetting(SettingConstants::DICT_FILE)));
+ std::string currentPath = settings_->getSetting(SettingConstants::DICT_PATH);
+ QString filename = "*.dic";
+ QDir dictDirectory = QDir(P2QSTRING(currentPath));
+ QStringList files = dictDirectory.entryList(QStringList(filename), QDir::Files);
+ showFiles(files);
+ setEnabled(settings_->getSetting(SettingConstants::SPELL_CHECKER));
+}
+
+void QtSpellCheckerWindow::handleChecker(bool state) {
+ setEnabled(state);
+}
+
+void QtSpellCheckerWindow::setEnabled(bool state) {
+ ui_.pathContent->setEnabled(state);
+ ui_.languageView->setEnabled(state);
+ ui_.pathButton->setEnabled(state);
+ ui_.pathLabel->setEnabled(state);
+ ui_.currentLanguage->setEnabled(state);
+ ui_.currentLanguageValue->setEnabled(state);
+ ui_.language->setEnabled(state);
+}
+
+void QtSpellCheckerWindow::handleApply() {
+ settings_->storeSetting(SettingConstants::SPELL_CHECKER, ui_.spellChecker->isChecked());
+ QList<QListWidgetItem* > selectedLanguage = ui_.languageView->selectedItems();
+ if (!selectedLanguage.empty()) {
+ settings_->storeSetting(SettingConstants::DICT_FILE, Q2PSTRING((selectedLanguage.first())->text()));
+ }
+ this->done(0);
+}
+
+void QtSpellCheckerWindow::handleCancel() {
+ this->done(0);
+}
+
+void QtSpellCheckerWindow::handlePathButton() {
+ std::string currentPath = settings_->getSetting(SettingConstants::DICT_PATH);
+ QString dirpath = QFileDialog::getExistingDirectory(this, tr("Dictionary Path"), P2QSTRING(currentPath));
+ if (dirpath != P2QSTRING(currentPath)) {
+ ui_.languageView->clear();
+ settings_->storeSetting(SettingConstants::DICT_FILE, "");
+ ui_.currentLanguageValue->setText(" ");
+ }
+ if (!dirpath.isEmpty()) {
+ if (!dirpath.endsWith("/")) {
+ dirpath.append("/");
+ }
+ settings_->storeSetting(SettingConstants::DICT_PATH, Q2PSTRING(dirpath));
+ QDir dictDirectory = QDir(dirpath);
+ ui_.pathContent->setText(dirpath);
+ QString filename = "*.dic";
+ QStringList files = dictDirectory.entryList(QStringList(filename), QDir::Files);
+ showFiles(files);
+ }
+}
+
+void QtSpellCheckerWindow::handlePersonalPathButton() {
+ std::string currentPath = settings_->getSetting(SettingConstants::PERSONAL_DICT_PATH);
+ QString filename = QFileDialog::getOpenFileName(this, tr("Select Personal Dictionary"), P2QSTRING(currentPath), tr("(*.dic"));
+ settings_->storeSetting(SettingConstants::PERSONAL_DICT_PATH, Q2PSTRING(filename));
+}
+
+void QtSpellCheckerWindow::showFiles(const QStringList& files) {
+ ui_.languageView->clear();
+ for (int i = 0; i < files.size(); ++i) {
+ ui_.languageView->insertItem(i, files[i]);
+ }
+}
+
+}
diff --git a/Swift/QtUI/QtSpellCheckerWindow.h b/Swift/QtUI/QtSpellCheckerWindow.h
new file mode 100644
index 0000000..7b63318
--- /dev/null
+++ b/Swift/QtUI/QtSpellCheckerWindow.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011 Vlad Voicu
+ * Licensed under the Simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include "ui_QtSpellCheckerWindow.h"
+
+#include <QDialog>
+
+namespace Swift {
+ class SettingsProvider;
+ class QtSpellCheckerWindow : public QDialog, protected Ui::QtSpellCheckerWindow {
+ Q_OBJECT
+ public:
+ QtSpellCheckerWindow(SettingsProvider* settings, QWidget* parent = NULL);
+ public slots:
+ void handleChecker(bool state);
+ void handleCancel();
+ void handlePathButton();
+ void handlePersonalPathButton();
+ void handleApply();
+ private slots:
+ void shrinkWindow();
+ private:
+ void setEnabled(bool state);
+ void setFromSettings();
+ void showFiles(const QStringList& files);
+ SettingsProvider* settings_;
+ Ui::QtSpellCheckerWindow ui_;
+ };
+}
diff --git a/Swift/QtUI/QtSpellCheckerWindow.ui b/Swift/QtUI/QtSpellCheckerWindow.ui
new file mode 100644
index 0000000..b7f5161
--- /dev/null
+++ b/Swift/QtUI/QtSpellCheckerWindow.ui
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtSpellCheckerWindow</class>
+ <widget class="QDialog" name="QtSpellCheckerWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>399</width>
+ <height>265</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QCheckBox" name="spellChecker">
+ <property name="text">
+ <string>Spell Checker Enabled</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="hunspellOptions" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="pathLabel">
+ <property name="text">
+ <string>Dictionary Path:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="pathContent"/>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pathButton">
+ <property name="text">
+ <string>Change</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="currentLanguage">
+ <property name="text">
+ <string>Current Language:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="currentLanguageValue">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QLabel" name="language">
+ <property name="text">
+ <string>Language:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QListWidget" name="languageView"/>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="cancel">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="apply">
+ <property name="text">
+ <string>Apply</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
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..4d4cef9 100644
--- a/Swift/QtUI/QtSwift.cpp
+++ b/Swift/QtUI/QtSwift.cpp
@@ -1,74 +1,81 @@
/*
- * Copyright (c) 2010-2012 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "QtSwift.h"
+#include <Swift/QtUI/QtSwift.h>
#include <string>
-#include <QSplitter>
-#include <QFile>
+#include <map>
+
#include <boost/bind.hpp>
+
+#include <QFile>
#include <QMessageBox>
#include <QApplication>
#include <QMap>
#include <qdebug.h>
-#include <QtLoginWindow.h>
-#include <QtChatTabs.h>
-#include <QtSystemTray.h>
-#include <QtSoundPlayer.h>
-#include <QtSwiftUtil.h>
-#include <QtUIFactory.h>
-#include <QtChatWindowFactory.h>
#include <Swiften/Base/Log.h>
-#include <Swift/Controllers/Storages/CertificateFileStorageFactory.h>
-#include <Swift/Controllers/Storages/FileStoragesFactory.h>
-#include <SwifTools/Application/PlatformApplicationPathProvider.h>
-#include <string>
+#include <Swiften/Base/Path.h>
#include <Swiften/Base/Platform.h>
#include <Swiften/Elements/Presence.h>
#include <Swiften/Client/Client.h>
+#include <Swiften/Base/Paths.h>
+
+#include <SwifTools/Application/PlatformApplicationPathProvider.h>
+#include <SwifTools/AutoUpdater/AutoUpdater.h>
+#include <SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h>
+
+#include <Swift/Controllers/Storages/CertificateFileStorageFactory.h>
+#include <Swift/Controllers/Storages/FileStoragesFactory.h>
#include <Swift/Controllers/Settings/XMLSettingsProvider.h>
#include <Swift/Controllers/Settings/SettingsProviderHierachy.h>
#include <Swift/Controllers/MainController.h>
#include <Swift/Controllers/ApplicationInfo.h>
#include <Swift/Controllers/BuildVersion.h>
-#include <SwifTools/AutoUpdater/AutoUpdater.h>
-#include <SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h>
-#include "Swiften/Base/Paths.h"
+#include <Swift/Controllers/StatusCache.h>
+
+#include <Swift/QtUI/QtLoginWindow.h>
+#include <Swift/QtUI/QtChatTabs.h>
+#include <Swift/QtUI/QtSystemTray.h>
+#include <Swift/QtUI/QtSoundPlayer.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+#include <Swift/QtUI/QtUIFactory.h>
+#include <Swift/QtUI/QtChatWindowFactory.h>
+#include <Swift/QtUI/QtSingleWindow.h>
#if defined(SWIFTEN_PLATFORM_WINDOWS)
-#include "WindowsNotifier.h"
+#include <Swift/QtUI/WindowsNotifier.h>
#elif defined(HAVE_GROWL)
-#include "SwifTools/Notifier/GrowlNotifier.h"
+#include <SwifTools/Notifier/GrowlNotifier.h>
#elif defined(SWIFTEN_PLATFORM_LINUX)
-#include "FreeDesktopNotifier.h"
+#include <Swift/QtUI/FreeDesktopNotifier.h>
#else
-#include "SwifTools/Notifier/NullNotifier.h"
+#include <SwifTools/Notifier/NullNotifier.h>
#endif
#if defined(SWIFTEN_PLATFORM_MACOSX)
-#include "SwifTools/Dock/MacOSXDock.h"
+#include <SwifTools/Dock/MacOSXDock.h>
#else
-#include "SwifTools/Dock/NullDock.h"
+#include <SwifTools/Dock/NullDock.h>
#endif
#if defined(SWIFTEN_PLATFORM_MACOSX)
-#include "QtURIHandler.h"
+#include <Swift/QtUI/QtURIHandler.h>
#elif defined(SWIFTEN_PLATFORM_WIN32)
#include <SwifTools/URIHandler/NullURIHandler.h>
#else
-#include "QtDBUSURIHandler.h"
+#include <Swift/QtUI/QtDBUSURIHandler.h>
#endif
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,44 +109,46 @@ 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, std::map<std::string, std::string>& 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[Q2PSTRING(tokens[0])] = Q2PSTRING(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);
QCoreApplication::setApplicationVersion(buildVersion);
qtSettings_ = new QtSettingsProvider();
- xmlSettings_ = loadSettingsFile(P2QSTRING((Paths::getExecutablePath() / "system-settings.xml").string()));
+ xmlSettings_ = loadSettingsFile(P2QSTRING(pathToString(Paths::getExecutablePath() / "system-settings.xml")));
settingsHierachy_ = new SettingsProviderHierachy();
settingsHierachy_->addProviderToTopOfStack(xmlSettings_);
settingsHierachy_->addProviderToTopOfStack(qtSettings_);
- QMap<QString, QString> emoticons = loadEmoticonsFile(P2QSTRING((Paths::getExecutablePath() / "emoticons.txt").string()));
+ std::map<std::string, std::string> emoticons;
+ loadEmoticonsFile(":/emoticons/emoticons.txt", emoticons);
+ loadEmoticonsFile(P2QSTRING(pathToString(Paths::getExecutablePath() / "emoticons.txt")), emoticons);
+
+ if (options.count("netbook-mode")) {
+ splitter_ = new QtSingleWindow(qtSettings_);
+ } else {
+ splitter_ = NULL;
+ }
int numberOfAccounts = 1;
try {
@@ -150,15 +159,15 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
}
if (options.count("debug")) {
- Swift::logging = true;
+ Log::setLogLevel(Swift::Log::debug);
}
- tabs_ = options.count("no-tabs") && !splitter_ ? NULL : new QtChatTabs();
+ tabs_ = options.count("no-tabs") && !splitter_ ? NULL : new QtChatTabs(splitter_ != NULL);
bool startMinimized = options.count("start-minimized") > 0;
applicationPathProvider_ = new PlatformApplicationPathProvider(SWIFT_APPLICATION_NAME);
- storagesFactory_ = new FileStoragesFactory(applicationPathProvider_->getDataDir());
- certificateStorageFactory_ = new CertificateFileStorageFactory(applicationPathProvider_->getDataDir(), tlsFactories_.getCertificateFactory());
- chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierachy_, qtSettings_, tabs_, "", emoticons);
+ storagesFactory_ = new FileStoragesFactory(applicationPathProvider_->getDataDir(), networkFactories_.getCryptoProvider());
+ certificateStorageFactory_ = new CertificateFileStorageFactory(applicationPathProvider_->getDataDir(), tlsFactories_.getCertificateFactory(), networkFactories_.getCryptoProvider());
+ chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierachy_, qtSettings_, tabs_, "");
soundPlayer_ = new QtSoundPlayer(applicationPathProvider_);
// Ugly, because the dock depends on the tray, but the temporary
@@ -190,6 +199,8 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
uriHandler_ = new QtDBUSURIHandler();
#endif
+ statusCache_ = new StatusCache(applicationPathProvider_);
+
if (splitter_) {
splitter_->show();
}
@@ -199,7 +210,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_,
@@ -214,6 +225,7 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
notifier_,
uriHandler_,
&idleDetector_,
+ emoticons,
options.count("latency-debug") > 0);
mainControllers_.push_back(mainController);
}
@@ -243,6 +255,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..1ea8886 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, std::map<std::string, std::string>& 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/QtSystemTray.cpp b/Swift/QtUI/QtSystemTray.cpp
index 2f45709..456a56f 100644
--- a/Swift/QtUI/QtSystemTray.cpp
+++ b/Swift/QtUI/QtSystemTray.cpp
@@ -9,7 +9,7 @@
#include "Swift/QtUI/QtSystemTray.h"
#include <QtDebug>
-#ifdef Q_WS_X11
+#if defined (Q_OS_UNIX) && !defined(Q_OS_MAC)
#include <QDBusInterface>
#endif
#include <QIcon>
@@ -24,7 +24,7 @@ QtSystemTray::QtSystemTray() : QObject(), trayMenu_(0), onlineIcon_(":icons/onli
trayIcon_->setToolTip("Swift");
connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(handleIconActivated(QSystemTrayIcon::ActivationReason)));
connect(&throbberMovie_, SIGNAL(frameChanged(int)), this, SLOT(handleThrobberFrameChanged(int)));
-#ifdef Q_WS_X11
+#if defined (Q_OS_UNIX) && !defined(Q_OS_MAC)
bool isUnity = QDBusInterface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher").isValid();
if (isUnity) {
// Add an activation menu, because this is the only way to get the application to the
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/QtTextEdit.cpp b/Swift/QtUI/QtTextEdit.cpp
index 3a62325..2c4677e 100644
--- a/Swift/QtUI/QtTextEdit.cpp
+++ b/Swift/QtUI/QtTextEdit.cpp
@@ -4,17 +4,41 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
+#include <boost/tuple/tuple.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/foreach.h>
+
+#include <SwifTools/SpellCheckerFactory.h>
+#include <SwifTools/SpellChecker.h>
+
#include <Swift/QtUI/QtTextEdit.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+#include <Swift/QtUI/QtSpellCheckerWindow.h>
+#include <Swift/Controllers/SettingConstants.h>
+#include <QApplication>
#include <QFontMetrics>
#include <QKeyEvent>
+#include <QDebug>
+#include <QMenu>
namespace Swift {
-QtTextEdit::QtTextEdit(QWidget* parent) : QTextEdit(parent){
+QtTextEdit::QtTextEdit(SettingsProvider* settings, QWidget* parent) : QTextEdit(parent) {
connect(this, SIGNAL(textChanged()), this, SLOT(handleTextChanged()));
+ checker_ = NULL;
+ settings_ = settings;
+#ifdef HAVE_SPELLCHECKER
+ setUpSpellChecker();
+#endif
handleTextChanged();
-};
+}
+
+QtTextEdit::~QtTextEdit() {
+ delete checker_;
+}
void QtTextEdit::keyPressEvent(QKeyEvent* event) {
int key = event->key();
@@ -35,13 +59,41 @@ void QtTextEdit::keyPressEvent(QKeyEvent* event) {
emit unhandledKeyPressEvent(event);
}
else if ((key == Qt::Key_Up)
- || (key == Qt::Key_Down)
- ){
+ || (key == Qt::Key_Down)) {
emit unhandledKeyPressEvent(event);
QTextEdit::keyPressEvent(event);
}
else {
QTextEdit::keyPressEvent(event);
+#ifdef HAVE_SPELLCHECKER
+ if (settings_->getSetting(SettingConstants::SPELL_CHECKER)) {
+ underlineMisspells();
+ }
+#endif
+ }
+}
+
+void QtTextEdit::underlineMisspells() {
+ QTextCursor cursor = textCursor();
+ misspelledPositions_.clear();
+ QTextCharFormat normalFormat;
+ cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor, 1);
+ cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor, 1);
+ cursor.setCharFormat(normalFormat);
+ if (checker_ == NULL) {
+ return;
+ }
+ QTextCharFormat spellingErrorFormat;
+ spellingErrorFormat.setUnderlineColor(QColor(Qt::red));
+ spellingErrorFormat.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline);
+ std::string fragment = Q2PSTRING(cursor.selectedText());
+ checker_->checkFragment(fragment, misspelledPositions_);
+ foreach (PositionPair position, misspelledPositions_) {
+ cursor.setPosition(boost::get<0>(position), QTextCursor::MoveAnchor);
+ cursor.setPosition(boost::get<1>(position), QTextCursor::KeepAnchor);
+ cursor.setCharFormat(spellingErrorFormat);
+ cursor.clearSelection();
+ cursor.setCharFormat(normalFormat);
}
}
@@ -53,6 +105,24 @@ void QtTextEdit::handleTextChanged() {
}
}
+void QtTextEdit::replaceMisspelledWord(const QString& word, int cursorPosition) {
+ QTextCursor cursor = textCursor();
+ PositionPair wordPosition = getWordFromCursor(cursorPosition);
+ cursor.setPosition(boost::get<0>(wordPosition), QTextCursor::MoveAnchor);
+ cursor.setPosition(boost::get<1>(wordPosition), QTextCursor::KeepAnchor);
+ QTextCharFormat normalFormat;
+ cursor.insertText(word, normalFormat);
+}
+
+PositionPair QtTextEdit::getWordFromCursor(int cursorPosition) {
+ for (PositionPairList::iterator it = misspelledPositions_.begin(); it != misspelledPositions_.end(); ++it) {
+ if (cursorPosition >= boost::get<0>(*it) && cursorPosition <= boost::get<1>(*it)) {
+ return *it;
+ }
+ }
+ return boost::make_tuple(-1,-1);
+}
+
QSize QtTextEdit::sizeHint() const {
QFontMetrics inputMetrics(currentFont());
QRect horizontalBounds = contentsRect().adjusted(0,0,0,9999);
@@ -66,7 +136,100 @@ QSize QtTextEdit::sizeHint() const {
//return QSize(QTextEdit::sizeHint().width(), lineHeight * numberOfLines);
}
+void QtTextEdit::contextMenuEvent(QContextMenuEvent* event) {
+ QMenu* menu = createStandardContextMenu();
+ QTextCursor cursor = cursorForPosition(event->pos());
+#ifdef HAVE_SPELLCHECKER
+ QAction* insertPoint = menu->actions().first();
+ QAction* settingsAction = new QAction(tr("Spell Checker Options"), menu);
+ menu->insertAction(insertPoint, settingsAction);
+ menu->insertAction(insertPoint, menu->addSeparator());
+ addSuggestions(menu, event);
+ QAction* result = menu->exec(event->globalPos());
+ if (result == settingsAction) {
+ spellCheckerSettingsWindow();
+ }
+ for (std::vector<QAction*>::iterator it = replaceWordActions_.begin(); it != replaceWordActions_.end(); ++it) {
+ if (*it == result) {
+ replaceMisspelledWord((*it)->text(), cursor.position());
+ }
+ }
+#else
+ menu->exec(event->globalPos());
+#endif
+ delete menu;
+}
+
+void QtTextEdit::addSuggestions(QMenu* menu, QContextMenuEvent* event)
+{
+ replaceWordActions_.clear();
+ QAction* insertPoint = menu->actions().first();
+ QTextCursor cursor = cursorForPosition(event->pos());
+ PositionPair wordPosition = getWordFromCursor(cursor.position());
+ if (boost::get<0>(wordPosition) < 0) {
+ // The click was executed outside a spellable word so no
+ // suggestions are necessary
+ return;
+ }
+ cursor.setPosition(boost::get<0>(wordPosition), QTextCursor::MoveAnchor);
+ cursor.setPosition(boost::get<1>(wordPosition), QTextCursor::KeepAnchor);
+ std::vector<std::string> wordList;
+ checker_->getSuggestions(Q2PSTRING(cursor.selectedText()), wordList);
+ if (wordList.size() == 0) {
+ QAction* noSuggestions = new QAction(tr("No Suggestions"), menu);
+ noSuggestions->setDisabled(true);
+ menu->insertAction(insertPoint, noSuggestions);
+ }
+ else {
+ for (std::vector<std::string>::iterator it = wordList.begin(); it != wordList.end(); ++it) {
+ QAction* wordAction = new QAction(it->c_str(), menu);
+ menu->insertAction(insertPoint, wordAction);
+ replaceWordActions_.push_back(wordAction);
+ }
+ }
+ menu->insertAction(insertPoint, menu->addSeparator());
+}
+
+
+#ifdef HAVE_SPELLCHECKER
+void QtTextEdit::setUpSpellChecker()
+{
+ SpellCheckerFactory* checkerFactory = new SpellCheckerFactory();
+ delete checker_;
+ if (settings_->getSetting(SettingConstants::SPELL_CHECKER)) {
+ std::string dictPath = settings_->getSetting(SettingConstants::DICT_PATH);
+ std::string dictFile = settings_->getSetting(SettingConstants::DICT_FILE);
+ checker_ = checkerFactory->createSpellChecker(dictPath + dictFile);
+ delete checkerFactory;
+ }
+ else {
+ checker_ = NULL;
+ }
}
+#endif
+void QtTextEdit::spellCheckerSettingsWindow() {
+ if (!spellCheckerWindow_) {
+ spellCheckerWindow_ = new QtSpellCheckerWindow(settings_);
+ settings_->onSettingChanged.connect(boost::bind(&QtTextEdit::handleSettingChanged, this, _1));
+ spellCheckerWindow_->show();
+ }
+ else {
+ spellCheckerWindow_->show();
+ spellCheckerWindow_->raise();
+ spellCheckerWindow_->activateWindow();
+ }
+}
+void QtTextEdit::handleSettingChanged(const std::string& settings) {
+ if (settings == SettingConstants::SPELL_CHECKER.getKey()
+ || settings == SettingConstants::DICT_PATH.getKey()
+ || settings == SettingConstants::DICT_FILE.getKey()) {
+#ifdef HAVE_SPELLCHECKER
+ setUpSpellChecker();
+ underlineMisspells();
+#endif
+ }
+}
+}
diff --git a/Swift/QtUI/QtTextEdit.h b/Swift/QtUI/QtTextEdit.h
index 075728b..a8df4d3 100644
--- a/Swift/QtUI/QtTextEdit.h
+++ b/Swift/QtUI/QtTextEdit.h
@@ -5,20 +5,46 @@
*/
#pragma once
+
+#include <SwifTools/SpellParser.h>
+
+#include <Swift/Controllers/Settings/SettingsProvider.h>
+#include <Swift/Controllers/SettingConstants.h>
+
#include <QTextEdit>
+#include <QPointer>
namespace Swift {
+ class SpellChecker;
+ class QtSpellCheckerWindow;
class QtTextEdit : public QTextEdit {
Q_OBJECT
public:
- QtTextEdit(QWidget* parent = 0);
+ QtTextEdit(SettingsProvider* settings, QWidget* parent = 0);
+ virtual ~QtTextEdit();
virtual QSize sizeHint() const;
signals:
+ void wordCorrected(QString& word);
void returnPressed();
void unhandledKeyPressEvent(QKeyEvent* event);
+ public slots:
+ void handleSettingChanged(const std::string& settings);
protected:
virtual void keyPressEvent(QKeyEvent* event);
+ virtual void contextMenuEvent(QContextMenuEvent* event);
private slots:
void handleTextChanged();
+ private:
+ SpellChecker *checker_;
+ std::vector<QAction*> replaceWordActions_;
+ PositionPairList misspelledPositions_;
+ SettingsProvider *settings_;
+ QPointer<QtSpellCheckerWindow> spellCheckerWindow_;
+ void addSuggestions(QMenu* menu, QContextMenuEvent* event);
+ void replaceMisspelledWord(const QString& word, int cursorPosition);
+ void setUpSpellChecker();
+ void underlineMisspells();
+ void spellCheckerSettingsWindow();
+ PositionPair getWordFromCursor(int cursorPosition);
};
}
diff --git a/Swift/QtUI/QtTranslator.h b/Swift/QtUI/QtTranslator.h
index fdafaf0..a2129f0 100644
--- a/Swift/QtUI/QtTranslator.h
+++ b/Swift/QtUI/QtTranslator.h
@@ -16,6 +16,10 @@ class QtTranslator : public Swift::Translator {
}
virtual std::string translate(const std::string& text, const std::string& context) {
+#if QT_VERSION >= 0x050000
+ return std::string(QCoreApplication::translate(context.c_str(), text.c_str(), 0).toUtf8());
+#else
return std::string(QCoreApplication::translate(context.c_str(), text.c_str(), 0, QCoreApplication::UnicodeUTF8).toUtf8());
+#endif
}
};
diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp
index a154fb0..0c7fbc2 100644
--- a/Swift/QtUI/QtUIFactory.cpp
+++ b/Swift/QtUI/QtUIFactory.cpp
@@ -4,36 +4,39 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "QtUIFactory.h"
+#include <Swift/QtUI/QtUIFactory.h>
#include <QSplitter>
-#include "QtXMLConsoleWidget.h"
-#include "QtChatTabs.h"
-#include "QtMainWindow.h"
-#include "QtLoginWindow.h"
-#include "QtSystemTray.h"
-#include "QtSettingsProvider.h"
-#include "QtMainWindow.h"
-#include "QtChatWindow.h"
-#include "QtJoinMUCWindow.h"
-#include "QtChatWindowFactory.h"
-#include "QtSwiftUtil.h"
-#include "MUCSearch/QtMUCSearchWindow.h"
-#include "UserSearch/QtUserSearchWindow.h"
-#include "QtProfileWindow.h"
-#include "QtContactEditWindow.h"
-#include "QtAdHocCommandWindow.h"
-#include "QtFileTransferListWidget.h"
-#include "Whiteboard/QtWhiteboardWindow.h"
+#include <Swift/QtUI/QtXMLConsoleWidget.h>
+#include <Swift/QtUI/QtChatTabs.h>
+#include <Swift/QtUI/QtMainWindow.h>
+#include <Swift/QtUI/QtLoginWindow.h>
+#include <Swift/QtUI/QtSystemTray.h>
+#include <Swift/QtUI/QtSettingsProvider.h>
+#include <Swift/QtUI/QtMainWindow.h>
+#include <Swift/QtUI/QtChatWindow.h>
+#include <Swift/QtUI/QtJoinMUCWindow.h>
+#include <Swift/QtUI/QtChatWindowFactory.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
+#include <Swift/QtUI/MUCSearch/QtMUCSearchWindow.h>
+#include <Swift/QtUI/UserSearch/QtUserSearchWindow.h>
+#include <Swift/QtUI/QtProfileWindow.h>
+#include <Swift/QtUI/QtContactEditWindow.h>
+#include <Swift/QtUI/QtAdHocCommandWindow.h>
+#include <Swift/QtUI/QtFileTransferListWidget.h>
+#include <Swift/QtUI/QtHighlightEditorWidget.h>
+#include <Swift/QtUI/Whiteboard/QtWhiteboardWindow.h>
#include <Swift/Controllers/Settings/SettingsProviderHierachy.h>
#include <Swift/QtUI/QtUISettingConstants.h>
-#include <QtHistoryWindow.h>
+#include <Swift/QtUI/QtHistoryWindow.h>
#include <Swiften/Whiteboard/WhiteboardSession.h>
+#include <Swift/QtUI/QtSingleWindow.h>
+#include <Swift/QtUI/QtBlockListEditorWindow.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 +81,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()));
@@ -143,7 +146,7 @@ void QtUIFactory::handleChatWindowFontResized(int size) {
UserSearchWindow* QtUIFactory::createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream, const std::set<std::string>& groups) {
return new QtUserSearchWindow(eventStream, type, groups);
-};
+}
JoinMUCWindow* QtUIFactory::createJoinMUCWindow(UIEventStream* uiEventStream) {
return new QtJoinMUCWindow(uiEventStream);
@@ -161,6 +164,14 @@ WhiteboardWindow* QtUIFactory::createWhiteboardWindow(boost::shared_ptr<Whiteboa
return new QtWhiteboardWindow(whiteboardSession);
}
+HighlightEditorWidget* QtUIFactory::createHighlightEditorWidget() {
+ return new QtHighlightEditorWidget();
+}
+
+BlockListEditorWidget *QtUIFactory::createBlockListEditorWidget() {
+ return new QtBlockListEditorWindow();
+}
+
void QtUIFactory::createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) {
new QtAdHocCommandWindow(command);
}
diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h
index 30f0101..662c78e 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,8 @@ namespace Swift {
virtual ContactEditWindow* createContactEditWindow();
virtual FileTransferListWidget* createFileTransferListWidget();
virtual WhiteboardWindow* createWhiteboardWindow(boost::shared_ptr<WhiteboardSession> whiteboardSession);
+ virtual HighlightEditorWidget* createHighlightEditorWidget();
+ virtual BlockListEditorWidget* createBlockListEditorWidget();
virtual void createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command);
private slots:
@@ -57,12 +61,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/QtUtilities.cpp b/Swift/QtUI/QtUtilities.cpp
index be9d179..e9aa4a4 100644
--- a/Swift/QtUI/QtUtilities.cpp
+++ b/Swift/QtUI/QtUtilities.cpp
@@ -1,13 +1,14 @@
/*
- * 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.
*/
#include "QtUtilities.h"
+#include <QTextDocument>
#include <QWidget>
-#ifdef Q_WS_X11
+#if defined (Q_OS_UNIX) && !defined(Q_OS_MAC)
#include <QX11Info>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -18,7 +19,7 @@
namespace QtUtilities {
void setX11Resource(QWidget* widget, const QString& c) {
-#ifdef Q_WS_X11
+#if defined (Q_OS_UNIX) && !defined(Q_OS_MAC)
char res_class[] = SWIFT_APPLICATION_NAME;
XClassHint hint;
hint.res_name = (QString(SWIFT_APPLICATION_NAME) + "-" + c).toUtf8().data();
@@ -30,4 +31,12 @@ void setX11Resource(QWidget* widget, const QString& c) {
#endif
}
+QString htmlEscape(const QString& s) {
+#if QT_VERSION >= 0x050000
+ return s.toHtmlEscaped();
+#else
+ return Qt::escape(s);
+#endif
+}
+
}
diff --git a/Swift/QtUI/QtUtilities.h b/Swift/QtUI/QtUtilities.h
index 6e64d6e..40c16bc 100644
--- a/Swift/QtUI/QtUtilities.h
+++ b/Swift/QtUI/QtUtilities.h
@@ -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.
*/
@@ -11,4 +11,5 @@ class QString;
namespace QtUtilities {
void setX11Resource(QWidget* widget, const QString& c);
-};
+ QString htmlEscape(const QString& s);
+}
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..d9bb4fe
--- /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->setVisible(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..b946fc4
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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>
+#include <Swift/QtUI/QtUtilities.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(QtUtilities::htmlEscape(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..ecb5533
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardJIDField.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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>
+#include <Swift/QtUI/QtUtilities.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(QtUtilities::htmlEscape(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..e399885
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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);
+
+#if QT_VERSION >= 0x050000
+ unitsTreeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch);
+#else
+ unitsTreeWidget->header()->setResizeMode(0, QHeaderView::Stretch);
+#endif
+
+ 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..35cc4ce
--- /dev/null
+++ b/Swift/QtUI/QtVCardWidget/QtVCardURLField.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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>
+#include <Swift/QtUI/QtUtilities.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(QtUtilities::htmlEscape(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/QtWin32NotifierWindow.h b/Swift/QtUI/QtWin32NotifierWindow.h
index b8d9c77..cd43cf2 100644
--- a/Swift/QtUI/QtWin32NotifierWindow.h
+++ b/Swift/QtUI/QtWin32NotifierWindow.h
@@ -23,7 +23,7 @@ namespace Swift {
}
virtual HWND getID() const {
- return winId();
+ return (HWND) winId();
}
};
}
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/DelegateCommons.cpp b/Swift/QtUI/Roster/DelegateCommons.cpp
index a575cb0..e7342f3 100644
--- a/Swift/QtUI/Roster/DelegateCommons.cpp
+++ b/Swift/QtUI/Roster/DelegateCommons.cpp
@@ -17,8 +17,8 @@ void DelegateCommons::drawElidedText(QPainter* painter, const QRect& region, con
painter->drawText(region, flags, adjustedText.simplified());
}
-void DelegateCommons::paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QColor& nameColor, const QString& avatarPath, const QIcon& presenceIcon, const QString& name, const QString& statusText, int unreadCount, bool compact) const {
- painter->save();
+void DelegateCommons::paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QColor& nameColor, const QString& avatarPath, const QIcon& presenceIcon, const QString& name, const QString& statusText, bool isIdle, int unreadCount, bool compact) const {
+ painter->save();
QRect fullRegion(option.rect);
if ( option.state & QStyle::State_Selected ) {
painter->fillRect(fullRegion, option.palette.highlight());
@@ -29,6 +29,7 @@ void DelegateCommons::paintContact(QPainter* painter, const QStyleOptionViewItem
QRect presenceIconRegion(QPoint(farLeftMargin, fullRegion.top()), QSize(presenceIconWidth, fullRegion.height() - verticalMargin));
+ QRect idleIconRegion(QPoint(farLeftMargin, fullRegion.top()), QSize(presenceIconWidth*2, fullRegion.height() - verticalMargin));
int calculatedAvatarSize = presenceIconRegion.height();
//This overlaps the presenceIcon, so must be painted first
QRect avatarRegion(QPoint(presenceIconRegion.right() - presenceIconWidth / 2, presenceIconRegion.top()), QSize(calculatedAvatarSize, calculatedAvatarSize));
@@ -51,6 +52,10 @@ void DelegateCommons::paintContact(QPainter* painter, const QStyleOptionViewItem
//Paint the presence icon over the top of the avatar
presenceIcon.paint(painter, presenceIconRegion, Qt::AlignBottom | Qt::AlignHCenter);
+ if (isIdle) {
+ idleIcon.paint(painter, idleIconRegion, Qt::AlignBottom | Qt::AlignHCenter);
+ }
+
QFontMetrics nameMetrics(nameFont);
painter->setFont(nameFont);
int extraFontWidth = nameMetrics.width("H");
diff --git a/Swift/QtUI/Roster/DelegateCommons.h b/Swift/QtUI/Roster/DelegateCommons.h
index 8732598..0684410 100644
--- a/Swift/QtUI/Roster/DelegateCommons.h
+++ b/Swift/QtUI/Roster/DelegateCommons.h
@@ -17,7 +17,7 @@
namespace Swift {
class DelegateCommons {
public:
- DelegateCommons() : nameFont(QApplication::font()), detailFont(QApplication::font()) {
+ DelegateCommons() : nameFont(QApplication::font()), detailFont(QApplication::font()), idleIcon(QIcon(":/icons/zzz.png")) {
detailFontSizeDrop = nameFont.pointSize() >= 10 ? 2 : 0;
detailFont.setStyle(QFont::StyleItalic);
detailFont.setPointSize(nameFont.pointSize() - detailFontSizeDrop);
@@ -26,7 +26,7 @@ namespace Swift {
static void drawElidedText(QPainter* painter, const QRect& region, const QString& text, int flags = Qt::AlignTop);
QSize contactSizeHint(const QStyleOptionViewItem& option, const QModelIndex& index, bool compact) const;
- void paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QColor& nameColor, const QString& avatarPath, const QIcon& presenceIcon, const QString& name, const QString& statusText, int unreadCount, bool compact) const;
+ void paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QColor& nameColor, const QString& avatarPath, const QIcon& presenceIcon, const QString& name, const QString& statusText, bool isIdle, int unreadCount, bool compact) const;
int detailFontSizeDrop;
QFont nameFont;
@@ -38,5 +38,6 @@ namespace Swift {
static const int presenceIconHeight;
static const int presenceIconWidth;
static const int unreadCountSize;
+ QIcon idleIcon;
};
}
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..6bf3989 100644
--- a/Swift/QtUI/Roster/QtRosterWidget.cpp
+++ b/Swift/QtUI/Roster/QtRosterWidget.cpp
@@ -4,23 +4,25 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#include "Roster/QtRosterWidget.h"
+#include <Swift/QtUI/Roster/QtRosterWidget.h>
#include <QContextMenuEvent>
#include <QMenu>
#include <QInputDialog>
#include <QFileDialog>
-#include "Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h"
-#include "Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h"
-#include "Swift/Controllers/UIEvents/RenameGroupUIEvent.h"
-#include "Swift/Controllers/UIEvents/SendFileUIEvent.h"
-#include "Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h"
-#include "QtContactEditWindow.h"
-#include "Swift/Controllers/Roster/ContactRosterItem.h"
-#include "Swift/Controllers/Roster/GroupRosterItem.h"
-#include "Swift/Controllers/UIEvents/UIEventStream.h"
-#include "QtSwiftUtil.h"
+#include <Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h>
+#include <Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h>
+#include <Swift/Controllers/UIEvents/RenameGroupUIEvent.h>
+#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h>
+#include <Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h>
+#include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h>
+#include <Swift/QtUI/QtContactEditWindow.h>
+#include <Swift/Controllers/Roster/ContactRosterItem.h>
+#include <Swift/Controllers/Roster/GroupRosterItem.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/QtUI/QtSwiftUtil.h>
namespace Swift {
@@ -57,6 +59,18 @@ 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"));
+
+ QAction* unblockContact = NULL;
+ if (contact->blockState() == ContactRosterItem::IsBlocked) {
+ unblockContact = contextMenu.addAction(tr("Unblock"));
+ }
+
+ QAction* blockContact = NULL;
+ if (contact->blockState() == ContactRosterItem::IsUnblocked) {
+ blockContact = contextMenu.addAction(tr("Block"));
+ }
+
#ifdef SWIFT_EXPERIMENTAL_FT
QAction* sendFile = NULL;
if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) {
@@ -69,6 +83,7 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) {
startWhiteboardChat = contextMenu.addAction(tr("Start Whiteboard Chat"));
}
#endif
+
QAction* result = contextMenu.exec(event->globalPos());
if (result == editContact) {
eventStream_->send(boost::make_shared<RequestContactEditorUIEvent>(contact->getJID()));
@@ -78,6 +93,15 @@ 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()));
+ }
+ else if (unblockContact && result == unblockContact) {
+ eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Unblocked, contact->getJID()));
+ }
+ else if (blockContact && result == blockContact) {
+ eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Blocked, contact->getJID()));
+ }
#ifdef SWIFT_EXPERIMENTAL_FT
else if (sendFile && result == sendFile) {
QString fileName = QFileDialog::getOpenFileName(this, tr("Send File"), "", tr("All Files (*);;"));
diff --git a/Swift/QtUI/Roster/QtTreeWidget.cpp b/Swift/QtUI/Roster/QtTreeWidget.cpp
index 5fdf138..64d0fcf 100644
--- a/Swift/QtUI/Roster/QtTreeWidget.cpp
+++ b/Swift/QtUI/Roster/QtTreeWidget.cpp
@@ -10,6 +10,7 @@
#include <boost/bind.hpp>
#include <QUrl>
+#include <QMimeData>
#include <Swiften/Base/Platform.h>
#include <Swift/Controllers/Roster/ContactRosterItem.h>
diff --git a/Swift/QtUI/Roster/RosterDelegate.cpp b/Swift/QtUI/Roster/RosterDelegate.cpp
index 7e6428b..aef588c 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 {
@@ -73,9 +74,10 @@ void RosterDelegate::paintContact(QPainter* painter, const QStyleOptionViewItem&
QIcon presenceIcon = index.data(PresenceIconRole).isValid() && !index.data(PresenceIconRole).value<QIcon>().isNull()
? index.data(PresenceIconRole).value<QIcon>()
: QIcon(":/icons/offline.png");
+ bool isIdle = index.data(IdleRole).isValid() ? index.data(IdleRole).toBool() : false;
QString name = index.data(Qt::DisplayRole).toString();
QString statusText = index.data(StatusTextRole).toString();
- common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, 0, compact_);
+ common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, isIdle, 0, compact_);
}
}
diff --git a/Swift/QtUI/Roster/RosterModel.cpp b/Swift/QtUI/Roster/RosterModel.cpp
index 1fc20dd..1b93047 100644
--- a/Swift/QtUI/Roster/RosterModel.cpp
+++ b/Swift/QtUI/Roster/RosterModel.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2013 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -16,6 +16,7 @@
#include "Swift/Controllers/Roster/ContactRosterItem.h"
#include "Swift/Controllers/Roster/GroupRosterItem.h"
#include <Swift/Controllers/StatusUtil.h>
+#include <Swiften/Base/Path.h>
#include "QtSwiftUtil.h"
#include "Swift/QtUI/Roster/QtTreeWidget.h"
@@ -40,7 +41,8 @@ void RosterModel::setRoster(Roster* roster) {
void RosterModel::reLayout() {
//emit layoutChanged();
- reset();
+ beginResetModel();
+ endResetModel(); // TODO: Not sure if this isn't too early?
if (!roster_) {
return;
}
@@ -53,7 +55,7 @@ void RosterModel::reLayout() {
void RosterModel::handleChildrenChanged(GroupRosterItem* /*group*/) {
reLayout();
-}
+}
void RosterModel::handleDataChanged(RosterItem* item) {
Q_ASSERT(item);
@@ -84,7 +86,8 @@ QVariant RosterModel::data(const QModelIndex& index, int role) const {
case AvatarRole: return getAvatar(item);
case PresenceIconRole: return getPresenceIcon(item);
case ChildCountRole: return getChildCount(item);
- default: return QVariant();
+ case IdleRole: return getIsIdle(item);
+ default: return QVariant();
}
}
@@ -93,6 +96,11 @@ int RosterModel::getChildCount(RosterItem* item) const {
return group ? group->getDisplayedChildren().size() : 0;
}
+bool RosterModel::getIsIdle(RosterItem* item) const {
+ ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item);
+ return contact ? !contact->getIdleText().empty() : false;
+}
+
QColor RosterModel::intToColor(int color) const {
return QColor(
((color & 0xFF0000)>>16),
@@ -131,6 +139,9 @@ QString RosterModel::getToolTip(RosterItem* item) const {
if (!getStatusText(item).isEmpty()) {
tip += ": " + getStatusText(item);
}
+ if (!contact->getIdleText().empty()) {
+ tip += "\n " + tr("Idle since ") + P2QSTRING(contact->getIdleText());
+ }
}
return tip;
}
@@ -140,7 +151,7 @@ QString RosterModel::getAvatar(RosterItem* item) const {
if (!contact) {
return "";
}
- return QString(contact->getAvatarPath().c_str());
+ return P2QSTRING(pathToString(contact->getAvatarPath()));
}
QString RosterModel::getStatusText(RosterItem* item) const {
@@ -152,14 +163,18 @@ QString RosterModel::getStatusText(RosterItem* item) const {
QIcon RosterModel::getPresenceIcon(RosterItem* item) const {
ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item);
if (!contact) return QIcon();
+ if (contact->blockState() == ContactRosterItem::IsBlocked) {
+ return QIcon(":/icons/stop.png");
+ }
+
QString iconString;
switch (contact->getStatusShow()) {
- case StatusShow::Online: iconString = "online";break;
- case StatusShow::Away: iconString = "away";break;
- case StatusShow::XA: iconString = "away";break;
- case StatusShow::FFC: iconString = "online";break;
- case StatusShow::DND: iconString = "dnd";break;
- case StatusShow::None: iconString = "offline";break;
+ case StatusShow::Online: iconString = "online";break;
+ case StatusShow::Away: iconString = "away";break;
+ case StatusShow::XA: iconString = "away";break;
+ case StatusShow::FFC: iconString = "online";break;
+ case StatusShow::DND: iconString = "dnd";break;
+ case StatusShow::None: iconString = "offline";break;
}
return QIcon(":/icons/" + iconString + ".png");
}
diff --git a/Swift/QtUI/Roster/RosterModel.h b/Swift/QtUI/Roster/RosterModel.h
index bd34e9c..23d54f8 100644
--- a/Swift/QtUI/Roster/RosterModel.h
+++ b/Swift/QtUI/Roster/RosterModel.h
@@ -18,6 +18,7 @@ namespace Swift {
PresenceIconRole = Qt::UserRole + 2,
StatusShowTypeRole = Qt::UserRole + 3,
ChildCountRole = Qt::UserRole + 4,
+ IdleRole = Qt::UserRole + 5
};
class QtTreeWidget;
@@ -48,6 +49,7 @@ namespace Swift {
QString getStatusText(RosterItem* item) const;
QIcon getPresenceIcon(RosterItem* item) const;
int getChildCount(RosterItem* item) const;
+ bool getIsIdle(RosterItem* item) const;
void reLayout();
Roster* roster_;
QtTreeWidget* view_;
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 64c3b15..1c3fd70 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", "-Wno-undefined-reinterpret-cast"])
+
myenv.UseFlags(env["SWIFT_CONTROLLERS_FLAGS"])
myenv.UseFlags(env["SWIFTOOLS_FLAGS"])
if myenv["HAVE_XSS"] :
@@ -31,7 +36,8 @@ if myenv["HAVE_SPARKLE"] :
myenv.UseFlags(env["SPARKLE_FLAGS"])
myenv.UseFlags(env["SWIFTEN_FLAGS"])
myenv.UseFlags(env["SWIFTEN_DEP_FLAGS"])
-myenv.UseFlags(env["BREAKPAD_FLAGS"])
+if myenv.get("HAVE_BREAKPAD") :
+ myenv.UseFlags(env["BREAKPAD_FLAGS"])
if myenv.get("HAVE_GROWL", False) :
myenv.UseFlags(myenv["GROWL_FLAGS"])
myenv.Append(CPPDEFINES = ["HAVE_GROWL"])
@@ -40,6 +46,9 @@ if myenv["swift_mobile"] :
if myenv.get("HAVE_SNARL", False) :
myenv.UseFlags(myenv["SNARL_FLAGS"])
myenv.Append(CPPDEFINES = ["HAVE_SNARL"])
+if myenv.get("HAVE_HUNSPELL", True):
+ myenv.Append(CPPDEFINES = ["HAVE_HUNSPELL"])
+ myenv.UseFlags(myenv["HUNSPELL_FLAGS"])
if env["PLATFORM"] == "win32" :
myenv.Append(LIBS = ["cryptui"])
myenv.UseFlags(myenv["PLATFORM_FLAGS"])
@@ -48,18 +57,23 @@ myenv.Tool("qt4", toolpath = ["#/BuildTools/SCons/Tools"])
myenv.Tool("nsis", toolpath = ["#/BuildTools/SCons/Tools"])
myenv.Tool("wix", toolpath = ["#/BuildTools/SCons/Tools"])
myenv.Tool("textfile", toolpath = ["#/BuildTools/SCons/Tools"])
-qt4modules = ['QtCore', 'QtGui', 'QtWebKit']
+qt4modules = ['QtCore', 'QtWebKit', 'QtGui']
+if myenv["qt5"] :
+ qt_version = '5'
+ qt4modules += ['QtWidgets', 'QtWebKitWidgets', 'QtMultimedia']
+else :
+ qt_version = '4'
if env["PLATFORM"] == "posix" :
qt4modules += ["QtDBus"]
if env["PLATFORM"] != "win32" and env["PLATFORM"] != "darwin" :
qt4modules += ["QtNetwork"]
-myenv.EnableQt4Modules(qt4modules, debug = False)
+myenv.EnableQt4Modules(qt4modules, debug = False, version = qt_version)
myenv.Append(CPPPATH = ["."])
if env["PLATFORM"] == "win32" :
- #myenv["LINKFLAGS"] = ["/SUBSYSTEM:CONSOLE"]
+ #myenv.Append(LINKFLAGS = ["/SUBSYSTEM:CONSOLE"])
myenv.Append(LINKFLAGS = ["/SUBSYSTEM:WINDOWS"])
myenv.Append(LIBS = "qtmain")
if myenv.get("HAVE_SCHANNEL", 0) :
@@ -73,6 +87,7 @@ myenv.WriteVal("DefaultTheme.qrc", myenv.Value(generateDefaultTheme(myenv.Dir("#
sources = [
"main.cpp",
"QtAboutWidget.cpp",
+ "QtSpellCheckerWindow.cpp",
"QtAvatarWidget.cpp",
"QtUIFactory.cpp",
"QtChatWindowFactory.cpp",
@@ -81,6 +96,7 @@ sources = [
"QtLoginWindow.cpp",
"QtMainWindow.cpp",
"QtProfileWindow.cpp",
+ "QtBlockListEditorWindow.cpp",
"QtNameWidget.cpp",
"QtSettingsProvider.cpp",
"QtStatusWidget.cpp",
@@ -107,6 +123,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 +186,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 +274,10 @@ 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.Uic4("QtBlockListEditorWindow.ui")
+myenv.Uic4("QtSpellCheckerWindow.ui")
myenv.Qrc("DefaultTheme.qrc")
myenv.Qrc("Swift.qrc")
@@ -306,7 +359,7 @@ if env.get("SWIFT_INSTALLDIR", "") :
env.Install(os.path.join(env["SWIFT_INSTALLDIR"], "share", "swift", dir), resource)
if env["PLATFORM"] == "win32" :
- if env["DIST"] :
+ if env["DIST"] or ARGUMENTS.get("dump_trace") :
commonResources[""] = commonResources.get("", []) + [
#os.path.join(env["OPENSSL_DIR"], "bin", "ssleay32.dll"),
#os.path.join(env["OPENSSL_DIR"], "bin", "libeay32.dll"),
@@ -314,13 +367,25 @@ if env["PLATFORM"] == "win32" :
]
if env["SWIFTEN_DLL"] :
commonResources[""] = commonResources.get("", []) + ["#/Swiften/${SWIFTEN_LIBRARY_FILE}"]
- qtimageformats = ["gif", "ico", "jpeg", "mng", "svg", "tiff"]
- qtlibs = ["QtCore4", "QtGui4", "QtNetwork4", "QtWebKit4", "QtXMLPatterns4", "phonon4"]
+ qtplugins = {}
+ qtplugins["imageformats"] = ["gif", "ico", "jpeg", "mng", "svg", "tiff"]
+ qtlibs = ["QtCore", "QtGui", "QtNetwork", "QtWebKit", "QtXMLPatterns"]
+ if qt_version == '4' :
+ qtlibs.append("phonon")
+ qtlibs = [lib + '4' for lib in qtlibs]
+ else :
+ qtlibs += ['QtQuick', 'QtQml', 'QtV8', 'QtMultimedia', 'QtSql', 'QtSensors', 'QtWidgets', 'QtWebKitWidgets', 'QtMultimediaWidgets', 'QtOpenGL', 'QtPrintSupport']
+ qtlibs = [lib.replace('Qt', 'Qt5') for lib in qtlibs]
+ qtlibs += ['icuin51', 'icuuc51', 'icudt51', 'libGLESv2', 'libEGL']
+ qtplugins["platforms"] = ['windows']
+ qtplugins["accessible"] = ["taccessiblewidgets"]
windowsBundleFiles = myenv.WindowsBundle("Swift",
resources = commonResources,
- qtimageformats = qtimageformats,
- qtlibs = qtlibs)
+ qtplugins = qtplugins,
+ qtlibs = qtlibs,
+ qtversion = qt_version)
+ if env["DIST"] :
#myenv.Append(NSIS_OPTIONS = [
# "/DmsvccRedistributableDir=\"" + env["vcredist"] + "\"",
# "/DbuildVersion=" + myenv["SWIFT_VERSION"]
@@ -344,7 +409,7 @@ if env["PLATFORM"] == "win32" :
outfile.write('}')
outfile.close()
infile.close()
- env.Command(["Swift/COPYING.rtf"], ["COPYING"], convertToRTF)
+ copying = env.Command(["Swift/COPYING.rtf"], ["COPYING"], convertToRTF)
wixvariables = {
'VCCRTFile': env["vcredist"],
@@ -355,7 +420,7 @@ if env["PLATFORM"] == "win32" :
wixincludecontent += "<?define %s = \"%s\" ?>" % (key, wixvariables[key])
wixincludecontent += "</Include>"
myenv.WriteVal("..\\Packaging\\Wix\\variables.wxs", env.Value(wixincludecontent))
- myenv.WiX_Heat('..\\Packaging\\WiX\\gen_files.wxs', windowsBundleFiles)
+ myenv.WiX_Heat('..\\Packaging\\WiX\\gen_files.wxs', windowsBundleFiles + copying)
myenv.WiX_Candle('..\\Packaging\\WiX\\Swift.wixobj', '..\\Packaging\\WiX\\Swift.wxs')
myenv.WiX_Candle('..\\Packaging\\WiX\\gen_files.wixobj', '..\\Packaging\\WiX\\gen_files.wxs')
myenv.WiX_Light('#/Packages/Swift/Swift-' + myenv["SWIFT_VERSION"] + '.msi', ['..\\Packaging\\WiX\\gen_files.wixobj','..\\Packaging\\WiX\\Swift.wixobj'])
diff --git a/Swift/QtUI/Swift.qrc b/Swift/QtUI/Swift.qrc
index eb4f7ee..eeef80d 100644
--- a/Swift/QtUI/Swift.qrc
+++ b/Swift/QtUI/Swift.qrc
@@ -29,5 +29,18 @@
<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>
+ <file alias="icons/zzz.png">../resources/icons/zzz.png</file>
+ <file alias="icons/stop.png">../resources/icons/stop.png</file>
</qresource>
</RCC>
diff --git a/Swift/QtUI/SystemMessageSnippet.cpp b/Swift/QtUI/SystemMessageSnippet.cpp
index c78fe36..39349bc 100644
--- a/Swift/QtUI/SystemMessageSnippet.cpp
+++ b/Swift/QtUI/SystemMessageSnippet.cpp
@@ -10,12 +10,13 @@
namespace Swift {
-SystemMessageSnippet::SystemMessageSnippet(const QString& message, const QDateTime& time, bool appendToPrevious, QtChatTheme* theme) : ChatSnippet(appendToPrevious) {
+SystemMessageSnippet::SystemMessageSnippet(const QString& message, const QDateTime& time, bool appendToPrevious, QtChatTheme* theme, Direction direction) : ChatSnippet(appendToPrevious) {
if (appendToPrevious) {
- setContinuationFallbackSnippet(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(message, time, false, theme)));
+ setContinuationFallbackSnippet(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(message, time, false, theme, direction)));
}
content_ = theme->getStatus();
+ content_.replace("%direction%", directionToCSS(direction));
content_.replace("%message%", wrapResizable("<span class='swift_message'>" + escape(message) + "</span>"));
content_.replace("%shortTime%", wrapResizable(escape(time.toString("h:mm"))));
content_.replace("%time%", wrapResizable("<span class='swift_time'>" + timeToEscapedString(time) + "</span>"));
diff --git a/Swift/QtUI/SystemMessageSnippet.h b/Swift/QtUI/SystemMessageSnippet.h
index 69d231f..34b0d40 100644
--- a/Swift/QtUI/SystemMessageSnippet.h
+++ b/Swift/QtUI/SystemMessageSnippet.h
@@ -15,7 +15,7 @@ class QDateTime;
namespace Swift {
class SystemMessageSnippet : public ChatSnippet {
public:
- SystemMessageSnippet(const QString& message, const QDateTime& time, bool appendToPrevious, QtChatTheme* theme);
+ SystemMessageSnippet(const QString& message, const QDateTime& time, bool appendToPrevious, QtChatTheme* theme, Direction direction);
virtual ~SystemMessageSnippet();
const QString& getContent() const {return content_;}
diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
index d69c626..02b238e 100644
--- a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
+++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp
@@ -29,7 +29,7 @@ namespace Swift {
QtUserSearchWindow::QtUserSearchWindow(UIEventStream* eventStream, UserSearchWindow::Type type, const std::set<std::string>& groups) : eventStream_(eventStream), type_(type), model_(NULL) {
setupUi(this);
-#ifndef Q_WS_MAC
+#ifndef Q_OS_MAC
setWindowIcon(QIcon(":/logo-icon-16.png"));
#endif
QString title(type == UserSearchWindow::AddContact ? tr("Add Contact") : tr("Chat to User"));
@@ -264,7 +264,11 @@ void QtUserSearchWindow::setResultsForm(Form::ref results) {
resultsPage_->results_->setModel(newModel);
resultsPage_->results_->setItemDelegate(new QItemDelegate());
resultsPage_->results_->setHeaderHidden(false);
+#if QT_VERSION >= 0x050000
+ resultsPage_->results_->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
+#else
resultsPage_->results_->header()->setResizeMode(QHeaderView::ResizeToContents);
+#endif
delete model_;
model_ = newModel;
resultsPage_->setNoResults(model_->rowCount() == 0);
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..89de95e 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>
@@ -25,7 +26,7 @@
namespace Swift {
QtWhiteboardWindow::QtWhiteboardWindow(WhiteboardSession::ref whiteboardSession) : QWidget() {
-#ifndef Q_WS_MAC
+#ifndef Q_OS_MAC
setWindowIcon(QIcon(":/logo-icon-16.png"));
#endif
layout = new QVBoxLayout(this);
@@ -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/QtUI/WinUIHelpers.cpp b/Swift/QtUI/WinUIHelpers.cpp
index 3942ac1..161ff1d 100644
--- a/Swift/QtUI/WinUIHelpers.cpp
+++ b/Swift/QtUI/WinUIHelpers.cpp
@@ -47,7 +47,7 @@ void WinUIHelpers::displayCertificateChainAsSheet(QWidget* parent, const std::ve
CRYPTUI_VIEWCERTIFICATE_STRUCT viewDialogProperties = { 0 };
viewDialogProperties.dwSize = sizeof(viewDialogProperties);
- viewDialogProperties.hwndParent = parent->winId();
+ viewDialogProperties.hwndParent = (HWND) parent->winId();
viewDialogProperties.dwFlags = CRYPTUI_DISABLE_EDITPROPERTIES | CRYPTUI_DISABLE_ADDTOSTORE | CRYPTUI_ENABLE_REVOCATION_CHECKING;
viewDialogProperties.pCertContext = certificate_chain.get();
viewDialogProperties.cStores = 1;
diff --git a/Swift/QtUI/main.cpp b/Swift/QtUI/main.cpp
index d02cce6..d734713 100644
--- a/Swift/QtUI/main.cpp
+++ b/Swift/QtUI/main.cpp
@@ -21,6 +21,7 @@
#include <SwifTools/Application/PlatformApplicationPathProvider.h>
#include <SwifTools/CrashReporter.h>
#include <stdlib.h>
+#include <Swiften/Base/Path.h>
#include "QtSwift.h"
#include "QtTranslator.h"
@@ -33,7 +34,9 @@ int main(int argc, char* argv[]) {
Swift::CrashReporter crashReporter(applicationPathProvider.getDataDir() / "crashes");
+#if QT_VERSION < 0x050000
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
+#endif
// Parse program options
boost::program_options::options_description desc = Swift::QtSwift::getOptionsDescription();
@@ -62,21 +65,23 @@ int main(int argc, char* argv[]) {
}
// Translation
+#if QT_VERSION < 0x050000
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
+#endif
boost::filesystem::path someTranslationPath = applicationPathProvider.getResourcePath("/translations/swift_en.qm");
QTranslator qtTranslator;
if (!someTranslationPath.empty()) {
#if QT_VERSION >= 0x040800
if (vm.count("language") > 0) {
- qtTranslator.load(QString(SWIFT_APPLICATION_NAME).toLower() + "_" + P2QSTRING(vm["language"].as<std::string>()), someTranslationPath.parent_path().string().c_str());
+ qtTranslator.load(QString(SWIFT_APPLICATION_NAME).toLower() + "_" + P2QSTRING(vm["language"].as<std::string>()), P2QSTRING(Swift::pathToString(someTranslationPath.parent_path())));
}
else {
- qtTranslator.load(QLocale::system(), QString(SWIFT_APPLICATION_NAME).toLower(), "_", someTranslationPath.parent_path().string().c_str());
+ qtTranslator.load(QLocale::system(), QString(SWIFT_APPLICATION_NAME).toLower(), "_", P2QSTRING(Swift::pathToString(someTranslationPath)));
}
#else
//std::cout << "Loading " << std::string(QLocale::system().name().toUtf8()) << std::endl;
- qtTranslator.load(QString(SWIFT_APPLICATION_NAME).toLower() + "_" + QLocale::system().name(), someTranslationPath.parent_path().string().c_str());
+ qtTranslator.load(QString(SWIFT_APPLICATION_NAME).toLower() + "_" + QLocale::system().name(), P2QSTRING(Swift::pathToString(someTranslationPath)));
#endif
}
app.installTranslator(&qtTranslator);
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.png
new file mode 100644
index 0000000..817bd50
--- /dev/null
+++ b/Swift/resources/emoticons/evilgrin.png
Binary files differ
diff --git a/Swift/resources/emoticons/grin.png b/Swift/resources/emoticons/grin.png
new file mode 100644
index 0000000..fc60c5e
--- /dev/null
+++ b/Swift/resources/emoticons/grin.png
Binary files differ
diff --git a/Swift/resources/emoticons/happy.png b/Swift/resources/emoticons/happy.png
new file mode 100644
index 0000000..6b7336e
--- /dev/null
+++ b/Swift/resources/emoticons/happy.png
Binary files differ
diff --git a/Swift/resources/emoticons/smile.png b/Swift/resources/emoticons/smile.png
new file mode 100644
index 0000000..ade4318
--- /dev/null
+++ b/Swift/resources/emoticons/smile.png
Binary files differ
diff --git a/Swift/resources/emoticons/surprised.png b/Swift/resources/emoticons/surprised.png
new file mode 100644
index 0000000..4520cfc
--- /dev/null
+++ b/Swift/resources/emoticons/surprised.png
Binary files differ
diff --git a/Swift/resources/emoticons/tongue.png b/Swift/resources/emoticons/tongue.png
new file mode 100644
index 0000000..ecafd2f
--- /dev/null
+++ b/Swift/resources/emoticons/tongue.png
Binary files differ
diff --git a/Swift/resources/emoticons/unhappy.png b/Swift/resources/emoticons/unhappy.png
new file mode 100644
index 0000000..fd5d030
--- /dev/null
+++ b/Swift/resources/emoticons/unhappy.png
Binary files differ
diff --git a/Swift/resources/emoticons/wink.png b/Swift/resources/emoticons/wink.png
new file mode 100644
index 0000000..a631949
--- /dev/null
+++ b/Swift/resources/emoticons/wink.png
Binary files differ
diff --git a/Swift/resources/icons/star-checked2.png b/Swift/resources/icons/star-checked2.png
new file mode 100644
index 0000000..2908534
--- /dev/null
+++ b/Swift/resources/icons/star-checked2.png
Binary files differ
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.png
new file mode 100644
index 0000000..ee85d69
--- /dev/null
+++ b/Swift/resources/icons/star-unchecked2.png
Binary files differ
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>
diff --git a/Swift/resources/icons/stop.png b/Swift/resources/icons/stop.png
new file mode 100644
index 0000000..1574342
--- /dev/null
+++ b/Swift/resources/icons/stop.png
Binary files differ
diff --git a/Swift/resources/icons/stop.svg b/Swift/resources/icons/stop.svg
new file mode 100644
index 0000000..b6171ef
--- /dev/null
+++ b/Swift/resources/icons/stop.svg
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="601"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.2 r9819"
+ height="601"
+ sodipodi:docname="Blank_stop_sign_octagon.svg"
+ inkscape:export-filename="/Users/tobias/Downloads/Blank_stop_sign_octagon.png"
+ inkscape:export-xdpi="3.29"
+ inkscape:export-ydpi="3.29">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1440"
+ inkscape:window-height="852"
+ id="namedview8"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="0.63429569"
+ inkscape:cx="102.64275"
+ inkscape:cy="390.12926"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <path
+ d="m 0.5,424.5 v -248 l 176,-176 h 248 l 176,176 v 248 l -176,176 h -248 z"
+ id="path4"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;stroke:#000000" />
+ <path
+ d="M 17,417 V 182 L 183,16 H 418 L 584,182 V 417 L 418,583 H 183 z"
+ id="path6"
+ inkscape:connector-curvature="0"
+ style="fill:#ff0000" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect2991"
+ width="368.91312"
+ height="96.169655"
+ x="116.04344"
+ y="249.42896" />
+</svg>
diff --git a/Swift/resources/icons/stop.txt b/Swift/resources/icons/stop.txt
new file mode 100644
index 0000000..9d4a1b1
--- /dev/null
+++ b/Swift/resources/icons/stop.txt
@@ -0,0 +1,2 @@
+License: Public Domain
+Source: https://commons.wikimedia.org/wiki/File:Blank_stop_sign_octagon.svg
diff --git a/Swift/resources/icons/zzz.png b/Swift/resources/icons/zzz.png
new file mode 100644
index 0000000..706c2f4
--- /dev/null
+++ b/Swift/resources/icons/zzz.png
Binary files differ
diff --git a/Swift/resources/icons/zzz.svg b/Swift/resources/icons/zzz.svg
new file mode 100644
index 0000000..adbd0be
--- /dev/null
+++ b/Swift/resources/icons/zzz.svg
@@ -0,0 +1,166 @@
+<?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:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1024"
+ height="1024"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.48.2 r9819"
+ version="1.0"
+ inkscape:export-filename="/Users/tobias/dev/rep/swift-lastseen/Swift/resources/icons/zzz.png"
+ inkscape:export-xdpi="2.8125"
+ inkscape:export-ydpi="2.8125"
+ sodipodi:docname="Swift_resources_icons_zzz_Before_b244d1f210fa994df40297664f111de31b6bf676.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient3155">
+ <stop
+ style="stop-color:#007600;stop-opacity:1;"
+ offset="0"
+ id="stop3157" />
+ <stop
+ style="stop-color:#97f597;stop-opacity:1;"
+ offset="1"
+ id="stop3159" />
+ </linearGradient>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3155"
+ id="linearGradient3185"
+ gradientUnits="userSpaceOnUse"
+ x1="146.36209"
+ y1="457.4635"
+ x2="376.76218"
+ y2="76.855392"
+ gradientTransform="matrix(0.5616546,0,0,0.5616546,113.6799,171.43482)" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.45254834"
+ inkscape:cx="461.16373"
+ inkscape:cy="517.26047"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1440"
+ inkscape:window-height="852"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:snap-global="false"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2988"
+ 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></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,512)">
+ <path
+ style="fill:url(#linearGradient3185);fill-opacity:1;stroke:#004900;stroke-width:40;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;opacity:0"
+ d="m 400.8403,315.01501 c 0,79.25627 -64.77302,144.32061 -143.37683,143.5802 -78.9121,-0.74331 -143.37683,-64.32393 -143.37683,-143.5802 0,-80.79036 64.23282,-234.091516 143.37683,-234.091516 79.14401,0 143.37683,154.835246 143.37683,234.091516 z"
+ id="path2383"
+ sodipodi:nodetypes="csssc"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-size:145.84281920999998761px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:#ffffff;font-family:Sans;stroke-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"
+ x="278.41916"
+ y="192.9375"
+ id="text2990"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan2992"
+ x="278.41916"
+ y="192.9375"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;letter-spacing:-60px;writing-mode:lr-tb;fill:#000000;stroke:#ffffff;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:Sans;-inkscape-font-specification:Helvetica Bold"><tspan
+ style="font-size:290px;font-weight:bold;letter-spacing:-60px;writing-mode:lr-tb;fill:#000000;stroke:#ffffff;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;-inkscape-font-specification:Helvetica Bold;font-family:Sans;font-style:normal;font-stretch:normal;font-variant:normal"
+ id="tspan2998"
+ dy="0"
+ dx="0">Z</tspan><tspan
+ style="font-size:350px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;letter-spacing:-60px;writing-mode:lr-tb;fill:#000000;stroke:#ffffff;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:Sans;-inkscape-font-specification:Helvetica Bold"
+ id="tspan2994"
+ dy="-60">Z</tspan><tspan
+ style="font-size:450px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;letter-spacing:-60px;writing-mode:lr-tb;fill:#000000;stroke:#ffffff;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:Sans;-inkscape-font-specification:Helvetica Bold"
+ id="tspan2996"
+ dy="-80">Z</tspan></tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="470.72134"
+ y="-22.449778"
+ id="text3000"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3002"
+ x="470.72134"
+ y="-22.449778" /></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="1020.8854"
+ y="840.59418"
+ id="text3791"
+ sodipodi:linespacing="125%"
+ transform="translate(0,-512)"><tspan
+ sodipodi:role="line"
+ id="tspan3793"
+ x="1020.8854"
+ y="840.59418" /></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="279.52814"
+ y="289.27185"
+ id="text3766"
+ sodipodi:linespacing="125%"
+ transform="translate(0,-512)"><tspan
+ sodipodi:role="line"
+ id="tspan3768"
+ x="279.52814"
+ y="289.27185" /></text>
+ </g>
+</svg>
diff --git a/Swift/resources/themes/Default/Incoming/Content.html b/Swift/resources/themes/Default/Incoming/Content.html
index eb5bdea..fb4795f 100755
--- a/Swift/resources/themes/Default/Incoming/Content.html
+++ b/Swift/resources/themes/Default/Incoming/Content.html
@@ -11,7 +11,7 @@
<td class="tr"></td>
</tr>
<tr>
- <td class="message">
+ <td class="message" style="direction: %direction%">
%message%
<div class="timeStamp"><span class="name">%wrapped_sender% @</span> %time%</div>
<span id="insert"></span>
diff --git a/Swift/resources/themes/Default/Incoming/Context.html b/Swift/resources/themes/Default/Incoming/Context.html
index b1aca27..c6fa61c 100755
--- a/Swift/resources/themes/Default/Incoming/Context.html
+++ b/Swift/resources/themes/Default/Incoming/Context.html
@@ -11,7 +11,7 @@
<td class="tr"></td>
</tr>
<tr>
- <td class="message">
+ <td class="message" style="direction: %direction%">
%message%
<div class="timeStamp"><span class="name">%sender% @</span> %time%</div>
<span id="insert"></span>
diff --git a/Swift/resources/themes/Default/Incoming/NextContent.html b/Swift/resources/themes/Default/Incoming/NextContent.html
index 4aec8ab..aff669b 100755
--- a/Swift/resources/themes/Default/Incoming/NextContent.html
+++ b/Swift/resources/themes/Default/Incoming/NextContent.html
@@ -1,6 +1,6 @@
<div>
<div class="followUp"></div>
- <div>
+ <div style="direction: %direction%">
%message%
<div class="timeStamp">%time%</div>
diff --git a/Swift/resources/themes/Default/Incoming/NextContext.html b/Swift/resources/themes/Default/Incoming/NextContext.html
index 18b8dc4..c0a8846 100755
--- a/Swift/resources/themes/Default/Incoming/NextContext.html
+++ b/Swift/resources/themes/Default/Incoming/NextContext.html
@@ -1,5 +1,5 @@
<div class="followUp"></div>
- <div>
+ <div style="direction: %direction%">
%message%
<div class="timeStamp">%time%</div>
diff --git a/Swift/resources/themes/Default/Outgoing/Content.html b/Swift/resources/themes/Default/Outgoing/Content.html
index f855f56..87ca272 100755
--- a/Swift/resources/themes/Default/Outgoing/Content.html
+++ b/Swift/resources/themes/Default/Outgoing/Content.html
@@ -11,7 +11,7 @@
<td class="tr"></td>
</tr>
<tr>
- <td class="message">
+ <td class="message" style="direction: %direction%">
%message%
<div class="timeStamp"><span class="name">%wrapped_sender% @</span> %time%</div>
<span id="insert"></span>
diff --git a/Swift/resources/themes/Default/Outgoing/Context.html b/Swift/resources/themes/Default/Outgoing/Context.html
index 7822cac..229c592 100755
--- a/Swift/resources/themes/Default/Outgoing/Context.html
+++ b/Swift/resources/themes/Default/Outgoing/Context.html
@@ -11,7 +11,7 @@
<td class="tr"></td>
</tr>
<tr>
- <td class="message">
+ <td class="message" style="direction: %direction%">
%message%
<div class="timeStamp"><span class="name">%sender% @</span> %time%</div>
diff --git a/Swift/resources/themes/Default/Outgoing/NextContent.html b/Swift/resources/themes/Default/Outgoing/NextContent.html
index 4367197..fe6490e 100755
--- a/Swift/resources/themes/Default/Outgoing/NextContent.html
+++ b/Swift/resources/themes/Default/Outgoing/NextContent.html
@@ -1,6 +1,6 @@
<div>
<div class="followUp"></div>
- <div>
+ <div style="direction: %direction%">
%message%
<div class="timeStamp">%time%</div>
</div>
diff --git a/Swift/resources/themes/Default/Outgoing/NextContext.html b/Swift/resources/themes/Default/Outgoing/NextContext.html
index 1f84771..f66aeeb 100755
--- a/Swift/resources/themes/Default/Outgoing/NextContext.html
+++ b/Swift/resources/themes/Default/Outgoing/NextContext.html
@@ -1,5 +1,5 @@
<div class="followUp"></div>
-<div>
+<div style="direction: %direction%">
%message%
<div class="timeStamp">%time%</div>
</div><span id="insert"></span>
diff --git a/Swift/resources/themes/Default/Status.html b/Swift/resources/themes/Default/Status.html
index b8168e8..e7add89 100755
--- a/Swift/resources/themes/Default/Status.html
+++ b/Swift/resources/themes/Default/Status.html
@@ -9,7 +9,7 @@
<td class="tr"></td>
</tr>
<tr>
- <td class="message">
+ <td class="message" style="direction: %direction%">
%message%
<div class="timeStamp">%time%</div>
</td>