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-contrib-b2f58c4f3eb93e3a32062670df5eb6682aed273a.zip
swift-contrib-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
@@ -1,29 +1,29 @@
/* http://www.sublimetext.com/docs/2/projects.html */
{
"folders":
[
{
"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":
{
"tab_size": 4,
"translate_tabs_to_spaces": false
},
"build_systems":
[
{
"name": "Scons",
"cmd" : ["./scons"],
"working_dir": ".."
},
{
"name": "Scons test",
"cmd" : ["./scons", "check=1"],
"working_dir": ".."
}
]
}
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
@@ -45,117 +45,122 @@ MUCController::MUCController (
const JID& self,
MUC::ref muc,
const boost::optional<std::string>& password,
const std::string &nick,
StanzaChannel* stanzaChannel,
IQRouter* iqRouter,
ChatWindowFactory* chatWindowFactory,
PresenceOracle* presenceOracle,
AvatarManager* avatarManager,
UIEventStream* uiEventStream,
bool useDelayForLatency,
TimerFactory* timerFactory,
EventController* eventController,
EntityCapsProvider* entityCapsProvider) :
ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider), muc_(muc), nick_(nick), desiredNick_(nick), password_(password) {
parting_ = true;
joined_ = false;
lastWasPresence_ = false;
shouldJoinOnReconnect_ = true;
doneGettingHistory_ = false;
events_ = uiEventStream;
roster_ = new Roster(false, true);
completer_ = new TabComplete();
chatWindow_->setRosterModel(roster_);
chatWindow_->setTabComplete(completer_);
chatWindow_->setName(muc->getJID().getNode());
chatWindow_->onClosed.connect(boost::bind(&MUCController::handleWindowClosed, this));
chatWindow_->onOccupantSelectionChanged.connect(boost::bind(&MUCController::handleWindowOccupantSelectionChanged, this, _1));
chatWindow_->onOccupantActionSelected.connect(boost::bind(&MUCController::handleActionRequestedOnOccupant, this, _1, _2));
chatWindow_->onChangeSubjectRequest.connect(boost::bind(&MUCController::handleChangeSubjectRequest, this, _1));
chatWindow_->onConfigureRequest.connect(boost::bind(&MUCController::handleConfigureRequest, this, _1));
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));
muc_->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1));
muc_->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3));
muc_->onOccupantRoleChanged.connect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3));
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));
loginCheckTimer_->start();
}
chatWindow_->convertToMUC();
setOnline(true);
if (avatarManager_ != NULL) {
avatarChangedConnection_ = (avatarManager_->onAvatarChanged.connect(boost::bind(&MUCController::handleAvatarChanged, this, _1)));
}
handleBareJIDCapsChanged(muc->getJID());
}
MUCController::~MUCController() {
chatWindow_->setRosterModel(NULL);
delete roster_;
if (loginCheckTimer_) {
loginCheckTimer_->stop();
}
chatWindow_->setTabComplete(NULL);
delete completer_;
}
void MUCController::handleWindowOccupantSelectionChanged(ContactRosterItem* item) {
std::vector<ChatWindow::OccupantAction> actions;
/* 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);
}
chatWindow_->setAvailableOccupantActions(actions);
}
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;
}
}
void MUCController::handleBareJIDCapsChanged(const JID& /*jid*/) {
ChatWindow::Tristate support = ChatWindow::Yes;
bool any = false;
foreach (const std::string& nick, currentOccupants_) {
DiscoInfo::ref disco = entityCapsProvider_->getCaps(toJID_.toBare().toString() + "/" + nick);
if (disco && disco->hasFeature(DiscoInfo::MessageCorrectionFeature)) {
any = true;
} else {
support = ChatWindow::Maybe;
}
}
if (!any) {
support = ChatWindow::No;
}
chatWindow_->setCorrectionEnabled(support);
}
/**
* Join the MUC if not already in it.
*/
void MUCController::rejoin() {
if (parting_) {
joined_ = false;
parting_ = false;
if (password_) {
muc_->setPassword(*password_);
}
//FIXME: check for received activity
if (lastActivity_ == boost::posix_time::not_a_date_time) {
@@ -169,71 +174,71 @@ void MUCController::rejoin() {
bool MUCController::isJoined() {
return joined_;
}
const std::string& MUCController::getNick() {
return nick_;
}
void MUCController::handleJoinTimeoutTick() {
receivedActivity();
chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "Room %1% is not responding. This operation may never complete.")) % toJID_.toString()));
}
void MUCController::receivedActivity() {
if (loginCheckTimer_) {
loginCheckTimer_->stop();
}
}
void MUCController::handleJoinFailed(boost::shared_ptr<ErrorPayload> error) {
receivedActivity();
std::string errorMessage = QT_TRANSLATE_NOOP("", "Unable to enter this room");
std::string rejoinNick;
if (error) {
switch (error->getCondition()) {
case ErrorPayload::Conflict:
rejoinNick = nick_ + "_";
errorMessage = str(format(QT_TRANSLATE_NOOP("", "Unable to enter this room as %1%, retrying as %2%")) % nick_ % rejoinNick);
break;
case ErrorPayload::JIDMalformed:
errorMessage += ": ";
errorMessage += QT_TRANSLATE_NOOP("", "No nickname specified");
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 += ": ";
errorMessage += QT_TRANSLATE_NOOP("", "Only members may enter");
break;
case ErrorPayload::Forbidden:
errorMessage += ": ";
errorMessage += QT_TRANSLATE_NOOP("", "You are banned from the room");
break;
case ErrorPayload::ServiceUnavailable:
errorMessage += ": ";
errorMessage += QT_TRANSLATE_NOOP("", "The room is full");
break;
case ErrorPayload::ItemNotFound:
errorMessage += ": ";
errorMessage += QT_TRANSLATE_NOOP("", "The room does not exist");
break;
default: break;
}
}
errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't join room: %1%.")) % errorMessage);
chatWindow_->addErrorMessage(errorMessage);
if (!rejoinNick.empty()) {
nick_ = rejoinNick;
parting_ = true;
rejoin();
}
}
void MUCController::handleJoinComplete(const std::string& nick) {
receivedActivity();
joined_ = true;
std::string joinMessage = str(format(QT_TRANSLATE_NOOP("", "You have entered room %1% as %2%.")) % toJID_.toString() % nick);
nick_ = nick;
@@ -598,36 +603,55 @@ void MUCController::handleConfigureRequest(Form::ref form) {
muc_->configureRoom(form);
}
else {
muc_->requestConfigurationForm();
}
}
void MUCController::handleConfigurationFailed(ErrorPayload::ref error) {
std::string errorMessage = getErrorMessage(error);
errorMessage = str(format(QT_TRANSLATE_NOOP("", "Room configuration failed: %1%.")) % errorMessage);
chatWindow_->addErrorMessage(errorMessage);
}
void MUCController::handleOccupantRoleChangeFailed(ErrorPayload::ref error, const JID&, MUCOccupant::Role) {
std::string errorMessage = getErrorMessage(error);
errorMessage = str(format(QT_TRANSLATE_NOOP("", "Occupant role change failed: %1%.")) % errorMessage);
chatWindow_->addErrorMessage(errorMessage);
}
void MUCController::handleConfigurationFormReceived(Form::ref form) {
chatWindow_->showRoomConfigurationForm(form);
}
void MUCController::handleConfigurationCancelled() {
muc_->cancelConfigureRoom();
}
void MUCController::handleDestroyRoomRequest() {
muc_->destroyRoom();
}
void MUCController::handleInvitePersonToThisMUCRequest(const JID& jid, const std::string& reason) {
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
@@ -63,57 +63,60 @@ namespace Swift {
void postHandleIncomingMessage(boost::shared_ptr<MessageEvent>);
private:
void clearPresenceQueue();
void addPresenceMessage(const std::string& message);
void handleWindowOccupantSelectionChanged(ContactRosterItem* item);
void handleActionRequestedOnOccupant(ChatWindow::OccupantAction, ContactRosterItem* item);
void handleWindowClosed();
void handleAvatarChanged(const JID& jid);
void handleOccupantJoined(const MUCOccupant& occupant);
void handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType type, const std::string& reason);
void handleOccupantPresenceChange(boost::shared_ptr<Presence> presence);
void handleOccupantRoleChanged(const std::string& nick, const MUCOccupant& occupant,const MUCOccupant::Role& oldRole);
void handleJoinComplete(const std::string& nick);
void handleJoinFailed(boost::shared_ptr<ErrorPayload> error);
void handleJoinTimeoutTick();
void handleChangeSubjectRequest(const std::string&);
std::string roleToGroupName(MUCOccupant::Role role);
std::string roleToSortName(MUCOccupant::Role role);
JID nickToJID(const std::string& nick);
std::string roleToFriendlyName(MUCOccupant::Role role);
void receivedActivity();
bool messageTargetsMe(boost::shared_ptr<Message> message);
void updateJoinParts();
bool shouldUpdateJoinParts();
void dayTicked() {lastWasPresence_ = false;}
void processUserPart();
void handleBareJIDCapsChanged(const JID& jid);
void handleConfigureRequest(Form::ref);
void handleConfigurationFailed(ErrorPayload::ref);
void handleConfigurationFormReceived(Form::ref);
void handleDestroyRoomRequest();
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_;
UIEventStream* events_;
std::string nick_;
std::string desiredNick_;
Roster* roster_;
TabComplete* completer_;
bool parting_;
bool joined_;
bool lastWasPresence_;
bool shouldJoinOnReconnect_;
bool doneGettingHistory_;
boost::bsignals::scoped_connection avatarChangedConnection_;
boost::shared_ptr<Timer> loginCheckTimer_;
std::set<std::string> currentOccupants_;
std::vector<NickJoinPart> joinParts_;
boost::posix_time::ptime lastActivity_;
boost::optional<std::string> password_;
};
}
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
@@ -1,124 +1,129 @@
/*
* Copyright (c) 2010 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#ifndef SWIFTEN_CHATWINDOW_H
#define SWIFTEN_CHATWINDOW_H
#include <boost/optional.hpp>
#include "Swiften/Base/boost_bsignals.h"
#include <boost/shared_ptr.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <vector>
#include <string>
#include <Swiften/Elements/SecurityLabelsCatalog.h>
#include <Swiften/Elements/ChatState.h>
#include <Swiften/Elements/Form.h>
+#include <Swiften/Elements/MUCOccupant.h>
namespace Swift {
class AvatarManager;
class TreeWidget;
class Roster;
class TabComplete;
class RosterItem;
class ContactRosterItem;
class FileTransferController;
class ChatWindow {
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() {};
/** Add message to window.
* @return id of added message (for acks).
*/
virtual std::string addMessage(const std::string& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time) = 0;
/** Adds action to window.
* @return id of added message (for acks);
*/
virtual std::string addAction(const std::string& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time) = 0;
virtual void addSystemMessage(const std::string& message) = 0;
virtual void addPresenceMessage(const std::string& message) = 0;
virtual void addErrorMessage(const std::string& message) = 0;
virtual void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) = 0;
// File transfer related stuff
virtual std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) = 0;
virtual void setFileTransferProgress(std::string, const int percentageDone) = 0;
virtual void setFileTransferStatus(std::string, const FileTransferState state, const std::string& msg = "") = 0;
virtual void addMUCInvitation(const JID& jid, const std::string& reason, const std::string& password) = 0;
virtual void setContactChatState(ChatState::ChatStateType state) = 0;
virtual void setName(const std::string& name) = 0;
virtual void show() = 0;
virtual void activate() = 0;
virtual void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) = 0;
virtual void setSecurityLabelsEnabled(bool enabled) = 0;
virtual void setCorrectionEnabled(Tristate enabled) = 0;
virtual void setUnreadMessageCount(int count) = 0;
virtual void convertToMUC() = 0;
// virtual TreeWidget *getTreeWidget() = 0;
virtual void setSecurityLabelsError() = 0;
virtual SecurityLabelsCatalog::Item getSelectedSecurityLabel() = 0;
virtual void setInputEnabled(bool enabled) = 0;
virtual void setRosterModel(Roster* model) = 0;
virtual void setTabComplete(TabComplete* completer) = 0;
virtual void replaceLastMessage(const std::string& message) = 0;
virtual void 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.
* @param alertText Description of alert (required).
* @param buttonText Button text to use (optional, no button is shown if empty).
*/
virtual void setAlert(const std::string& alertText, const std::string& buttonText = "") = 0;
/**
* Removes an alert.
*/
virtual void cancelAlert() = 0;
/**
* Actions that can be performed on the selected occupant.
*/
virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions) = 0;
/**
* A room configuration has been requested, show the form.
* If the form is cancelled, must emit onConfigurationFormCancelled().
*/
virtual void showRoomConfigurationForm(Form::ref) = 0;
boost::signal<void ()> onClosed;
boost::signal<void ()> onAllMessagesRead;
boost::signal<void (const std::string&, bool isCorrection)> onSendMessageRequest;
boost::signal<void ()> onSendCorrectionMessageRequest;
boost::signal<void ()> onUserTyping;
boost::signal<void ()> onUserCancelsTyping;
boost::signal<void ()> onAlertButtonClicked;
boost::signal<void (ContactRosterItem*)> onOccupantSelectionChanged;
boost::signal<void (ChatWindow::OccupantAction, ContactRosterItem*)> onOccupantActionSelected;
boost::signal<void (const std::string&)> onChangeSubjectRequest;
boost::signal<void (Form::ref)> onConfigureRequest;
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;
boost::signal<void (std::string /* id */, std::string /* description */)> onFileTransferStart;
boost::signal<void (std::string /* id */, std::string /* path */)> onFileTransferAccept;
boost::signal<void (std::string /* path */)> onSendFileRequest;
};
}
#endif
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
@@ -17,44 +17,45 @@ namespace Swift {
virtual std::string addMessage(const std::string& message, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime&) {lastMessageBody_ = message; return "";};
virtual std::string addAction(const std::string& message, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime&) {lastMessageBody_ = message; return "";};
virtual void addSystemMessage(const std::string& /*message*/) {};
virtual void addErrorMessage(const std::string& /*message*/) {};
virtual void addPresenceMessage(const std::string& /*message*/) {};
// File transfer related stuff
virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/,const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/) { return 0; };
virtual void setFileTransferProgress(std::string /*id*/, const int /*alreadyTransferedBytes*/) { };
virtual void setFileTransferStatus(std::string /*id*/, const FileTransferState /*state*/, const std::string& /*msg*/) { };
virtual void setContactChatState(ChatState::ChatStateType /*state*/) {};
virtual void setName(const std::string& name) {name_ = name;};
virtual void show() {};
virtual void activate() {};
virtual void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) {labels_ = labels;};
virtual void setSecurityLabelsEnabled(bool enabled) {labelsEnabled_ = enabled;};
virtual void setUnreadMessageCount(int /*count*/) {};
virtual void convertToMUC() {};
virtual void setSecurityLabelsError() {};
virtual SecurityLabelsCatalog::Item getSelectedSecurityLabel() {return label_;}
virtual void setInputEnabled(bool /*enabled*/) {};
virtual void setRosterModel(Roster* /*roster*/) {};
virtual void setTabComplete(TabComplete*) {};
virtual void replaceLastMessage(const std::string&) {};
virtual void replaceMessage(const std::string&, const std::string&, const boost::posix_time::ptime&) {};
void setAckState(const std::string& /*id*/, AckState /*state*/) {};
virtual void flash() {};
virtual void setAlert(const std::string& /*alertText*/, const std::string& /*buttonText*/) {};
virtual void cancelAlert() {};
virtual void setCorrectionEnabled(Tristate /*enabled*/) {}
void setAvailableOccupantActions(const std::vector<OccupantAction>&/* actions*/) {}
void setSubject(const std::string& /*subject*/) {}
virtual void showRoomConfigurationForm(Form::ref) {}
virtual void addMUCInvitation(const 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_;
std::vector<SecurityLabelsCatalog::Item> labels_;
bool labelsEnabled_;
SecurityLabelsCatalog::Item label_;
};
}
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
@@ -21,70 +21,71 @@
#include "SwifTools/TabComplete.h"
#include <Swift/Controllers/UIEvents/UIEventStream.h>
#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
#include "QtFileTransferJSBridge.h"
#include <boost/cstdint.hpp>
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <QLabel>
#include <QMessageBox>
#include <QInputDialog>
#include <QApplication>
#include <QBoxLayout>
#include <QCloseEvent>
#include <QComboBox>
#include <QFileInfo>
#include <QLineEdit>
#include <QSplitter>
#include <QString>
#include <QTextEdit>
#include <QTime>
#include <QUrl>
#include <QPushButton>
#include <QFileDialog>
#include <QMenu>
#include <QDebug>
namespace Swift {
QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageWasSystem_(false), previousMessageWasPresence_(false), previousMessageWasFileTransfer_(false), eventStream_(eventStream) {
unreadCount_ = 0;
inputEnabled_ = true;
completer_ = NULL;
+ affiliationEditor_ = NULL;
theme_ = theme;
isCorrection_ = false;
correctionEnabled_ = Maybe;
updateTitleWithUnreadCount();
QtSettingsProvider settings;
#ifdef SWIFT_EXPERIMENTAL_FT
setAcceptDrops(true);
#endif
alertStyleSheet_ = "background: rgb(255, 255, 153); color: black";
QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
layout->setContentsMargins(0,0,0,0);
layout->setSpacing(2);
alertWidget_ = new QWidget(this);
QHBoxLayout* alertLayout = new QHBoxLayout(alertWidget_);
layout->addWidget(alertWidget_);
alertLabel_ = new QLabel(this);
alertLayout->addWidget(alertLabel_);
alertButton_ = new QPushButton(this);
connect (alertButton_, SIGNAL(clicked()), this, SLOT(handleAlertButtonClicked()));
alertLayout->addWidget(alertButton_);
QPalette palette = alertWidget_->palette();
palette.setColor(QPalette::Window, QColor(Qt::yellow));
palette.setColor(QPalette::WindowText, QColor(Qt::black));
alertWidget_->setStyleSheet(alertStyleSheet_);
alertLabel_->setStyleSheet(alertStyleSheet_);
alertWidget_->hide();
QBoxLayout* subjectLayout = new QBoxLayout(QBoxLayout::LeftToRight);
subject_ = new QLineEdit(this);
subjectLayout->addWidget(subject_);
setSubject("");
@@ -133,72 +134,72 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
input_->setAcceptRichText(false);
inputBarLayout->addWidget(input_);
correctingLabel_ = new QLabel(tr("Correcting"), this);
inputBarLayout->addWidget(correctingLabel_);
correctingLabel_->hide();
layout->addLayout(inputBarLayout);
inputClearing_ = false;
contactIsTyping_ = false;
connect(input_, SIGNAL(unhandledKeyPressEvent(QKeyEvent*)), this, SLOT(handleKeyPressEvent(QKeyEvent*)));
connect(input_, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
connect(input_, SIGNAL(textChanged()), this, SLOT(handleInputChanged()));
setFocusProxy(input_);
logRosterSplitter_->setFocusProxy(input_);
midBar->setFocusProxy(input_);
messageLog_->setFocusProxy(input_);
connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(qAppFocusChanged(QWidget*, QWidget*)));
connect(messageLog_, SIGNAL(gotFocus()), input_, SLOT(setFocus()));
resize(400,300);
connect(messageLog_, SIGNAL(fontResized(int)), this, SIGNAL(fontResized(int)));
treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QtChatWindow::handleOccupantSelectionChanged, this, _1));
treeWidget_->onOccupantActionSelected.connect(boost::bind(boost::ref(onOccupantActionSelected), _1, _2));
fileTransferJS = new QtFileTransferJSBridge();
messageLog_->addToJSEnvironment("filetransfer", fileTransferJS);
connect(fileTransferJS, SIGNAL(setDescription(QString)), this, SLOT(handleFileTransferSetDescription(QString)));
connect(fileTransferJS, SIGNAL(sendRequest(QString)), this, SLOT(handleFileTransferStart(QString)));
connect(fileTransferJS, SIGNAL(acceptRequest(QString, QString)), this, SLOT(handleFileTransferAccept(QString, QString)));
connect(fileTransferJS, SIGNAL(cancel(QString)), this, SLOT(handleFileTransferCancel(QString)));
}
QtChatWindow::~QtChatWindow() {
delete fileTransferJS;
- if (mucConfigurationWindow) {
- delete mucConfigurationWindow.data();
+ if (mucConfigurationWindow_) {
+ delete mucConfigurationWindow_.data();
}
}
void QtChatWindow::handleOccupantSelectionChanged(RosterItem* item) {
onOccupantSelectionChanged(dynamic_cast<ContactRosterItem*>(item));
}
void QtChatWindow::handleFontResized(int fontSizeSteps) {
messageLog_->resizeFont(fontSizeSteps);
}
void QtChatWindow::handleAlertButtonClicked() {
onAlertButtonClicked();
}
void QtChatWindow::setAlert(const std::string& alertText, const std::string& buttonText) {
alertLabel_->setText(alertText.c_str());
if (buttonText.empty()) {
alertButton_->hide();
} else {
alertButton_->setText(buttonText.c_str());
alertButton_->show();
}
alertWidget_->show();
}
void QtChatWindow::cancelAlert() {
alertWidget_->hide();
}
void QtChatWindow::setTabComplete(TabComplete* completer) {
completer_ = completer;
}
@@ -309,102 +310,107 @@ void QtChatWindow::setAvailableSecurityLabels(const std::vector<SecurityLabelsCa
int i = 0;
int defaultIndex = 0;
foreach (SecurityLabelsCatalog::Item label, labels) {
std::string selector = label.getSelector();
std::string displayMarking = label.getLabel() ? label.getLabel()->getDisplayMarking() : "";
QString labelName = selector.empty() ? displayMarking.c_str() : selector.c_str();
labelsWidget_->addItem(labelName, QVariant(i));
if (label.getIsDefault()) {
defaultIndex = i;
}
i++;
}
labelsWidget_->setCurrentIndex(defaultIndex);
}
void QtChatWindow::setSecurityLabelsError() {
labelsWidget_->setEnabled(false);
}
void QtChatWindow::setSecurityLabelsEnabled(bool enabled) {
if (enabled) {
labelsWidget_->setEnabled(true);
labelsWidget_->show();
} else {
labelsWidget_->hide();
}
}
void QtChatWindow::setCorrectionEnabled(Tristate enabled) {
correctionEnabled_ = 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()];
}
void QtChatWindow::closeEvent(QCloseEvent* event) {
event->accept();
emit windowClosing();
onClosed();
}
void QtChatWindow::convertToMUC() {
setAcceptDrops(false);
treeWidget_->show();
subject_->show();
actionButton_->show();
}
void QtChatWindow::qAppFocusChanged(QWidget* /*old*/, QWidget* /*now*/) {
if (isWidgetSelected()) {
lastLineTracker_.setHasFocus(true);
input_->setFocus();
onAllMessagesRead();
}
else {
lastLineTracker_.setHasFocus(false);
}
}
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();
+ }
}
}
void QtChatWindow::showEvent(QShowEvent* event) {
emit windowOpening();
QWidget::showEvent(event);
}
void QtChatWindow::setUnreadMessageCount(int count) {
if (unreadCount_ != count) {
unreadCount_ = count;
updateTitleWithUnreadCount();
emit countUpdated();
}
}
void QtChatWindow::setContactChatState(ChatState::ChatStateType state) {
contactIsTyping_ = (state == ChatState::Composing);
emit titleUpdated();
}
QtTabbable::AlertType QtChatWindow::getWidgetAlertState() {
if (contactIsTyping_) {
return ImpendingActivity;
}
if (unreadCount_ > 0) {
return WaitingActivity;
}
return NoActivity;
}
void QtChatWindow::setName(const std::string& name) {
contact_ = P2QSTRING(name);
updateTitleWithUnreadCount();
}
@@ -672,94 +678,112 @@ void QtChatWindow::moveEvent(QMoveEvent*) {
emit geometryChanged();
}
void QtChatWindow::dragEnterEvent(QDragEnterEvent *event) {
if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1) {
// TODO: check whether contact actually supports file transfer
event->acceptProposedAction();
}
}
void QtChatWindow::dropEvent(QDropEvent *event) {
if (event->mimeData()->urls().size() == 1) {
onSendFileRequest(Q2PSTRING(event->mimeData()->urls().at(0).toLocalFile()));
} else {
addSystemMessage("Sending of multiple files at once isn't supported at this time.");
}
}
void QtChatWindow::replaceLastMessage(const std::string& message) {
messageLog_->replaceLastMessage(P2QSTRING(Linkify::linkify(message)));
}
void QtChatWindow::setAvailableOccupantActions(const std::vector<OccupantAction>& actions) {
treeWidget_->setAvailableOccupantActions(actions);
}
void QtChatWindow::setSubject(const std::string& subject) {
//subject_->setVisible(!subject.empty());
subject_->setText(P2QSTRING(subject));
}
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());
if (result == changeSubject) {
bool ok;
QString subject = QInputDialog::getText(this, tr("Change room subject"), tr("New subject:"), QLineEdit::Normal, subject_->text(), &ok);
if (ok) {
onChangeSubjectRequest(Q2PSTRING(subject));
}
}
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();
}
else if (result == invite) {
bool ok;
QString jid = QInputDialog::getText(this, tr("Enter person's address"), tr("Address:"), QLineEdit::Normal, "", &ok);
if (ok) {
onInvitePersonToThisMUCRequest(JID(Q2PSTRING(jid)), "");
}
}
}
+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) {
bool accepted = false;
QMessageBox msgBox;
msgBox.setText(QString("You have been invited to the room %1 by %2.").arg(P2QSTRING(jid.toString())).arg(contact_));
QString reasonString;
if (!reason.empty()) {
reasonString = QString("\"%1\"").arg(P2QSTRING(reason));
}
msgBox.setInformativeText(QString("Accept invitation from %1 to enter %2?\n%3").arg(contact_).arg(P2QSTRING(jid.toString())).arg(reasonString));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::Yes);
int ret = msgBox.exec();
switch (ret) {
case QMessageBox::Yes:
accepted = true;
break;
default:
break;
}
if (accepted) {
eventStream_->send(boost::make_shared<JoinMUCUIEvent>(jid));
}
}
}
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
@@ -1,158 +1,162 @@
/*
* Copyright (c) 2010-2011 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
#include <Swift/QtUI/QtMUCConfigurationWindow.h>
+#include <Swift/QtUI/QtAffiliationEditor.h>
#include <QtTabbable.h>
#include <SwifTools/LastLineTracker.h>
#include <Swiften/Base/IDGenerator.h>
#include <map>
#include <QPointer>
class QTextEdit;
class QLineEdit;
class QComboBox;
class QLabel;
class QSplitter;
class QPushButton;
namespace Swift {
class QtChatView;
class QtOccupantListWidget;
class QtChatTheme;
class TreeWidget;
class QtTextEdit;
class UIEventStream;
class QtFileTransferJSBridge;
class QtChatWindow : public QtTabbable, public ChatWindow {
Q_OBJECT
public:
QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream);
~QtChatWindow();
std::string addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time);
std::string addAction(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time);
void addSystemMessage(const std::string& message);
void addPresenceMessage(const std::string& message);
void addErrorMessage(const std::string& errorMessage);
void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time);
// File transfer related stuff
std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes);
void setFileTransferProgress(std::string id, const int percentageDone);
void setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg);
void show();
void activate();
void setUnreadMessageCount(int count);
void convertToMUC();
// TreeWidget *getTreeWidget();
void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels);
void setSecurityLabelsEnabled(bool enabled);
void setSecurityLabelsError();
SecurityLabelsCatalog::Item getSelectedSecurityLabel();
void setName(const std::string& name);
void setInputEnabled(bool enabled);
QtTabbable::AlertType getWidgetAlertState();
void setContactChatState(ChatState::ChatStateType state);
void setRosterModel(Roster* roster);
void setTabComplete(TabComplete* completer);
int getCount();
void replaceLastMessage(const std::string& message);
void setAckState(const std::string& id, AckState state);
void flash();
QByteArray getSplitterState();
virtual void setAvailableOccupantActions(const std::vector<OccupantAction>& actions);
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);
void handleFontResized(int fontSizeSteps);
void setAlert(const std::string& alertText, const std::string& buttonText = "");
void cancelAlert();
void setCorrectionEnabled(Tristate enabled);
signals:
void geometryChanged();
void splitterMoved();
void fontResized(int);
protected slots:
void qAppFocusChanged(QWidget* old, QWidget* now);
void closeEvent(QCloseEvent* event);
void resizeEvent(QResizeEvent* event);
void moveEvent(QMoveEvent* event);
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
protected:
void showEvent(QShowEvent* event);
private slots:
void returnPressed();
void handleInputChanged();
void handleKeyPressEvent(QKeyEvent* event);
void handleSplitterMoved(int pos, int index);
void handleAlertButtonClicked();
void handleActionButtonClicked();
void handleFileTransferCancel(QString id);
void handleFileTransferSetDescription(QString id);
void handleFileTransferStart(QString id);
void handleFileTransferAccept(QString id, QString filename);
+ void handleAffiliationEditorAccepted();
private:
void updateTitleWithUnreadCount();
void tabComplete();
void beginCorrection();
void cancelCorrection();
std::string addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time);
void handleOccupantSelectionChanged(RosterItem* item);
int unreadCount_;
bool contactIsTyping_;
LastLineTracker lastLineTracker_;
QString contact_;
QString lastSentMessage_;
QtChatView* messageLog_;
QtChatTheme* theme_;
QtTextEdit* input_;
QComboBox* labelsWidget_;
QtOccupantListWidget* treeWidget_;
QLabel* correctingLabel_;
QLabel* alertLabel_;
QWidget* alertWidget_;
QPushButton* alertButton_;
TabComplete* completer_;
QLineEdit* subject_;
QPushButton* actionButton_;
std::vector<SecurityLabelsCatalog::Item> availableLabels_;
bool isCorrection_;
bool previousMessageWasSelf_;
bool previousMessageWasSystem_;
bool previousMessageWasPresence_;
bool previousMessageWasFileTransfer_;
QString previousSenderName_;
bool inputClearing_;
UIEventStream* eventStream_;
bool inputEnabled_;
IDGenerator id_;
QSplitter *logRosterSplitter_;
Tristate correctionEnabled_;
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
@@ -13,51 +13,52 @@
#include <QInputDialog>
#include "Swift/Controllers/Roster/ContactRosterItem.h"
#include "Swift/Controllers/Roster/GroupRosterItem.h"
#include "Swift/Controllers/UIEvents/UIEventStream.h"
#include "QtSwiftUtil.h"
namespace Swift {
QtOccupantListWidget::QtOccupantListWidget(UIEventStream* eventStream, QWidget* parent) : QtTreeWidget(eventStream, parent) {
}
QtOccupantListWidget::~QtOccupantListWidget() {
}
void QtOccupantListWidget::setAvailableOccupantActions(const std::vector<ChatWindow::OccupantAction>& actions) {
availableOccupantActions_ = actions;
}
void QtOccupantListWidget::contextMenuEvent(QContextMenuEvent* event) {
QModelIndex index = indexAt(event->pos());
if (!index.isValid()) {
return;
}
RosterItem* item = static_cast<RosterItem*>(index.internalPointer());
ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item);
if (contact) {
QMenu contextMenu;
std::map<QAction*, ChatWindow::OccupantAction> actions;
foreach (ChatWindow::OccupantAction availableAction, availableOccupantActions_) {
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;
}
QAction* action = contextMenu.addAction(text);
actions[action] = availableAction;
}
QAction* result = contextMenu.exec(event->globalPos());
if (result) {
onOccupantActionSelected(actions[result], contact);
}
}
}
}
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 82e4490..71dc59f 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -52,159 +52,161 @@ myenv.Append(CPPPATH = ["."])
if env["PLATFORM"] == "win32" :
#myenv["LINKFLAGS"] = ["/SUBSYSTEM:CONSOLE"]
myenv.Append(LINKFLAGS = ["/SUBSYSTEM:WINDOWS"])
myenv.Append(LIBS = "qtmain")
myenv.WriteVal("DefaultTheme.qrc", myenv.Value(generateDefaultTheme(myenv.Dir("#/Swift/resources/themes/Default"))))
sources = [
"main.cpp",
"QtAboutWidget.cpp",
"QtAvatarWidget.cpp",
"QtUIFactory.cpp",
"QtChatWindowFactory.cpp",
"QtChatWindow.cpp",
"QtClickableLabel.cpp",
"QtLoginWindow.cpp",
"QtMainWindow.cpp",
"QtProfileWindow.cpp",
"QtNameWidget.cpp",
"QtSettingsProvider.cpp",
"QtStatusWidget.cpp",
"QtScaledAvatarCache.cpp",
"QtSwift.cpp",
"QtURIHandler.cpp",
"QtChatView.cpp",
"QtChatTheme.cpp",
"QtChatTabs.cpp",
"QtSoundPlayer.cpp",
"QtSystemTray.cpp",
"QtCachedImageScaler.cpp",
"QtTabbable.cpp",
"QtTabWidget.cpp",
"QtTextEdit.cpp",
"QtXMLConsoleWidget.cpp",
- "QtFileTransferListWidget.cpp",
- "QtFileTransferListItemModel.cpp",
+ "QtFileTransferListWidget.cpp",
+ "QtFileTransferListItemModel.cpp",
"QtAdHocCommandWindow.cpp",
"QtUtilities.cpp",
"QtBookmarkDetailWindow.cpp",
"QtAddBookmarkWindow.cpp",
"QtEditBookmarkWindow.cpp",
"QtContactEditWindow.cpp",
"QtContactEditWidget.cpp",
"ChatSnippet.cpp",
"MessageSnippet.cpp",
"SystemMessageSnippet.cpp",
"QtElidingLabel.cpp",
"QtFormWidget.cpp",
"QtLineEdit.cpp",
"QtJoinMUCWindow.cpp",
"Roster/RosterModel.cpp",
"Roster/QtTreeWidget.cpp",
# "Roster/QtTreeWidgetItem.cpp",
"Roster/RosterDelegate.cpp",
"Roster/GroupItemDelegate.cpp",
"Roster/DelegateCommons.cpp",
"Roster/QtRosterWidget.cpp",
"Roster/QtOccupantListWidget.cpp",
"EventViewer/EventModel.cpp",
"EventViewer/EventDelegate.cpp",
"EventViewer/TwoLineDelegate.cpp",
"EventViewer/QtEventWindow.cpp",
"EventViewer/QtEvent.cpp",
"ChatList/QtChatListWindow.cpp",
"ChatList/ChatListModel.cpp",
"ChatList/ChatListDelegate.cpp",
"ChatList/ChatListMUCItem.cpp",
"ChatList/ChatListRecentItem.cpp",
"MUCSearch/QtMUCSearchWindow.cpp",
"MUCSearch/MUCSearchModel.cpp",
"MUCSearch/MUCSearchRoomItem.cpp",
"MUCSearch/MUCSearchEmptyItem.cpp",
"MUCSearch/MUCSearchDelegate.cpp",
"UserSearch/QtUserSearchFirstPage.cpp",
"UserSearch/QtUserSearchFieldsPage.cpp",
"UserSearch/QtUserSearchResultsPage.cpp",
"UserSearch/QtUserSearchDetailsPage.cpp",
"UserSearch/QtUserSearchWindow.cpp",
"UserSearch/UserSearchModel.cpp",
"UserSearch/UserSearchDelegate.cpp",
"QtSubscriptionRequestWindow.cpp",
"QtRosterHeader.cpp",
"QtWebView.cpp",
"qrc_DefaultTheme.cc",
"qrc_Swift.cc",
"QtFileTransferJSBridge.cpp",
"QtMUCConfigurationWindow.cpp",
+ "QtAffiliationEditor.cpp",
]
myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")
if env["PLATFORM"] == "win32" :
res = myenv.RES("#/Swift/resources/Windows/Swift.rc")
# For some reason, SCons isn't picking up the dependency correctly
# Adding it explicitly until i figure out why
myenv.Depends(res, "../Controllers/BuildVersion.h")
sources += [
"WindowsNotifier.cpp",
"#/Swift/resources/Windows/Swift.res"
]
if env["PLATFORM"] == "posix" :
sources += [
"FreeDesktopNotifier.cpp",
"QtDBUSURIHandler.cpp",
]
if env["PLATFORM"] == "darwin" or env["PLATFORM"] == "win32" :
swiftProgram = myenv.Program("Swift", sources)
else :
swiftProgram = myenv.Program("swift", sources)
if env["PLATFORM"] != "darwin" and env["PLATFORM"] != "win32" :
openURIProgram = myenv.Program("swift-open-uri", "swift-open-uri.cpp")
else :
openURIProgram = []
myenv.Uic4("MUCSearch/QtMUCSearchWindow.ui")
myenv.Uic4("UserSearch/QtUserSearchWizard.ui")
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")
# Resources
commonResources = {
"": ["#/Swift/resources/sounds"]
}
################################################################################
# Translation
################################################################################
# Collect available languages
translation_languages = []
for file in os.listdir(Dir("#/Swift/Translations").abspath) :
if file.startswith("swift_") and file.endswith(".ts") :
translation_languages.append(file[6:-3])
# Generate translation modules
translation_sources = [env.File("#/Swift/Translations/swift.ts").abspath]
translation_modules = []
for lang in translation_languages :
translation_resource = "#/Swift/resources/translations/swift_" + lang + ".qm"
translation_source = "#/Swift/Translations/swift_" + lang + ".ts"
translation_sources.append(env.File(translation_source).abspath)
translation_modules.append(env.File(translation_resource).abspath)
myenv.Qm(translation_resource, translation_source)
commonResources["translations"] = commonResources.get("translations", []) + [translation_resource]
# LUpdate translation (if requested)
if ARGUMENTS.get("update_translations", False) :
myenv.Precious(translation_sources)
remove_obsolete_option = ""
if ARGUMENTS.get("remove_obsolete_translations", False) :
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
@@ -209,88 +209,136 @@ void MUC::handleIncomingPresence(Presence::ref presence) {
else {
MUCOwnerPayload::ref mucPayload(new MUCOwnerPayload());
presenceSender->addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::DontSendPresence);
mucPayload->setPayload(boost::make_shared<Form>(Form::SubmitType));
GenericRequest<MUCOwnerPayload>* request = new GenericRequest<MUCOwnerPayload>(IQ::Set, getJID(), mucPayload, iqRouter_);
request->onResponse.connect(boost::bind(&MUC::handleCreationConfigResponse, this, _1, _2));
request->send();
}
}
}
}
}
void MUC::handleCreationConfigResponse(MUCOwnerPayload::ref /*unused*/, ErrorPayload::ref error) {
unlocking = false;
if (error) {
presenceSender->removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::AndSendPresence);
onJoinFailed(error);
} else {
onJoinComplete(getOwnNick()); /* Previously, this wasn't needed here, as the presence duplication bug caused an emit elsewhere. */
}
}
bool MUC::hasOccupant(const std::string& nick) {
return occupants.find(nick) != occupants.end();
}
MUCOccupant MUC::getOccupant(const std::string& nick) {
return occupants.find(nick)->second;
}
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;
item.role = role;
item.nick = jid.getResource();
mucPayload->addItem(item);
GenericRequest<MUCAdminPayload>* request = new GenericRequest<MUCAdminPayload>(IQ::Set, getJID(), mucPayload, iqRouter_);
request->onResponse.connect(boost::bind(&MUC::handleOccupantRoleChangeResponse, this, _1, _2, jid, role));
request->send();
}
void MUC::handleOccupantRoleChangeResponse(MUCAdminPayload::ref /*unused*/, ErrorPayload::ref error, const JID& jid, MUCOccupant::Role role) {
if (error) {
onRoleChangeFailed(error, jid, role);
}
}
+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);
message->setType(Message::Groupchat);
message->setTo(ownMUCJID.toBare());
stanzaChannel->sendMessage(message);
}
void MUC::requestConfigurationForm() {
MUCOwnerPayload::ref mucPayload(new MUCOwnerPayload());
GenericRequest<MUCOwnerPayload>* request = new GenericRequest<MUCOwnerPayload>(IQ::Get, getJID(), mucPayload, iqRouter_);
request->onResponse.connect(boost::bind(&MUC::handleConfigurationFormReceived, this, _1, _2));
request->send();
}
void MUC::cancelConfigureRoom() {
MUCOwnerPayload::ref mucPayload(new MUCOwnerPayload());
mucPayload->setPayload(boost::make_shared<Form>(Form::CancelType));
GenericRequest<MUCOwnerPayload>* request = new GenericRequest<MUCOwnerPayload>(IQ::Set, getJID(), mucPayload, iqRouter_);
request->send();
}
void MUC::handleConfigurationFormReceived(MUCOwnerPayload::ref payload, ErrorPayload::ref error) {
Form::ref form;
if (payload) {
form = payload->getForm();
}
if (error || !form) {
onConfigurationFailed(error);
} else {
onConfigurationFormReceived(form);
}
}
void MUC::handleConfigurationResultReceived(MUCOwnerPayload::ref /*payload*/, ErrorPayload::ref error) {
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
@@ -26,92 +26,100 @@ namespace Swift {
class StanzaChannel;
class IQRouter;
class DirectedPresenceSender;
class MUC {
public:
typedef boost::shared_ptr<MUC> ref;
enum JoinResult { JoinSucceeded, JoinFailed };
enum LeavingType { Part, Disconnect };
public:
MUC(StanzaChannel* stanzaChannel, IQRouter* iqRouter, DirectedPresenceSender* presenceSender, const JID &muc, MUCRegistry* mucRegistry);
/**
* Returns the (bare) JID of the MUC.
*/
JID getJID() const {
return ownMUCJID.toBare();
}
void joinAs(const std::string &nick);
void joinWithContextSince(const std::string &nick, const boost::posix_time::ptime& since);
/*void queryRoomInfo(); */
/*void queryRoomItems(); */
std::string getCurrentNick();
void part();
void handleIncomingMessage(Message::ref message);
/** Expose public so it can be called when e.g. user goes offline */
void handleUserLeft(LeavingType);
/** Get occupant information*/
MUCOccupant getOccupant(const std::string& nick);
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);
void cancelConfigureRoom();
void destroyRoom();
/** Send an invite for the person to join the MUC */
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; */
private:
bool isFromMUC(const JID& j) const {
return ownMUCJID.equals(j, JID::WithoutResource);
}
const std::string& getOwnNick() const {
return ownMUCJID.getResource();
}
private:
void handleIncomingPresence(Presence::ref presence);
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);
private:
JID ownMUCJID;
StanzaChannel* stanzaChannel;
IQRouter* iqRouter_;
DirectedPresenceSender* presenceSender;
MUCRegistry* mucRegistry;
std::map<std::string, MUCOccupant> occupants;
bool joinSucceeded_;
bool joinComplete_;
boost::bsignals::scoped_connection scopedConnection_;
boost::posix_time::ptime joinSince_;
bool createAsReservedIfNew;
bool unlocking;
boost::optional<std::string> password;
};
}