diff options
Diffstat (limited to 'Swift')
| -rw-r--r-- | Swift/Controllers/Chat/MUCController.cpp | 31 | ||||
| -rw-r--r-- | Swift/Controllers/UIInterfaces/ChatWindow.h | 3 | ||||
| -rw-r--r-- | Swift/Controllers/UnitTest/MockChatWindow.h | 2 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatWindow.cpp | 7 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatWindow.h | 2 | ||||
| -rw-r--r-- | Swift/QtUI/QtHistoryWindow.cpp | 2 | ||||
| -rw-r--r-- | Swift/QtUI/Roster/QtOccupantListWidget.cpp | 15 | ||||
| -rw-r--r-- | Swift/QtUI/Roster/QtOccupantListWidget.h | 11 | ||||
| -rw-r--r-- | Swift/QtUI/Roster/QtRosterWidget.cpp | 2 | ||||
| -rw-r--r-- | Swift/QtUI/Roster/QtTreeWidget.cpp | 30 | ||||
| -rw-r--r-- | Swift/QtUI/Roster/QtTreeWidget.h | 22 | ||||
| -rw-r--r-- | Swift/QtUI/Roster/RosterModel.cpp | 12 | ||||
| -rw-r--r-- | Swift/QtUI/Roster/RosterModel.h | 6 |
13 files changed, 98 insertions, 47 deletions
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp index 14d1767..f83a772 100644 --- a/Swift/Controllers/Chat/MUCController.cpp +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -72,121 +72,121 @@ MUCController::MUCController ( MUCRegistry* mucRegistry, HighlightManager* highlightManager, ChatMessageParser* chatMessageParser, bool isImpromptu, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, VCardManager* vcardManager) : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider, historyController, mucRegistry, highlightManager, chatMessageParser, autoAcceptMUCInviteDecider), muc_(muc), nick_(nick), desiredNick_(nick), password_(password), renameCounter_(0), isImpromptu_(isImpromptu), isImpromptuAlreadyConfigured_(false) { parting_ = true; joined_ = false; lastWasPresence_ = false; shouldJoinOnReconnect_ = true; doneGettingHistory_ = false; events_ = uiEventStream; xmppRoster_ = roster; roster_ = new Roster(false, true); rosterVCardProvider_ = new RosterVCardProvider(roster_, vcardManager, JID::WithResource); completer_ = new TabComplete(); chatWindow_->setRosterModel(roster_); chatWindow_->setTabComplete(completer_); 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_->onInviteToChat.connect(boost::bind(&MUCController::handleInvitePersonToThisMUCRequest, this, _1)); 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_->onOccupantAffiliationChanged.connect(boost::bind(&MUCController::handleOccupantAffiliationChanged, this, _1, _2, _3)); muc_->onRoleChangeFailed.connect(boost::bind(&MUCController::handleOccupantRoleChangeFailed, this, _1, _2, _3)); muc_->onAffiliationListReceived.connect(boost::bind(&MUCController::handleAffiliationListReceived, this, _1, _2)); muc_->onConfigurationFailed.connect(boost::bind(&MUCController::handleConfigurationFailed, this, _1)); muc_->onConfigurationFormReceived.connect(boost::bind(&MUCController::handleConfigurationFormReceived, this, _1)); highlighter_->setMode(Highlighter::MUCMode); highlighter_->setNick(nick_); if (timerFactory) { loginCheckTimer_ = boost::shared_ptr<Timer>(timerFactory->createTimer(MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS)); loginCheckTimer_->onTick.connect(boost::bind(&MUCController::handleJoinTimeoutTick, this)); loginCheckTimer_->start(); } if (isImpromptu) { muc_->onUnlocked.connect(boost::bind(&MUCController::handleRoomUnlocked, this)); - chatWindow_->convertToMUC(true); + chatWindow_->convertToMUC(ChatWindow::ImpromptuMUC); } else { - chatWindow_->convertToMUC(); + muc_->onOccupantRoleChanged.connect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3)); + muc_->onOccupantAffiliationChanged.connect(boost::bind(&MUCController::handleOccupantAffiliationChanged, this, _1, _2, _3)); + chatWindow_->convertToMUC(ChatWindow::StandardMUC); chatWindow_->setName(muc->getJID().getNode()); } setOnline(true); if (avatarManager_ != NULL) { avatarChangedConnection_ = (avatarManager_->onAvatarChanged.connect(boost::bind(&MUCController::handleAvatarChanged, this, _1))); } handleBareJIDCapsChanged(muc->getJID()); eventStream_->onUIEvent.connect(boost::bind(&MUCController::handleUIEvent, this, _1)); } MUCController::~MUCController() { eventStream_->onUIEvent.disconnect(boost::bind(&MUCController::handleUIEvent, this, _1)); chatWindow_->setRosterModel(NULL); delete rosterVCardProvider_; delete roster_; if (loginCheckTimer_) { loginCheckTimer_->stop(); } chatWindow_->setTabComplete(NULL); delete completer_; } void MUCController::cancelReplaces() { lastWasPresence_ = false; } void MUCController::handleWindowOccupantSelectionChanged(ContactRosterItem* item) { std::vector<ChatWindow::OccupantAction> actions; if (item) { MUCOccupant::Affiliation affiliation = muc_->getOccupant(getNick()).getAffiliation(); MUCOccupant::Role role = muc_->getOccupant(getNick()).getRole(); - if (role == MUCOccupant::Moderator) + if (role == MUCOccupant::Moderator && !isImpromptu_) { if (affiliation == MUCOccupant::Admin || affiliation == MUCOccupant::Owner) { actions.push_back(ChatWindow::Ban); } actions.push_back(ChatWindow::Kick); actions.push_back(ChatWindow::MakeModerator); actions.push_back(ChatWindow::MakeParticipant); actions.push_back(ChatWindow::MakeVisitor); } // Add contact is available only if the real JID is also available if (muc_->getOccupant(item->getJID().getResource()).getRealJID()) { actions.push_back(ChatWindow::AddContact); } actions.push_back(ChatWindow::ShowProfile); } chatWindow_->setAvailableOccupantActions(actions); } void MUCController::handleActionRequestedOnOccupant(ChatWindow::OccupantAction action, ContactRosterItem* item) { JID mucJID = item->getJID(); MUCOccupant occupant = muc_->getOccupant(mucJID.getResource()); JID realJID; if (occupant.getRealJID()) { realJID = occupant.getRealJID().get(); } switch (action) { case ChatWindow::Kick: muc_->kickOccupant(mucJID);break; case ChatWindow::Ban: muc_->changeAffiliation(realJID, MUCOccupant::Outcast);break; case ChatWindow::MakeModerator: muc_->changeOccupantRole(mucJID, MUCOccupant::Moderator);break; case ChatWindow::MakeParticipant: muc_->changeOccupantRole(mucJID, MUCOccupant::Participant);break; case ChatWindow::MakeVisitor: muc_->changeOccupantRole(mucJID, MUCOccupant::Visitor);break; case ChatWindow::AddContact: if (occupant.getRealJID()) events_->send(boost::make_shared<RequestAddUserDialogUIEvent>(realJID, occupant.getNick()));break; case ChatWindow::ShowProfile: events_->send(boost::make_shared<ShowProfileForRosterItemUIEvent>(mucJID));break; } @@ -316,113 +316,122 @@ void MUCController::handleJoinFailed(boost::shared_ptr<ErrorPayload> error) { default: break; } } errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't join room: %1%.")) % errorMessage); chatWindow_->addErrorMessage(chatMessageParser_->parseMessageBody(errorMessage)); parting_ = true; if (!rejoinNick.empty() && renameCounter_ < 10) { renameCounter_++; setNick(rejoinNick); rejoin(); } } #pragma clang diagnostic pop void MUCController::handleJoinComplete(const std::string& nick) { receivedActivity(); renameCounter_ = 0; joined_ = true; std::string joinMessage; if (isImpromptu_) { joinMessage = str(format(QT_TRANSLATE_NOOP("", "You have entered chat as %1%.")) % nick); } else { joinMessage = str(format(QT_TRANSLATE_NOOP("", "You have entered room %1% as %2%.")) % toJID_.toString() % nick); } setNick(nick); chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(joinMessage), ChatWindow::DefaultDirection); #ifdef SWIFT_EXPERIMENTAL_HISTORY addRecentLogs(); #endif clearPresenceQueue(); shouldJoinOnReconnect_ = true; setEnabled(true); - MUCOccupant occupant = muc_->getOccupant(nick); - setAvailableRoomActions(occupant.getAffiliation(), occupant.getRole()); + if (isImpromptu_) { + setAvailableRoomActions(MUCOccupant::NoAffiliation, MUCOccupant::Participant); + } else { + MUCOccupant occupant = muc_->getOccupant(nick); + setAvailableRoomActions(occupant.getAffiliation(), occupant.getRole()); + } onUserJoined(); if (isImpromptu_) { setImpromptuWindowTitle(); } } void MUCController::handleAvatarChanged(const JID& jid) { if (parting_ || !jid.equals(toJID_, JID::WithoutResource)) { return; } roster_->applyOnItems(SetAvatar(jid, avatarManager_->getAvatarPath(jid), JID::WithResource)); } void MUCController::handleWindowClosed() { parting_ = true; shouldJoinOnReconnect_ = false; muc_->part(); onUserLeft(); } void MUCController::handleOccupantJoined(const MUCOccupant& occupant) { if (nick_ != occupant.getNick()) { completer_->addWord(occupant.getNick()); } receivedActivity(); JID jid(nickToJID(occupant.getNick())); JID realJID; if (occupant.getRealJID()) { realJID = occupant.getRealJID().get(); } currentOccupants_.insert(occupant.getNick()); NickJoinPart event(occupant.getNick(), Join); appendToJoinParts(joinParts_, event); - std::string groupName(roleToGroupName(occupant.getRole())); + MUCOccupant::Role role = MUCOccupant::Participant; + MUCOccupant::Affiliation affiliation = MUCOccupant::NoAffiliation; + if (!isImpromptu_) { + role = occupant.getRole(); + affiliation = occupant.getAffiliation(); + } + std::string groupName(roleToGroupName(role)); roster_->addContact(jid, realJID, occupant.getNick(), groupName, avatarManager_->getAvatarPath(jid)); - roster_->applyOnItems(SetMUC(jid, occupant.getRole(), occupant.getAffiliation())); - roster_->getGroup(groupName)->setManualSort(roleToSortName(occupant.getRole())); + roster_->applyOnItems(SetMUC(jid, role, affiliation)); + roster_->getGroup(groupName)->setManualSort(roleToSortName(role)); if (joined_) { std::string joinString; - MUCOccupant::Role role = occupant.getRole(); if (role != MUCOccupant::NoRole && role != MUCOccupant::Participant) { joinString = str(format(QT_TRANSLATE_NOOP("", "%1% has entered the %3% as a %2%.")) % occupant.getNick() % roleToFriendlyName(role) % (isImpromptu_ ? QT_TRANSLATE_NOOP("", "chat") : QT_TRANSLATE_NOOP("", "room"))); } else { joinString = str(format(QT_TRANSLATE_NOOP("", "%1% has entered the %2%.")) % occupant.getNick() % (isImpromptu_ ? QT_TRANSLATE_NOOP("", "chat") : QT_TRANSLATE_NOOP("", "room"))); } if (shouldUpdateJoinParts()) { updateJoinParts(); } else { addPresenceMessage(joinString); } if (isImpromptu_) { setImpromptuWindowTitle(); onActivity(""); } } if (avatarManager_ != NULL) { handleAvatarChanged(jid); } } void MUCController::addPresenceMessage(const std::string& message) { lastWasPresence_ = true; chatWindow_->addPresenceMessage(chatMessageParser_->parseMessageBody(message), ChatWindow::DefaultDirection); } void MUCController::setAvailableRoomActions(const MUCOccupant::Affiliation& affiliation, const MUCOccupant::Role& role) { std::vector<ChatWindow::RoomAction> actions; if (role <= MUCOccupant::Participant) { actions.push_back(ChatWindow::ChangeSubject); } diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h index 0f0062d..771872a 100644 --- a/Swift/Controllers/UIInterfaces/ChatWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatWindow.h @@ -60,111 +60,112 @@ namespace Swift { class ChatTextMessagePart : public ChatMessagePart { public: ChatTextMessagePart(const std::string& text) : text(text) {} std::string text; }; class ChatURIMessagePart : public ChatMessagePart { public: ChatURIMessagePart(const std::string& target) : target(target) {} std::string target; }; class ChatEmoticonMessagePart : public ChatMessagePart { public: std::string imagePath; std::string alternativeText; }; class ChatHighlightingMessagePart : public ChatMessagePart { public: std::string foregroundColor; std::string backgroundColor; std::string text; }; enum AckState {Pending, Received, Failed}; enum ReceiptState {ReceiptRequested, ReceiptReceived, ReceiptFailed}; enum Tristate {Yes, No, Maybe}; enum OccupantAction {Kick, Ban, MakeModerator, MakeParticipant, MakeVisitor, AddContact, ShowProfile}; enum RoomAction {ChangeSubject, Configure, Affiliations, Destroy, Invite}; enum FileTransferState {WaitingForAccept, Negotiating, Transferring, Canceled, Finished, FTFailed}; enum WhiteboardSessionState {WhiteboardAccepted, WhiteboardTerminated, WhiteboardRejected}; enum BlockingState {BlockingUnsupported, IsBlocked, IsUnblocked}; enum Direction { UnknownDirection, DefaultDirection }; + enum MUCType { StandardMUC, ImpromptuMUC }; ChatWindow() {} virtual ~ChatWindow() {} /** Add message to window. * @return id of added message (for acks). */ virtual std::string addMessage(const ChatMessage& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) = 0; /** Adds action to window. * @return id of added message (for acks); */ virtual std::string addAction(const ChatMessage& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) = 0; virtual void addSystemMessage(const ChatMessage& message, Direction direction) = 0; virtual void addPresenceMessage(const ChatMessage& message, Direction direction) = 0; virtual void addErrorMessage(const ChatMessage& message) = 0; virtual void replaceMessage(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) = 0; virtual void replaceWithAction(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) = 0; // File transfer related stuff virtual std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) = 0; 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 std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true, bool isImpromptu = false, bool isContinuation = false) = 0; virtual std::string addWhiteboardRequest(bool senderIsSelf) = 0; virtual void setWhiteboardSessionStatus(std::string id, const ChatWindow::WhiteboardSessionState state) = 0; // message receipts virtual void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState state) = 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(bool impromptuMUC = false) = 0; + virtual void convertToMUC(MUCType mucType) = 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 ChatMessage& message) = 0; virtual void setAckState(const std::string& id, AckState state) = 0; virtual void flash() = 0; virtual void setSubject(const std::string& subject) = 0; virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&) = 0; virtual void setAvailableRoomActions(const std::vector<RoomAction> &actions) = 0; virtual void setBlockingState(BlockingState state) = 0; virtual void setCanInitiateImpromptuChats(bool supportsImpromptu) = 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; diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h index 59ed0f1..8aa645d 100644 --- a/Swift/Controllers/UnitTest/MockChatWindow.h +++ b/Swift/Controllers/UnitTest/MockChatWindow.h @@ -13,71 +13,71 @@ namespace Swift { class MockChatWindow : public ChatWindow { public: MockChatWindow() : labelsEnabled_(false) {} virtual ~MockChatWindow(); virtual std::string addMessage(const ChatMessage& message, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) { lastMessageBody_ = bodyFromMessage(message); return "id";} virtual std::string addAction(const ChatMessage& /*message*/, const std::string& /*senderName*/, bool /*senderIsSelf*/, boost::shared_ptr<SecurityLabel> /*label*/, const std::string& /*avatarPath*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {return "id";} virtual void addSystemMessage(const ChatMessage& /*message*/, Direction /*direction*/) {} virtual void addPresenceMessage(const ChatMessage& /*message*/, Direction /*direction*/) {} virtual void addErrorMessage(const ChatMessage& /*message*/) {} virtual void replaceMessage(const ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {} virtual void replaceWithAction(const ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/, const HighlightAction& /*highlight*/) {} virtual void replaceLastMessage(const ChatMessage& /*message*/) {} // File transfer related stuff virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/,const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/) { return 0; } virtual void setFileTransferProgress(std::string /*id*/, const int /*alreadyTransferedBytes*/) { } virtual void setFileTransferStatus(std::string /*id*/, const FileTransferState /*state*/, const std::string& /*msg*/) { } virtual void setMessageReceiptState(const std::string &/* id */, ReceiptState /* state */) { } virtual void setContactChatState(ChatState::ChatStateType /*state*/) {} virtual void setName(const std::string& name) {name_ = name;} virtual void show() {} virtual void activate() {} virtual void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) {labels_ = labels;} virtual void setSecurityLabelsEnabled(bool enabled) {labelsEnabled_ = enabled;} virtual void setUnreadMessageCount(int /*count*/) {} - virtual void convertToMUC(bool /*impromptuMUC*/) {} + virtual void convertToMUC(MUCType /*mucType*/) {} virtual void setSecurityLabelsError() {} virtual SecurityLabelsCatalog::Item getSelectedSecurityLabel() {return label_;} virtual void setInputEnabled(bool /*enabled*/) {} virtual void setRosterModel(Roster* roster) { roster_ = roster; } Roster* getRosterModel() { return roster_; } virtual void setTabComplete(TabComplete*) {} void setAckState(const std::string& /*id*/, AckState /*state*/) {} virtual void flash() {} virtual void setAlert(const std::string& /*alertText*/, const std::string& /*buttonText*/) {} virtual void cancelAlert() {} virtual void setCorrectionEnabled(Tristate /*enabled*/) {} void setAvailableOccupantActions(const std::vector<OccupantAction>&/* actions*/) {} void setSubject(const std::string& /*subject*/) {} virtual void showRoomConfigurationForm(Form::ref) {} virtual void addMUCInvitation(const std::string& /*senderName*/, const JID& /*jid*/, const std::string& /*reason*/, const std::string& /*password*/, bool = true, bool = false, bool = false) {} virtual std::string addWhiteboardRequest(bool) {return "";} virtual void setWhiteboardSessionStatus(std::string, const ChatWindow::WhiteboardSessionState){} virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&) {} virtual void setAvailableRoomActions(const std::vector<RoomAction> &) {} virtual void setBlockingState(BlockingState) {} virtual void setCanInitiateImpromptuChats(bool /*supportsImpromptu*/) {} std::string bodyFromMessage(const ChatMessage& message) { boost::shared_ptr<ChatTextMessagePart> text; foreach (boost::shared_ptr<ChatMessagePart> part, message.getParts()) { if ((text = boost::dynamic_pointer_cast<ChatTextMessagePart>(part))) { return text->text; } } return ""; } diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 826ec9e..bd7c817 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -84,71 +84,71 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt 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(); subjectLayout_ = new QBoxLayout(QBoxLayout::LeftToRight); subject_ = new QLineEdit(this); subjectLayout_->addWidget(subject_); setSubject(""); subject_->setReadOnly(true); QPushButton* actionButton_ = new QPushButton(this); actionButton_->setIcon(QIcon(":/icons/actions.png")); connect(actionButton_, SIGNAL(clicked()), this, SLOT(handleActionButtonClicked())); subject_->hide(); layout->addLayout(subjectLayout_); logRosterSplitter_ = new QSplitter(this); logRosterSplitter_->setAutoFillBackground(true); layout->addWidget(logRosterSplitter_); if (settings_->getSetting(QtUISettingConstants::USE_PLAIN_CHATS) || settings_->getSetting(QtUISettingConstants::USE_SCREENREADER)) { messageLog_ = new QtPlainChatView(this, eventStream_); } else { messageLog_ = new QtWebKitChatView(this, eventStream_, theme, this); // I accept that passing the ChatWindow in so that the view can call the signals is somewhat inelegant, but it saves a lot of boilerplate. This patch is unpleasant enough already. So let's fix this soon (it at least needs fixing by the time history is sorted), but not now. } logRosterSplitter_->addWidget(messageLog_); - treeWidget_ = new QtOccupantListWidget(eventStream_, settings_, this); + treeWidget_ = new QtOccupantListWidget(eventStream_, settings_, QtTreeWidget::MessageDefaultJID, this); treeWidget_->hide(); logRosterSplitter_->addWidget(treeWidget_); logRosterSplitter_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); connect(logRosterSplitter_, SIGNAL(splitterMoved(int, int)), this, SLOT(handleSplitterMoved(int, int))); midBar_ = new QWidget(this); //layout->addWidget(midBar); midBar_->setAutoFillBackground(true); QHBoxLayout *midBarLayout = new QHBoxLayout(midBar_); midBarLayout->setContentsMargins(0,0,0,0); midBarLayout->setSpacing(2); //midBarLayout->addStretch(); labelsWidget_ = new QComboBox(this); labelsWidget_->setFocusPolicy(Qt::NoFocus); labelsWidget_->hide(); labelsWidget_->setSizeAdjustPolicy(QComboBox::AdjustToContents); midBarLayout->addWidget(labelsWidget_,0); connect(labelsWidget_, SIGNAL(currentIndexChanged(int)), this, SLOT(handleCurrentLabelChanged(int))); defaultLabelsPalette_ = labelsWidget_->palette(); QHBoxLayout* inputBarLayout = new QHBoxLayout(); inputBarLayout->setContentsMargins(0,0,0,0); inputBarLayout->setSpacing(2); input_ = new QtTextEdit(settings_, this); input_->setAcceptRichText(false); inputBarLayout->addWidget(midBar_); inputBarLayout->addWidget(input_); correctingLabel_ = new QLabel(tr("Correcting"), this); inputBarLayout->addWidget(correctingLabel_); correctingLabel_->hide(); // using an extra layout to work around Qt margin glitches on OS X QHBoxLayout* actionLayout = new QHBoxLayout(); actionLayout->addWidget(actionButton_); @@ -364,72 +364,73 @@ void QtChatWindow::handleCurrentLabelChanged(int index) { labelsWidget_->setAutoFillBackground(false); labelsWidget_->setPalette(defaultLabelsPalette_); midBar_->setPalette(defaultLabelsPalette_); } } 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 && static_cast<size_t>(labelsWidget_->currentIndex()) < labelModel_->availableLabels_.size()); return labelModel_->availableLabels_[labelsWidget_->currentIndex()]; } void QtChatWindow::closeEvent(QCloseEvent* event) { event->accept(); emit windowClosing(); onClosed(); } -void QtChatWindow::convertToMUC(bool impromptuMUC) { - impromptu_ = impromptuMUC; +void QtChatWindow::convertToMUC(MUCType mucType) { + impromptu_ = (mucType == ImpromptuMUC); + treeWidget_->setMessageTarget(impromptu_ ? QtTreeWidget::MessageDisplayJID : QtTreeWidget::MessageDefaultJID); isMUC_ = true; treeWidget_->show(); subject_->setVisible(!impromptu_); } 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) { 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) { diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index eeb8093..5a4fe95 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -67,71 +67,71 @@ namespace Swift { QString labelName = selector.empty() ? displayMarking.c_str() : selector.c_str(); return labelName; } return QVariant(); } std::vector<SecurityLabelsCatalog::Item> availableLabels_; }; class QtChatWindow : public QtTabbable, public ChatWindow { Q_OBJECT public: QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings); ~QtChatWindow(); std::string addMessage(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight); std::string addAction(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight); void addSystemMessage(const ChatMessage& message, Direction direction); void addPresenceMessage(const ChatMessage& message, Direction direction); void addErrorMessage(const ChatMessage& message); void replaceMessage(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight); void replaceWithAction(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight); // File transfer related stuff std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes); void setFileTransferProgress(std::string id, const int percentageDone); void setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg); std::string addWhiteboardRequest(bool senderIsSelf); void setWhiteboardSessionStatus(std::string id, const ChatWindow::WhiteboardSessionState state); void show(); void activate(); void setUnreadMessageCount(int count); - void convertToMUC(bool impromptuMUC = false); + void convertToMUC(MUCType mucType); // 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 ChatMessage& message); void setAckState(const std::string& id, AckState state); // message receipts void setMessageReceiptState(const std::string& id, ChatWindow::ReceiptState 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 std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true, bool isImpromptu = false, bool isContinuation = false); void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&); void setAvailableRoomActions(const std::vector<RoomAction>& actions); void setBlockingState(BlockingState state); virtual void setCanInitiateImpromptuChats(bool supportsImpromptu); 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); diff --git a/Swift/QtUI/QtHistoryWindow.cpp b/Swift/QtUI/QtHistoryWindow.cpp index 9f88258..75eeaad 100644 --- a/Swift/QtUI/QtHistoryWindow.cpp +++ b/Swift/QtUI/QtHistoryWindow.cpp @@ -28,71 +28,71 @@ #include <Swiften/History/HistoryMessage.h> #include <Swift/Controllers/Settings/SettingsProvider.h> #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/QtUI/QtSwiftUtil.h> #include <Swift/QtUI/MessageSnippet.h> #include <Swift/QtUI/QtScaledAvatarCache.h> #include <Swift/QtUI/ChatSnippet.h> #include <Swift/QtUI/QtUtilities.h> #include <Swift/QtUI/Roster/QtTreeWidget.h> #include <Swift/QtUI/QtWebKitChatView.h> namespace Swift { QtHistoryWindow::QtHistoryWindow(SettingsProvider* settings, UIEventStream* eventStream) : previousTopMessageWasSelf_(false), previousBottomMessageWasSelf_(false) { ui_.setupUi(this); theme_ = new QtChatTheme(""); idCounter_ = 0; delete ui_.conversation_; conversation_ = new QtWebKitChatView(NULL, NULL, theme_, this, true); // Horrible unsafe. Do not do this. FIXME QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(80); sizePolicy.setVerticalStretch(0); conversation_->setSizePolicy(sizePolicy); ui_.conversation_ = conversation_; ui_.bottomLayout_->addWidget(conversation_); delete ui_.conversationRoster_; - conversationRoster_ = new QtTreeWidget(eventStream, settings, this); + conversationRoster_ = new QtTreeWidget(eventStream, settings, QtTreeWidget::MessageDefaultJID, this); QSizePolicy sizePolicy2(QSizePolicy::Preferred, QSizePolicy::Expanding); sizePolicy2.setVerticalStretch(80); conversationRoster_->setSizePolicy(sizePolicy2); ui_.conversationRoster_ = conversationRoster_; ui_.bottomLeftLayout_->setDirection(QBoxLayout::BottomToTop); ui_.bottomLeftLayout_->addWidget(conversationRoster_); setWindowTitle(tr("History")); conversationRoster_->onSomethingSelectedChanged.connect(boost::bind(&QtHistoryWindow::handleSomethingSelectedChanged, this, _1)); connect(conversation_, SIGNAL(scrollRequested(int)), this, SLOT(handleScrollRequested(int))); connect(conversation_, SIGNAL(scrollReachedTop()), this, SLOT(handleScrollReachedTop())); connect(conversation_, SIGNAL(scrollReachedBottom()), this, SLOT(handleScrollReachedBottom())); connect(conversation_, SIGNAL(fontResized(int)), this, SLOT(handleFontResized(int))); connect(ui_.searchBox_->lineEdit(), SIGNAL(returnPressed()), this, SLOT(handleReturnPressed())); connect(ui_.calendarWidget_, SIGNAL(clicked(const QDate&)), this, SLOT(handleCalendarClicked(const QDate&))); connect(ui_.calendarWidget_, SIGNAL(activated(const QDate&)), this, SLOT(handleCalendarClicked(const QDate&))); connect(ui_.previousButton_, SIGNAL(clicked(bool)), this, SLOT(handlePreviousButtonClicked())); connect(ui_.nextButton_, SIGNAL(clicked(bool)), this, SLOT(handleNextButtonClicked())); } QtHistoryWindow::~QtHistoryWindow() { disconnect(conversation_, SIGNAL(scrollRequested(int)), this, SLOT(handleScrollRequested(int))); disconnect(conversation_, SIGNAL(scrollReachedTop()), this, SLOT(handleScrollReachedTop())); disconnect(conversation_, SIGNAL(scrollReachedBottom()), this, SLOT(handleScrollReachedBottom())); disconnect(conversation_, SIGNAL(fontResized(int)), this, SLOT(handleFontResized(int))); disconnect(ui_.searchBox_->lineEdit(), SIGNAL(returnPressed()), this, SLOT(handleReturnPressed())); disconnect(ui_.calendarWidget_, SIGNAL(clicked(const QDate&)), this, SLOT(handleCalendarClicked(const QDate&))); disconnect(ui_.calendarWidget_, SIGNAL(activated(const QDate&)), this, SLOT(handleCalendarClicked(const QDate&))); disconnect(ui_.previousButton_, SIGNAL(clicked(bool)), this, SLOT(handlePreviousButtonClicked())); disconnect(ui_.nextButton_, SIGNAL(clicked(bool)), this, SLOT(handleNextButtonClicked())); delete theme_; delete conversation_; // TODO: delete ui_ diff --git a/Swift/QtUI/Roster/QtOccupantListWidget.cpp b/Swift/QtUI/Roster/QtOccupantListWidget.cpp index 12dc1e4..4f1baf3 100644 --- a/Swift/QtUI/Roster/QtOccupantListWidget.cpp +++ b/Swift/QtUI/Roster/QtOccupantListWidget.cpp @@ -1,57 +1,58 @@ /* - * Copyright (c) 2011-2012 Kevin Smith + * Copyright (c) 2011-2014 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "Roster/QtOccupantListWidget.h" +#include <Swift/QtUI/Roster/QtOccupantListWidget.h> #include <QContextMenuEvent> #include <QMenu> #include <QAction> #include <QInputDialog> -#include "Swift/Controllers/Roster/ContactRosterItem.h" -#include "Swift/Controllers/Roster/GroupRosterItem.h" -#include "Swift/Controllers/UIEvents/UIEventStream.h" -#include "QtSwiftUtil.h" +#include <Swift/Controllers/Roster/ContactRosterItem.h> +#include <Swift/Controllers/Roster/GroupRosterItem.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> + +#include <Swift/QtUI/QtSwiftUtil.h> namespace Swift { -QtOccupantListWidget::QtOccupantListWidget(UIEventStream* eventStream, SettingsProvider* settings, QWidget* parent) : QtTreeWidget(eventStream, settings, parent) { +QtOccupantListWidget::QtOccupantListWidget(UIEventStream* eventStream, SettingsProvider* settings, MessageTarget privateMessageTarget, QWidget* parent) : QtTreeWidget(eventStream, settings, privateMessageTarget, 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) { onSomethingSelectedChanged(contact); QMenu contextMenu; if (availableOccupantActions_.empty()) { QAction* noAction = contextMenu.addAction(tr("No actions for this user")); noAction->setEnabled(false); contextMenu.exec(event->globalPos()); } else { 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; diff --git a/Swift/QtUI/Roster/QtOccupantListWidget.h b/Swift/QtUI/Roster/QtOccupantListWidget.h index 729115a..c944658 100644 --- a/Swift/QtUI/Roster/QtOccupantListWidget.h +++ b/Swift/QtUI/Roster/QtOccupantListWidget.h @@ -1,31 +1,32 @@ /* - * Copyright (c) 2011-2012 Kevin Smith + * Copyright (c) 2011-2014 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once -#include "Swift/QtUI/Roster/QtTreeWidget.h" +#include <Swift/QtUI/Roster/QtTreeWidget.h> -#include "Swiften/Base/boost_bsignals.h" -#include "Swift/Controllers/UIInterfaces/ChatWindow.h" +#include <Swiften/Base/boost_bsignals.h> + +#include <Swift/Controllers/UIInterfaces/ChatWindow.h> namespace Swift { class SettingsProvider; class QtOccupantListWidget : public QtTreeWidget { Q_OBJECT public: - QtOccupantListWidget(UIEventStream* eventStream, SettingsProvider* settings, QWidget* parent = 0); + QtOccupantListWidget(UIEventStream* eventStream, SettingsProvider* settings, MessageTarget privateMessageTarget, QWidget* parent = NULL); virtual ~QtOccupantListWidget(); void setAvailableOccupantActions(const std::vector<ChatWindow::OccupantAction>& actions); boost::signal<void (ChatWindow::OccupantAction, ContactRosterItem*)> onOccupantActionSelected; protected: void contextMenuEvent(QContextMenuEvent* event); private: std::vector<ChatWindow::OccupantAction> availableOccupantActions_; }; } diff --git a/Swift/QtUI/Roster/QtRosterWidget.cpp b/Swift/QtUI/Roster/QtRosterWidget.cpp index 1436c7c..fff0ccd 100644 --- a/Swift/QtUI/Roster/QtRosterWidget.cpp +++ b/Swift/QtUI/Roster/QtRosterWidget.cpp @@ -1,66 +1,66 @@ /* * Copyright (c) 2011 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include <Swift/QtUI/Roster/QtRosterWidget.h> #include <QContextMenuEvent> #include <QMenu> #include <QMessageBox> #include <QInputDialog> #include <QFileDialog> #include <QPushButton> #include <Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h> #include <Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h> #include <Swift/Controllers/UIEvents/RenameGroupUIEvent.h> #include <Swift/Controllers/UIEvents/SendFileUIEvent.h> #include <Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h> #include <Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h> #include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h> #include <Swift/QtUI/QtContactEditWindow.h> #include <Swift/Controllers/Roster/ContactRosterItem.h> #include <Swift/Controllers/Roster/GroupRosterItem.h> #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/QtUI/QtSwiftUtil.h> namespace Swift { -QtRosterWidget::QtRosterWidget(UIEventStream* eventStream, SettingsProvider* settings, QWidget* parent) : QtTreeWidget(eventStream, settings, parent) { +QtRosterWidget::QtRosterWidget(UIEventStream* eventStream, SettingsProvider* settings, QWidget* parent) : QtTreeWidget(eventStream, settings, MessageDefaultJID, parent) { } QtRosterWidget::~QtRosterWidget() { } void QtRosterWidget::handleEditUserActionTriggered(bool /*checked*/) { QModelIndexList selectedIndexList = getSelectedIndexes(); if (selectedIndexList.empty()) { return; } QModelIndex index = selectedIndexList[0]; if (!index.isValid()) { return; } RosterItem* item = static_cast<RosterItem*>(index.internalPointer()); if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) { eventStream_->send(boost::make_shared<RequestContactEditorUIEvent>(contact->getJID())); } } void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) { QModelIndex index = indexAt(event->pos()); if (!index.isValid()) { return; } RosterItem* item = static_cast<RosterItem*>(index.internalPointer()); QMenu contextMenu; if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) { QAction* editContact = contextMenu.addAction(tr("Edit…")); QAction* removeContact = contextMenu.addAction(tr("Remove")); QAction* showProfileForContact = contextMenu.addAction(tr("Show Profile")); QAction* unblockContact = NULL; diff --git a/Swift/QtUI/Roster/QtTreeWidget.cpp b/Swift/QtUI/Roster/QtTreeWidget.cpp index 91e9a33..ed76a6f 100644 --- a/Swift/QtUI/Roster/QtTreeWidget.cpp +++ b/Swift/QtUI/Roster/QtTreeWidget.cpp @@ -1,69 +1,69 @@ /* * Copyright (c) 2010-2012 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include <Swift/QtUI/Roster/QtTreeWidget.h> #include <boost/smart_ptr/make_shared.hpp> #include <boost/bind.hpp> #include <QUrl> #include <QMimeData> #include <QObject> #include <QLabel> #include <QTimer> #include <QToolTip> #include <Swiften/Base/Platform.h> #include <Swift/Controllers/Roster/ContactRosterItem.h> #include <Swift/Controllers/Roster/GroupRosterItem.h> #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/Controllers/UIEvents/RequestChatUIEvent.h> #include <Swift/Controllers/UIEvents/SendFileUIEvent.h> #include <Swift/Controllers/Settings/SettingsProvider.h> #include <Swift/QtUI/QtUISettingConstants.h> - +#include <Swift/QtUI/Roster/RosterModel.h> #include <QtSwiftUtil.h> namespace Swift { -QtTreeWidget::QtTreeWidget(UIEventStream* eventStream, SettingsProvider* settings, QWidget* parent) : QTreeView(parent), tooltipShown_(false) { +QtTreeWidget::QtTreeWidget(UIEventStream* eventStream, SettingsProvider* settings, MessageTarget messageTarget, QWidget* parent) : QTreeView(parent), messageTarget_(messageTarget), tooltipShown_(false) { eventStream_ = eventStream; settings_ = settings; model_ = new RosterModel(this, settings_->getSetting(QtUISettingConstants::USE_SCREENREADER)); setModel(model_); delegate_ = new RosterDelegate(this, settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER)); setItemDelegate(delegate_); setHeaderHidden(true); #ifdef SWIFT_PLATFORM_MACOSX setAlternatingRowColors(true); #endif setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); expandAll(); setAnimated(true); setIndentation(0); #ifdef SWIFT_EXPERIMENTAL_FT setAcceptDrops(true); #endif setDragEnabled(true); setRootIsDecorated(true); connect(this, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleItemActivated(const QModelIndex&))); connect(model_, SIGNAL(itemExpanded(const QModelIndex&, bool)), this, SLOT(handleModelItemExpanded(const QModelIndex&, bool))); connect(this, SIGNAL(expanded(const QModelIndex&)), this, SLOT(handleExpanded(const QModelIndex&))); connect(this, SIGNAL(collapsed(const QModelIndex&)), this, SLOT(handleCollapsed(const QModelIndex&))); connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(handleClicked(const QModelIndex&))); settings_->onSettingChanged.connect(boost::bind(&QtTreeWidget::handleSettingChanged, this, _1)); } QtTreeWidget::~QtTreeWidget() { settings_->onSettingChanged.disconnect(boost::bind(&QtTreeWidget::handleSettingChanged, this, _1)); delete model_; delete delegate_; } void QtTreeWidget::handleSettingChanged(const std::string& setting) { @@ -105,76 +105,88 @@ void QtTreeWidget::handleClicked(const QModelIndex& index) { } QModelIndexList QtTreeWidget::getSelectedIndexes() const { // Not using selectedIndexes(), because this seems to cause a crash in Qt (4.7.0) in the // QModelIndexList destructor. // This is a workaround posted in http://www.qtcentre.org/threads/16933 (although this case // was resolved by linking against the debug libs, ours isn't, and we're not alone) QItemSelection ranges = selectionModel()->selection(); QModelIndexList selectedIndexList; for (int i = 0; i < ranges.count(); ++i) { QModelIndex parent = ranges.at(i).parent(); int right = ranges.at(i).model()->columnCount(parent) - 1; if (ranges.at(i).left() == 0 && ranges.at(i).right() == right) { for (int r = ranges.at(i).top(); r <= ranges.at(i).bottom(); ++r) { selectedIndexList.append(ranges.at(i).model()->index(r, 0, parent)); } } } return selectedIndexList; } void QtTreeWidget::currentChanged(const QModelIndex& current, const QModelIndex& previous) { RosterItem* item = NULL; QModelIndexList selectedIndexList = getSelectedIndexes(); if (selectedIndexList.empty() || !selectedIndexList[0].isValid()) { /* I didn't quite understand why using current didn't seem to work here.*/ } else if (current.isValid()) { item = static_cast<RosterItem*>(current.internalPointer()); item = dynamic_cast<ContactRosterItem*>(item); } onSomethingSelectedChanged(item); QTreeView::currentChanged(current, previous); } - void QtTreeWidget::handleItemActivated(const QModelIndex& index) { - RosterItem* item = static_cast<RosterItem*>(index.internalPointer()); - ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); - if (contact) { - eventStream_->send(boost::shared_ptr<UIEvent>(new RequestChatUIEvent(contact->getJID()))); + switch (messageTarget_) { + case MessageDisplayJID: { + QString indexJID = index.data(DisplayJIDRole).toString(); + if (!indexJID.isEmpty()) { + JID target = JID(Q2PSTRING(indexJID)).toBare(); + eventStream_->send(boost::shared_ptr<UIEvent>(new RequestChatUIEvent(target))); + break; + } + } + case MessageDefaultJID: { + QString indexJID = index.data(JIDRole).toString(); + if (!indexJID.isEmpty()) { + JID target = JID(Q2PSTRING(indexJID)); + eventStream_->send(boost::shared_ptr<UIEvent>(new RequestChatUIEvent(target))); + } + break; + } } } void QtTreeWidget::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1) { event->acceptProposedAction(); } } void QtTreeWidget::dropEvent(QDropEvent *event) { QModelIndex index = indexAt(event->pos()); if (index.isValid()) { RosterItem* item = static_cast<RosterItem*>(index.internalPointer()); if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) { if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) { QString filename = event->mimeData()->urls().at(0).toLocalFile(); if (!filename.isEmpty()) { eventStream_->send(boost::make_shared<SendFileUIEvent>(contact->getJID(), Q2PSTRING(filename))); } } } } } void QtTreeWidget::dragMoveEvent(QDragMoveEvent* event) { QModelIndex index = indexAt(event->pos()); if (index.isValid()) { RosterItem* item = static_cast<RosterItem*>(index.internalPointer()); if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) { if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) { event->accept(); return; } } } @@ -198,36 +210,40 @@ bool QtTreeWidget::event(QEvent* event) { return QAbstractItemView::event(event); } void QtTreeWidget::handleExpanded(const QModelIndex& index) { GroupRosterItem* item = dynamic_cast<GroupRosterItem*>(static_cast<RosterItem*>(index.internalPointer())); if (item) { item->setExpanded(true); } } void QtTreeWidget::handleCollapsed(const QModelIndex& index) { GroupRosterItem* item = dynamic_cast<GroupRosterItem*>(static_cast<RosterItem*>(index.internalPointer())); if (item) { item->setExpanded(false); } } void QtTreeWidget::handleModelItemExpanded(const QModelIndex& index, bool shouldExpand) { if (!index.isValid()) { return; } bool alreadyRight = this->isExpanded(index) == shouldExpand; if (alreadyRight) { return; } setExpanded(index, shouldExpand); } void QtTreeWidget::drawBranches(QPainter*, const QRect&, const QModelIndex&) const { } void QtTreeWidget::show() { QWidget::show(); } +void QtTreeWidget::setMessageTarget(MessageTarget messageTarget) { + messageTarget_ = messageTarget; +} + } diff --git a/Swift/QtUI/Roster/QtTreeWidget.h b/Swift/QtUI/Roster/QtTreeWidget.h index 8884a40..29e985d 100644 --- a/Swift/QtUI/Roster/QtTreeWidget.h +++ b/Swift/QtUI/Roster/QtTreeWidget.h @@ -1,68 +1,74 @@ /* - * Copyright (c) 2010-2012 Kevin Smith + * Copyright (c) 2010-2014 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once -#include <QTreeView> -#include <QModelIndex> #include <QDragEnterEvent> #include <QDropEvent> #include <QDragMoveEvent> +#include <QModelIndex> +#include <QTreeView> -#include <Swift/QtUI/Roster/RosterModel.h> #include <Swift/QtUI/Roster/RosterDelegate.h> +#include <Swift/QtUI/Roster/RosterModel.h> + +#include <Swift/Controllers/UIInterfaces/ChatWindow.h> namespace Swift { class UIEventStream; class SettingsProvider; -class QtTreeWidget : public QTreeView{ +class QtTreeWidget : public QTreeView { Q_OBJECT public: - QtTreeWidget(UIEventStream* eventStream, SettingsProvider* settings, QWidget* parent = 0); + enum MessageTarget {MessageDefaultJID, MessageDisplayJID}; + + QtTreeWidget(UIEventStream* eventStream, SettingsProvider* settings, MessageTarget messageTarget, QWidget* parent = 0); ~QtTreeWidget(); void show(); QtTreeWidgetItem* getRoot(); void setRosterModel(Roster* roster); Roster* getRoster() {return roster_;} void refreshTooltip(); + void setMessageTarget(MessageTarget messageTarget); + public: boost::signal<void (RosterItem*)> onSomethingSelectedChanged; private slots: void handleItemActivated(const QModelIndex&); void handleModelItemExpanded(const QModelIndex&, bool expanded); void handleExpanded(const QModelIndex&); void handleCollapsed(const QModelIndex&); void handleClicked(const QModelIndex&); void handleSettingChanged(const std::string& setting); void handleRefreshTooltip(); protected: void dragEnterEvent(QDragEnterEvent* event); void dropEvent(QDropEvent* event); void dragMoveEvent(QDragMoveEvent* event); bool event(QEvent* event); - - protected: QModelIndexList getSelectedIndexes() const; + private: void drawBranches(QPainter*, const QRect&, const QModelIndex&) const; protected slots: virtual void currentChanged(const QModelIndex& current, const QModelIndex& previous); protected: UIEventStream* eventStream_; private: RosterModel* model_; Roster* roster_; RosterDelegate* delegate_; QtTreeWidgetItem* treeRoot_; SettingsProvider* settings_; bool tooltipShown_; + MessageTarget messageTarget_; }; } diff --git a/Swift/QtUI/Roster/RosterModel.cpp b/Swift/QtUI/Roster/RosterModel.cpp index fa8c393..2bd0d09 100644 --- a/Swift/QtUI/Roster/RosterModel.cpp +++ b/Swift/QtUI/Roster/RosterModel.cpp @@ -70,70 +70,72 @@ void RosterModel::handleDataChanged(RosterItem* item) { emit dataChanged(modelIndex, modelIndex); view_->refreshTooltip(); } } Qt::ItemFlags RosterModel::flags(const QModelIndex& index) const { Qt::ItemFlags flags = QAbstractItemModel::flags(index); if (dynamic_cast<GroupRosterItem*>(getItem(index)) == NULL) { flags |= Qt::ItemIsDragEnabled; } return flags; } int RosterModel::columnCount(const QModelIndex& /*parent*/) const { return 1; } RosterItem* RosterModel::getItem(const QModelIndex& index) const { return index.isValid() ? static_cast<RosterItem*>(index.internalPointer()) : NULL; } QVariant RosterModel::data(const QModelIndex& index, int role) const { RosterItem* item = getItem(index); if (!item) return QVariant(); switch (role) { case Qt::DisplayRole: return getScreenReaderTextOr(item, P2QSTRING(item->getDisplayName())); case Qt::TextColorRole: return getTextColor(item); case Qt::BackgroundColorRole: return getBackgroundColor(item); case Qt::ToolTipRole: return getToolTip(item); case StatusTextRole: return getStatusText(item); case AvatarRole: return getAvatar(item); case PresenceIconRole: return getPresenceIcon(item); case ChildCountRole: return getChildCount(item); case IdleRole: return getIsIdle(item); + case JIDRole: return getJID(item); + case DisplayJIDRole: return getDisplayJID(item); default: return QVariant(); } } QString RosterModel::getScreenReaderTextOr(RosterItem* item, const QString& alternative) const { QString name = P2QSTRING(item->getDisplayName()); ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); if (contact && screenReader_) { name += ": " + P2QSTRING(statusShowTypeToFriendlyName(contact->getStatusShow())); if (!contact->getStatusText().empty()) { name += " (" + P2QSTRING(contact->getStatusText()) + ")"; } return name; } else { return alternative; } } int RosterModel::getChildCount(RosterItem* item) const { GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item); return group ? group->getDisplayedChildren().size() : 0; } bool RosterModel::getIsIdle(RosterItem* item) const { ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); return contact ? !contact->getIdleText().empty() : false; } QColor RosterModel::intToColor(int color) const { return QColor( ((color & 0xFF0000)>>16), ((color & 0xFF00)>>8), (color & 0xFF)); } @@ -149,70 +151,80 @@ QColor RosterModel::getTextColor(RosterItem* item) const { case StatusShow::FFC: color = 0x000000; break; case StatusShow::DND: color = 0x990000; break; case StatusShow::None: color = 0x7F7F7F;break; } } return intToColor(color); } QColor RosterModel::getBackgroundColor(RosterItem* item) const { return dynamic_cast<ContactRosterItem*>(item) ? intToColor(0xFFFFFF) : intToColor(0x969696); } QString RosterModel::getToolTip(RosterItem* item) const { QString tip(P2QSTRING(item->getDisplayName())); ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); if (contact) { return RosterTooltip::buildDetailedTooltip(contact, cachedImageScaler_); } return tip; } QString RosterModel::getAvatar(RosterItem* item) const { ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); if (!contact) { return ""; } return P2QSTRING(pathToString(contact->getAvatarPath())); } QString RosterModel::getStatusText(RosterItem* item) const { ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); if (!contact) return ""; return P2QSTRING(contact->getStatusText()); } +QString RosterModel::getJID(RosterItem* item) const { + ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); + return contact ? P2QSTRING(contact->getJID().toString()) : QString(); +} + +QString RosterModel::getDisplayJID(RosterItem* item) const { + ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); + return contact ? P2QSTRING(contact->getDisplayJID().toString()) : QString(); +} + QIcon RosterModel::getPresenceIcon(RosterItem* item) const { ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); if (!contact) return QIcon(); if (contact->blockState() == ContactRosterItem::IsBlocked || contact->blockState() == ContactRosterItem::IsDomainBlocked) { return QIcon(":/icons/stop.png"); } return QIcon(statusShowTypeToIconPath(contact->getStatusShow())); } QModelIndex RosterModel::index(int row, int column, const QModelIndex& parent) const { if (!roster_) { return QModelIndex(); } GroupRosterItem* parentItem; if (!parent.isValid()) { //top level parentItem = roster_->getRoot(); } else { parentItem = dynamic_cast<GroupRosterItem*>(getItem(parent)); if (!parentItem) return QModelIndex(); } return static_cast<size_t>(row) < parentItem->getDisplayedChildren().size() ? createIndex(row, column, parentItem->getDisplayedChildren()[row]) : QModelIndex(); } QModelIndex RosterModel::index(RosterItem* item) const { GroupRosterItem* parent = item->getParent(); /* Recursive check that it's ok to create such an item Assuming there are more contacts in a group than groups in a group, this could save a decent chunk of search time at startup.*/ if (parent == NULL || roster_ == NULL || (parent != roster_->getRoot() && !index(parent).isValid())) { return QModelIndex(); } diff --git a/Swift/QtUI/Roster/RosterModel.h b/Swift/QtUI/Roster/RosterModel.h index 7f6cdd2..2b05044 100644 --- a/Swift/QtUI/Roster/RosterModel.h +++ b/Swift/QtUI/Roster/RosterModel.h @@ -1,67 +1,71 @@ /* * Copyright (c) 2010-2013 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #pragma once #include <QAbstractItemModel> #include <QList> #include <Swift/Controllers/Roster/Roster.h> #include <Swift/QtUI/QtScaledAvatarCache.h> namespace Swift { enum RosterRoles { StatusTextRole = Qt::UserRole, AvatarRole = Qt::UserRole + 1, PresenceIconRole = Qt::UserRole + 2, StatusShowTypeRole = Qt::UserRole + 3, ChildCountRole = Qt::UserRole + 4, - IdleRole = Qt::UserRole + 5 + IdleRole = Qt::UserRole + 5, + JIDRole = Qt::UserRole + 6, + DisplayJIDRole = Qt::UserRole + 7 }; class QtTreeWidget; class RosterModel : public QAbstractItemModel { Q_OBJECT public: RosterModel(QtTreeWidget* view, bool screenReaderMode); ~RosterModel(); void setRoster(Roster* swiftRoster); Qt::ItemFlags flags(const QModelIndex& index) const; int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; QModelIndex index(RosterItem* item) const; QModelIndex parent(const QModelIndex& index) const; int rowCount(const QModelIndex& parent = QModelIndex()) const; QMimeData* mimeData(const QModelIndexList& indexes) const; signals: void itemExpanded(const QModelIndex& item, bool expanded); private: void handleDataChanged(RosterItem* item); void handleChildrenChanged(GroupRosterItem* item); RosterItem* getItem(const QModelIndex& index) const; QColor intToColor(int color) const; QColor getTextColor(RosterItem* item) const; QColor getBackgroundColor(RosterItem* item) const; QString getToolTip(RosterItem* item) const; QString getAvatar(RosterItem* item) const; QString getStatusText(RosterItem* item) const; + QString getJID(RosterItem* item) const; + QString getDisplayJID(RosterItem* item) const; QIcon getPresenceIcon(RosterItem* item) const; int getChildCount(RosterItem* item) const; bool getIsIdle(RosterItem* item) const; void reLayout(); /** calculates screenreader-friendly text if in screenreader mode, otherwise uses alternative text */ QString getScreenReaderTextOr(RosterItem* item, const QString& alternative) const; private: Roster* roster_; QtTreeWidget* view_; QtScaledAvatarCache* cachedImageScaler_; bool screenReader_; }; } |
Swift