summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Smith <git@kismith.co.uk>2011-10-07 14:09:27 (GMT)
committerKevin Smith <git@kismith.co.uk>2011-10-07 14:11:23 (GMT)
commitb2f58c4f3eb93e3a32062670df5eb6682aed273a (patch)
tree737884f5e3e826407466290cf7c324c4f0069dd0
parent7f7b05d8d242a9b73b7d9f971779c6af19980f76 (diff)
downloadswift-b2f58c4f3eb93e3a32062670df5eb6682aed273a.zip
swift-b2f58c4f3eb93e3a32062670df5eb6682aed273a.tar.bz2
Allow affiliation editing in MUCs.
Resolves: #986 Resolves: #988
-rw-r--r--.subl/swift.sublime-project2
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp26
-rw-r--r--Swift/Controllers/Chat/MUCController.h3
-rw-r--r--Swift/Controllers/UIInterfaces/ChatWindow.h7
-rw-r--r--Swift/Controllers/UnitTest/MockChatWindow.h1
-rw-r--r--Swift/QtUI/QtAffiliationEditor.cpp79
-rw-r--r--Swift/QtUI/QtAffiliationEditor.h37
-rw-r--r--Swift/QtUI/QtAffiliationEditor.ui146
-rw-r--r--Swift/QtUI/QtChatWindow.cpp44
-rw-r--r--Swift/QtUI/QtChatWindow.h6
-rw-r--r--Swift/QtUI/Roster/QtOccupantListWidget.cpp1
-rw-r--r--Swift/QtUI/SConscript6
-rw-r--r--Swiften/MUC/MUC.cpp48
-rw-r--r--Swiften/MUC/MUC.h8
14 files changed, 398 insertions, 16 deletions
diff --git a/.subl/swift.sublime-project b/.subl/swift.sublime-project
index 5746207..1d637c4 100644
--- a/.subl/swift.sublime-project
+++ b/.subl/swift.sublime-project
@@ -5,7 +5,7 @@
{
"path": "..",
"folder_exclude_patterns": ["tmp", ".sconf_temp", ".settings", "Swift.app", "3rdParty"],
- "file_exclude_patterns": [".cproject", ".project", ".sconsign.dblite", "*~", "config.log", "*.o"]
+ "file_exclude_patterns": ["moc_*", ".cproject", ".project", ".sconsign.dblite", "*~", "config.log", "*.o"]
}
],
"settings":
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index e413d92..30e85ad 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -77,6 +77,8 @@ MUCController::MUCController (
chatWindow_->onConfigurationFormCancelled.connect(boost::bind(&MUCController::handleConfigurationCancelled, this));
chatWindow_->onDestroyRequest.connect(boost::bind(&MUCController::handleDestroyRoomRequest, this));
chatWindow_->onInvitePersonToThisMUCRequest.connect(boost::bind(&MUCController::handleInvitePersonToThisMUCRequest, this, _1, _2));
+ chatWindow_->onGetAffiliationsRequest.connect(boost::bind(&MUCController::handleGetAffiliationsRequest, this));
+ chatWindow_->onChangeAffiliationsRequest.connect(boost::bind(&MUCController::handleChangeAffiliationsRequest, this, _1));
muc_->onJoinComplete.connect(boost::bind(&MUCController::handleJoinComplete, this, _1));
muc_->onJoinFailed.connect(boost::bind(&MUCController::handleJoinFailed, this, _1));
muc_->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1));
@@ -86,6 +88,7 @@ MUCController::MUCController (
muc_->onConfigurationFailed.connect(boost::bind(&MUCController::handleConfigurationFailed, this, _1));
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));
if (timerFactory) {
loginCheckTimer_ = boost::shared_ptr<Timer>(timerFactory->createTimer(MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS));
loginCheckTimer_->onTick.connect(boost::bind(&MUCController::handleJoinTimeoutTick, this));
@@ -114,6 +117,7 @@ void MUCController::handleWindowOccupantSelectionChanged(ContactRosterItem* item
/* FIXME: all of these should be conditional */
if (item) {
actions.push_back(ChatWindow::Kick);
+ actions.push_back(ChatWindow::Ban);
actions.push_back(ChatWindow::MakeModerator);
actions.push_back(ChatWindow::MakeParticipant);
actions.push_back(ChatWindow::MakeVisitor);
@@ -124,6 +128,7 @@ void MUCController::handleWindowOccupantSelectionChanged(ContactRosterItem* item
void MUCController::handleActionRequestedOnOccupant(ChatWindow::OccupantAction action, ContactRosterItem* item) {
switch (action) {
case ChatWindow::Kick: muc_->kickOccupant(item->getJID());break;
+ case ChatWindow::Ban: muc_->changeAffiliation(item->getJID(), MUCOccupant::Outcast);break;
case ChatWindow::MakeModerator: muc_->changeOccupantRole(item->getJID(), MUCOccupant::Moderator);break;
case ChatWindow::MakeParticipant: muc_->changeOccupantRole(item->getJID(), MUCOccupant::Participant);break;
case ChatWindow::MakeVisitor: muc_->changeOccupantRole(item->getJID(), MUCOccupant::Visitor);break;
@@ -201,7 +206,7 @@ void MUCController::handleJoinFailed(boost::shared_ptr<ErrorPayload> error) {
break;
case ErrorPayload::NotAuthorized:
errorMessage += ": ";
- errorMessage += QT_TRANSLATE_NOOP("", "A password needed");
+ errorMessage += QT_TRANSLATE_NOOP("", "A password is needed");
break;
case ErrorPayload::RegistrationRequired:
errorMessage += ": ";
@@ -630,4 +635,23 @@ void MUCController::handleInvitePersonToThisMUCRequest(const JID& jid, const std
muc_->invitePerson(jid, reason);
}
+void MUCController::handleGetAffiliationsRequest() {
+ muc_->requestAffiliationList(MUCOccupant::Owner);
+ muc_->requestAffiliationList(MUCOccupant::Admin);
+ muc_->requestAffiliationList(MUCOccupant::Member);
+ muc_->requestAffiliationList(MUCOccupant::Outcast);
+}
+
+typedef std::pair<MUCOccupant::Affiliation, JID> AffiliationChangePair;
+
+void MUCController::handleChangeAffiliationsRequest(const std::vector<std::pair<MUCOccupant::Affiliation, JID> >& changes) {
+ foreach (const AffiliationChangePair& change, changes) {
+ muc_->changeAffiliation(change.second, change.first);
+ }
+}
+
+void MUCController::handleAffiliationListReceived(MUCOccupant::Affiliation affiliation, const std::vector<JID>& jids) {
+ chatWindow_->setAffiliations(affiliation, jids);
+}
+
}
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index 19d3f66..16dcb99 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -95,6 +95,9 @@ namespace Swift {
void handleInvitePersonToThisMUCRequest(const JID& jid, const std::string& reason);
void handleConfigurationCancelled();
void handleOccupantRoleChangeFailed(ErrorPayload::ref, const JID&, MUCOccupant::Role);
+ void handleGetAffiliationsRequest();
+ void handleAffiliationListReceived(MUCOccupant::Affiliation affiliation, const std::vector<JID>& jids);
+ void handleChangeAffiliationsRequest(const std::vector<std::pair<MUCOccupant::Affiliation, JID> >& changes);
private:
MUC::ref muc_;
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index 4a93b42..6fce7a0 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -17,6 +17,7 @@
#include <Swiften/Elements/SecurityLabelsCatalog.h>
#include <Swiften/Elements/ChatState.h>
#include <Swiften/Elements/Form.h>
+#include <Swiften/Elements/MUCOccupant.h>
namespace Swift {
@@ -32,7 +33,7 @@ namespace Swift {
public:
enum AckState {Pending, Received, Failed};
enum Tristate {Yes, No, Maybe};
- enum OccupantAction {Kick, MakeModerator, MakeParticipant, MakeVisitor};
+ enum OccupantAction {Kick, Ban, MakeModerator, MakeParticipant, MakeVisitor};
enum FileTransferState {WaitingForAccept, Negotiating, Transferring, Canceled, Finished, FTFailed};
ChatWindow() {}
virtual ~ChatWindow() {};
@@ -75,6 +76,7 @@ namespace Swift {
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;
/**
* Set an alert on the window.
@@ -112,6 +114,9 @@ namespace Swift {
boost::signal<void ()> onDestroyRequest;
boost::signal<void (const JID&, const std::string& /*reason*/)> onInvitePersonToThisMUCRequest;
boost::signal<void ()> onConfigurationFormCancelled;
+ boost::signal<void ()> onGetAffiliationsRequest;
+ boost::signal<void (MUCOccupant::Affiliation, const JID&)> onSetAffiliationRequest;
+ boost::signal<void (const std::vector<std::pair<MUCOccupant::Affiliation, JID> >& changes)> onChangeAffiliationsRequest;
// File transfer related
boost::signal<void (std::string /* id */)> onFileTransferCancel;
diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h
index 7b90a57..7e31179 100644
--- a/Swift/Controllers/UnitTest/MockChatWindow.h
+++ b/Swift/Controllers/UnitTest/MockChatWindow.h
@@ -49,6 +49,7 @@ namespace Swift {
void setSubject(const std::string& /*subject*/) {}
virtual void showRoomConfigurationForm(Form::ref) {}
virtual void addMUCInvitation(const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/) {};
+ virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&) {}
std::string name_;
std::string lastMessageBody_;
diff --git a/Swift/QtUI/QtAffiliationEditor.cpp b/Swift/QtUI/QtAffiliationEditor.cpp
new file mode 100644
index 0000000..ed03c23
--- /dev/null
+++ b/Swift/QtUI/QtAffiliationEditor.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2011 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "QtAffiliationEditor.h"
+
+#include <QListWidgetItem>
+#include <QInputDialog>
+
+#include "QtSwiftUtil.h"
+
+
+namespace Swift {
+QtAffiliationEditor::QtAffiliationEditor(QWidget* parent) : QDialog(parent){
+ ui_.setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+ connect(ui_.affiliation, SIGNAL(currentIndexChanged(int)), this, SLOT(handleCurrentIndexChanged(int)));
+ connect(ui_.addJID, SIGNAL(clicked()), this, SLOT(handleAddClicked()));
+ connect(ui_.removeJID, SIGNAL(clicked()), this, SLOT(handleRemoveClicked()));
+}
+
+QtAffiliationEditor::~QtAffiliationEditor() {
+
+}
+
+void QtAffiliationEditor::setAffiliations(MUCOccupant::Affiliation affiliation, const std::vector<JID>& jids) {
+ affiliations_[affiliation] = jids;
+ if (affiliationFromIndex(ui_.affiliation->currentIndex()) == affiliation) {
+ handleCurrentIndexChanged(ui_.affiliation->currentIndex());
+ }
+}
+
+const std::vector<std::pair<MUCOccupant::Affiliation, JID> >& QtAffiliationEditor::getChanges() const {
+ return changes_;
+}
+
+void QtAffiliationEditor::handleCurrentIndexChanged(int index) {
+ ui_.list->clear();
+ foreach (const JID& jid, affiliations_[affiliationFromIndex(index)]) {
+ ui_.list->addItem(P2QSTRING(jid.toString()));
+ }
+}
+
+void QtAffiliationEditor::handleAddClicked() {
+ bool ok = false;
+ JID jid = JID(Q2PSTRING(QInputDialog::getText(this, tr("Add User"), tr("Added User's Address:"), QLineEdit::Normal, "", &ok)));
+ if (ok && jid.isValid()) {
+ //FIXME: validation
+ MUCOccupant::Affiliation affiliation = affiliationFromIndex(ui_.affiliation->currentIndex());
+ changes_.push_back(ChangePair(affiliation, jid));
+ ui_.list->addItem(P2QSTRING(jid.toString()));
+ affiliations_[affiliation].push_back(jid);
+ }
+}
+
+void QtAffiliationEditor::handleRemoveClicked() {
+ QListWidgetItem* item = ui_.list->currentItem();
+ if (item) {
+ JID jid(Q2PSTRING(item->text()));
+ changes_.push_back(ChangePair(MUCOccupant::NoAffiliation, jid));
+ std::vector<JID>& jids = affiliations_[affiliationFromIndex(ui_.affiliation->currentIndex())];
+ jids.erase(std::remove(jids.begin(), jids.end(), jid), jids.end());
+ handleCurrentIndexChanged(ui_.affiliation->currentIndex());
+ }
+}
+
+MUCOccupant::Affiliation QtAffiliationEditor::affiliationFromIndex(int affiliation) {
+ switch (affiliation) {
+ case 0: return MUCOccupant::Owner;
+ case 1: return MUCOccupant::Admin;
+ case 2: return MUCOccupant::Member;
+ case 3: return MUCOccupant::Outcast;
+ default: return MUCOccupant::Outcast;
+ }
+}
+
+} \ No newline at end of file
diff --git a/Swift/QtUI/QtAffiliationEditor.h b/Swift/QtUI/QtAffiliationEditor.h
new file mode 100644
index 0000000..913b2cc
--- /dev/null
+++ b/Swift/QtUI/QtAffiliationEditor.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2011 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+#include <map>
+
+#include <QDialog>
+#include <Swift/QtUI/ui_QtAffiliationEditor.h>
+
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/MUCOccupant.h>
+
+namespace Swift {
+ class QtAffiliationEditor : public QDialog {
+ Q_OBJECT
+ public:
+ QtAffiliationEditor(QWidget* parent = NULL);
+ ~QtAffiliationEditor();
+ void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>& jids);
+ const std::vector<std::pair<MUCOccupant::Affiliation, JID> >& getChanges() const;
+ private slots:
+ void handleCurrentIndexChanged(int);
+ void handleAddClicked();
+ void handleRemoveClicked();
+ private:
+ typedef std::pair<MUCOccupant::Affiliation, JID> ChangePair;
+ MUCOccupant::Affiliation affiliationFromIndex(int affiliation);
+ Ui::QtAffiliationEditor ui_;
+ std::map<MUCOccupant::Affiliation, std::vector<JID> > affiliations_;
+ std::vector<ChangePair> changes_;
+ };
+} \ No newline at end of file
diff --git a/Swift/QtUI/QtAffiliationEditor.ui b/Swift/QtUI/QtAffiliationEditor.ui
new file mode 100644
index 0000000..1447884
--- /dev/null
+++ b/Swift/QtUI/QtAffiliationEditor.ui
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtAffiliationEditor</class>
+ <widget class="QDialog" name="QtAffiliationEditor">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>575</width>
+ <height>466</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Affiliation:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="affiliation">
+ <item>
+ <property name="text">
+ <string>Owner</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Administrator</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Member</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Outcast (Banned)</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QListWidget" name="list"/>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QPushButton" name="addJID">
+ <property name="text">
+ <string>Add User</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeJID">
+ <property name="text">
+ <string>Remove User</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>QtAffiliationEditor</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>224</x>
+ <y>444</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>QtAffiliationEditor</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>292</x>
+ <y>450</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index 7e47f4d..0635496 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -53,6 +53,7 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
unreadCount_ = 0;
inputEnabled_ = true;
completer_ = NULL;
+ affiliationEditor_ = NULL;
theme_ = theme;
isCorrection_ = false;
correctionEnabled_ = Maybe;
@@ -165,8 +166,8 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
QtChatWindow::~QtChatWindow() {
delete fileTransferJS;
- if (mucConfigurationWindow) {
- delete mucConfigurationWindow.data();
+ if (mucConfigurationWindow_) {
+ delete mucConfigurationWindow_.data();
}
}
@@ -341,7 +342,7 @@ void QtChatWindow::setCorrectionEnabled(Tristate enabled) {
SecurityLabelsCatalog::Item QtChatWindow::getSelectedSecurityLabel() {
assert(labelsWidget_->isEnabled());
- assert(labelsWidget_->currentIndex() >= 0 && labelsWidget_->currentIndex() < availableLabels_.size());
+ assert(labelsWidget_->currentIndex() >= 0 && static_cast<size_t>(labelsWidget_->currentIndex()) < availableLabels_.size());
return availableLabels_[labelsWidget_->currentIndex()];
}
@@ -371,8 +372,13 @@ void QtChatWindow::qAppFocusChanged(QWidget* /*old*/, QWidget* /*now*/) {
void QtChatWindow::setInputEnabled(bool enabled) {
inputEnabled_ = enabled;
- if (!enabled && mucConfigurationWindow) {
- delete mucConfigurationWindow.data();
+ if (!enabled) {
+ if (mucConfigurationWindow_) {
+ delete mucConfigurationWindow_.data();
+ }
+ if (affiliationEditor_) {
+ delete affiliationEditor_.data();
+ }
}
}
@@ -704,6 +710,7 @@ void QtChatWindow::handleActionButtonClicked() {
QMenu contextMenu;
QAction* changeSubject = contextMenu.addAction(tr("Change subject"));
QAction* configure = contextMenu.addAction(tr("Configure room"));
+ QAction* affiliations = contextMenu.addAction(tr("Edit affiliations"));
QAction* destroy = contextMenu.addAction(tr("Destroy room"));
QAction* invite = contextMenu.addAction(tr("Invite person to this room"));
QAction* result = contextMenu.exec(QCursor::pos());
@@ -717,6 +724,14 @@ void QtChatWindow::handleActionButtonClicked() {
else if (result == configure) {
onConfigureRequest(Form::ref());
}
+ else if (result == affiliations) {
+ if (!affiliationEditor_) {
+ onGetAffiliationsRequest();
+ affiliationEditor_ = new QtAffiliationEditor(this);
+ connect(affiliationEditor_, SIGNAL(accepted()), this, SLOT(handleAffiliationEditorAccepted()));
+ }
+ affiliationEditor_->show();
+ }
else if (result == destroy) {
onDestroyRequest();
}
@@ -729,13 +744,22 @@ void QtChatWindow::handleActionButtonClicked() {
}
}
+void QtChatWindow::handleAffiliationEditorAccepted() {
+ onChangeAffiliationsRequest(affiliationEditor_->getChanges());
+}
+
+void QtChatWindow::setAffiliations(MUCOccupant::Affiliation affiliation, const std::vector<JID>& jids) {
+ if (!affiliationEditor_) return;
+ affiliationEditor_->setAffiliations(affiliation, jids);
+}
+
void QtChatWindow::showRoomConfigurationForm(Form::ref form) {
- if (mucConfigurationWindow) {
- delete mucConfigurationWindow.data();
+ if (mucConfigurationWindow_) {
+ delete mucConfigurationWindow_.data();
}
- mucConfigurationWindow = new QtMUCConfigurationWindow(form);
- mucConfigurationWindow->onFormComplete.connect(boost::bind(boost::ref(onConfigureRequest), _1));
- mucConfigurationWindow->onFormCancelled.connect(boost::bind(boost::ref(onConfigurationFormCancelled)));
+ mucConfigurationWindow_ = new QtMUCConfigurationWindow(form);
+ mucConfigurationWindow_->onFormComplete.connect(boost::bind(boost::ref(onConfigureRequest), _1));
+ mucConfigurationWindow_->onFormCancelled.connect(boost::bind(boost::ref(onConfigurationFormCancelled)));
}
void QtChatWindow::addMUCInvitation(const JID& jid, const std::string& reason, const std::string& password) {
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index 8dfe767..e546f12 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -8,6 +8,7 @@
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
#include <Swift/QtUI/QtMUCConfigurationWindow.h>
+#include <Swift/QtUI/QtAffiliationEditor.h>
#include <QtTabbable.h>
@@ -73,6 +74,7 @@ namespace Swift {
void setSubject(const std::string& subject);
void showRoomConfigurationForm(Form::ref);
void addMUCInvitation(const JID& jid, const std::string& reason, const std::string& password);
+ void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&);
public slots:
void handleChangeSplitterState(QByteArray state);
@@ -111,6 +113,7 @@ namespace Swift {
void handleFileTransferSetDescription(QString id);
void handleFileTransferStart(QString id);
void handleFileTransferAccept(QString id, QString filename);
+ void handleAffiliationEditorAccepted();
private:
void updateTitleWithUnreadCount();
@@ -153,6 +156,7 @@ namespace Swift {
QString alertStyleSheet_;
std::map<QString, QString> descriptions;
QtFileTransferJSBridge* fileTransferJS;
- QPointer<QtMUCConfigurationWindow> mucConfigurationWindow;
+ QPointer<QtMUCConfigurationWindow> mucConfigurationWindow_;
+ QPointer<QtAffiliationEditor> affiliationEditor_;
};
}
diff --git a/Swift/QtUI/Roster/QtOccupantListWidget.cpp b/Swift/QtUI/Roster/QtOccupantListWidget.cpp
index 3ee0b7d..3f66585 100644
--- a/Swift/QtUI/Roster/QtOccupantListWidget.cpp
+++ b/Swift/QtUI/Roster/QtOccupantListWidget.cpp
@@ -45,6 +45,7 @@ void QtOccupantListWidget::contextMenuEvent(QContextMenuEvent* event) {
QString text = "Error: missing string";
switch (availableAction) {
case ChatWindow::Kick: text = tr("Kick user"); break;
+ case ChatWindow::Ban: text = tr("Kick and ban user"); break;
case ChatWindow::MakeModerator: text = tr("Make moderator"); break;
case ChatWindow::MakeParticipant: text = tr("Make participant"); break;
case ChatWindow::MakeVisitor: text = tr("Remove voice"); break;
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 82e4490..71dc59f 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -84,8 +84,8 @@ sources = [
"QtTabWidget.cpp",
"QtTextEdit.cpp",
"QtXMLConsoleWidget.cpp",
- "QtFileTransferListWidget.cpp",
- "QtFileTransferListItemModel.cpp",
+ "QtFileTransferListWidget.cpp",
+ "QtFileTransferListItemModel.cpp",
"QtAdHocCommandWindow.cpp",
"QtUtilities.cpp",
"QtBookmarkDetailWindow.cpp",
@@ -137,6 +137,7 @@ sources = [
"qrc_Swift.cc",
"QtFileTransferJSBridge.cpp",
"QtMUCConfigurationWindow.cpp",
+ "QtAffiliationEditor.cpp",
]
myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")
@@ -173,6 +174,7 @@ myenv.Uic4("UserSearch/QtUserSearchFirstPage.ui")
myenv.Uic4("UserSearch/QtUserSearchFieldsPage.ui")
myenv.Uic4("UserSearch/QtUserSearchResultsPage.ui")
myenv.Uic4("QtBookmarkDetailWindow.ui")
+myenv.Uic4("QtAffiliationEditor.ui")
myenv.Uic4("QtJoinMUCWindow.ui")
myenv.Qrc("DefaultTheme.qrc")
myenv.Qrc("Swift.qrc")
diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp
index 204fdcc..824ced1 100644
--- a/Swiften/MUC/MUC.cpp
+++ b/Swiften/MUC/MUC.cpp
@@ -241,6 +241,9 @@ void MUC::kickOccupant(const JID& jid) {
changeOccupantRole(jid, MUCOccupant::NoRole);
}
+/**
+ * Call with the room JID, not the real JID.
+ */
void MUC::changeOccupantRole(const JID& jid, MUCOccupant::Role role) {
MUCAdminPayload::ref mucPayload = boost::make_shared<MUCAdminPayload>();
MUCItem item;
@@ -259,6 +262,51 @@ void MUC::handleOccupantRoleChangeResponse(MUCAdminPayload::ref /*unused*/, Erro
}
}
+void MUC::requestAffiliationList(MUCOccupant::Affiliation affiliation) {
+ MUCAdminPayload::ref mucPayload = boost::make_shared<MUCAdminPayload>();
+ MUCItem item;
+ item.affiliation = affiliation;
+ mucPayload->addItem(item);
+ GenericRequest<MUCAdminPayload>* request = new GenericRequest<MUCAdminPayload>(IQ::Get, getJID(), mucPayload, iqRouter_);
+ request->onResponse.connect(boost::bind(&MUC::handleAffiliationListResponse, this, _1, _2, affiliation));
+ request->send();
+}
+
+/**
+ * Must be called with the real JID, not the room JID.
+ */
+void MUC::changeAffiliation(const JID& jid, MUCOccupant::Affiliation affiliation) {
+ MUCAdminPayload::ref mucPayload = boost::make_shared<MUCAdminPayload>();
+ MUCItem item;
+ item.affiliation = affiliation;
+ item.realJID = jid;
+ mucPayload->addItem(item);
+ GenericRequest<MUCAdminPayload>* request = new GenericRequest<MUCAdminPayload>(IQ::Set, getJID(), mucPayload, iqRouter_);
+ request->onResponse.connect(boost::bind(&MUC::handleAffiliationChangeResponse, this, _1, _2, jid, affiliation));
+ request->send();
+}
+
+void MUC::handleAffiliationListResponse(MUCAdminPayload::ref payload, ErrorPayload::ref error, MUCOccupant::Affiliation affiliation) {
+ if (error) {
+ onAffiliationListFailed(error);
+ }
+ else {
+ std::vector<JID> jids;
+ foreach (MUCItem item, payload->getItems()) {
+ if (item.realJID) {
+ jids.push_back(*item.realJID);
+ }
+ }
+ onAffiliationListReceived(affiliation, jids);
+ }
+}
+
+void MUC::handleAffiliationChangeResponse(MUCAdminPayload::ref /*unused*/, ErrorPayload::ref error, const JID& jid, MUCOccupant::Affiliation affiliation) {
+ if (error) {
+ onAffiliationChangeFailed(error, jid, affiliation);
+ }
+}
+
void MUC::changeSubject(const std::string& subject) {
Message::ref message = boost::make_shared<Message>();
message->setSubject(subject);
diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h
index d855033..1070c69 100644
--- a/Swiften/MUC/MUC.h
+++ b/Swiften/MUC/MUC.h
@@ -58,6 +58,8 @@ namespace Swift {
bool hasOccupant(const std::string& nick);
void kickOccupant(const JID& jid);
void changeOccupantRole(const JID& jid, MUCOccupant::Role role);
+ void requestAffiliationList(MUCOccupant::Affiliation);
+ void changeAffiliation(const JID& jid, MUCOccupant::Affiliation affiliation);
void changeSubject(const std::string& subject);
void requestConfigurationForm();
void configureRoom(Form::ref);
@@ -67,17 +69,21 @@ namespace Swift {
void invitePerson(const JID& person, const std::string& reason = "");
void setCreateAsReservedIfNew() {createAsReservedIfNew = true;}
void setPassword(const boost::optional<std::string>& password);
+
public:
boost::signal<void (const std::string& /*nick*/)> onJoinComplete;
boost::signal<void (ErrorPayload::ref)> onJoinFailed;
boost::signal<void (ErrorPayload::ref, const JID&, MUCOccupant::Role)> onRoleChangeFailed;
+ boost::signal<void (ErrorPayload::ref, const JID&, MUCOccupant::Affiliation)> onAffiliationChangeFailed;
boost::signal<void (ErrorPayload::ref)> onConfigurationFailed;
+ boost::signal<void (ErrorPayload::ref)> onAffiliationListFailed;
boost::signal<void (Presence::ref)> onOccupantPresenceChange;
boost::signal<void (const std::string&, const MUCOccupant& /*now*/, const MUCOccupant::Role& /*old*/)> onOccupantRoleChanged;
boost::signal<void (const std::string&, const MUCOccupant::Affiliation& /*new*/, const MUCOccupant::Affiliation& /*old*/)> onOccupantAffiliationChanged;
boost::signal<void (const MUCOccupant&)> onOccupantJoined;
boost::signal<void (const MUCOccupant&, LeavingType, const std::string& /*reason*/)> onOccupantLeft;
boost::signal<void (Form::ref)> onConfigurationFormReceived;
+ boost::signal<void (MUCOccupant::Affiliation, const std::vector<JID>&)> onAffiliationListReceived;
/* boost::signal<void (const MUCInfo&)> onInfoResult; */
/* boost::signal<void (const blah&)> onItemsResult; */
@@ -96,6 +102,8 @@ namespace Swift {
void internalJoin(const std::string& nick);
void handleCreationConfigResponse(MUCOwnerPayload::ref, ErrorPayload::ref);
void handleOccupantRoleChangeResponse(MUCAdminPayload::ref, ErrorPayload::ref, const JID&, MUCOccupant::Role);
+ void handleAffiliationChangeResponse(MUCAdminPayload::ref, ErrorPayload::ref, const JID&, MUCOccupant::Affiliation);
+ void handleAffiliationListResponse(MUCAdminPayload::ref, ErrorPayload::ref, MUCOccupant::Affiliation);
void handleConfigurationFormReceived(MUCOwnerPayload::ref, ErrorPayload::ref);
void handleConfigurationResultReceived(MUCOwnerPayload::ref, ErrorPayload::ref);