diff options
| author | Kevin Smith <git@kismith.co.uk> | 2011-10-07 14:09:27 (GMT) | 
|---|---|---|
| committer | Kevin Smith <git@kismith.co.uk> | 2011-10-07 14:11:23 (GMT) | 
| commit | b2f58c4f3eb93e3a32062670df5eb6682aed273a (patch) | |
| tree | 737884f5e3e826407466290cf7c324c4f0069dd0 | |
| parent | 7f7b05d8d242a9b73b7d9f971779c6af19980f76 (diff) | |
| download | swift-b2f58c4f3eb93e3a32062670df5eb6682aed273a.zip swift-b2f58c4f3eb93e3a32062670df5eb6682aed273a.tar.bz2 | |
Allow affiliation editing in MUCs.
Resolves: #986
Resolves: #988
| -rw-r--r-- | .subl/swift.sublime-project | 2 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/MUCController.cpp | 26 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/MUCController.h | 3 | ||||
| -rw-r--r-- | Swift/Controllers/UIInterfaces/ChatWindow.h | 7 | ||||
| -rw-r--r-- | Swift/Controllers/UnitTest/MockChatWindow.h | 1 | ||||
| -rw-r--r-- | Swift/QtUI/QtAffiliationEditor.cpp | 79 | ||||
| -rw-r--r-- | Swift/QtUI/QtAffiliationEditor.h | 37 | ||||
| -rw-r--r-- | Swift/QtUI/QtAffiliationEditor.ui | 146 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatWindow.cpp | 44 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatWindow.h | 6 | ||||
| -rw-r--r-- | Swift/QtUI/Roster/QtOccupantListWidget.cpp | 1 | ||||
| -rw-r--r-- | Swift/QtUI/SConscript | 6 | ||||
| -rw-r--r-- | Swiften/MUC/MUC.cpp | 48 | ||||
| -rw-r--r-- | Swiften/MUC/MUC.h | 8 | 
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); | 
 Swift
 Swift