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