diff options
| -rw-r--r-- | Swift/Controllers/Chat/ChatController.cpp | 37 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatController.h | 6 | ||||
| -rw-r--r-- | Swift/Controllers/UIInterfaces/ChatWindow.h | 11 | ||||
| -rw-r--r-- | Swift/Controllers/UnitTest/MockChatWindow.h | 4 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatWindow.cpp | 59 | ||||
| -rw-r--r-- | Swift/QtUI/QtChatWindow.h | 10 |
6 files changed, 70 insertions, 57 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp index 9b65c9f..786b4e9 100644 --- a/Swift/Controllers/Chat/ChatController.cpp +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -137,72 +137,72 @@ void ChatController::handleBareJIDCapsChanged(const JID& /*jid*/) { } else { contactSupportsReceipts_ = ChatWindow::No; } if (FileTransferManager::isSupportedBy(disco)) { chatWindow_->setFileTransferEnabled(ChatWindow::Yes); } else { chatWindow_->setFileTransferEnabled(ChatWindow::No); } } else { SWIFT_LOG(debug) << "No disco info :(" << std::endl; chatWindow_->setCorrectionEnabled(ChatWindow::Maybe); contactSupportsReceipts_ = ChatWindow::Maybe; } checkForDisplayingDisplayReceiptsAlert(); } void ChatController::setToJID(const JID& jid) { chatStateNotifier_->setContact(jid); ChatControllerBase::setToJID(jid); Presence::ref presence; if (isInMUC_) { presence = presenceOracle_->getLastPresence(jid); } else { presence = jid.isBare() ? presenceOracle_->getHighestPriorityPresence(jid.toBare()) : presenceOracle_->getLastPresence(jid); } chatStateNotifier_->setContactIsOnline(presence && presence->getType() == Presence::Available); handleBareJIDCapsChanged(toJID_); } void ChatController::setAvailableServerFeatures(boost::shared_ptr<DiscoInfo> info) { ChatControllerBase::setAvailableServerFeatures(info); if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::BlockingCommandFeature)) { boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList(); blockingOnStateChangedConnection_ = blockList->onStateChanged.connect(boost::bind(&ChatController::handleBlockingStateChanged, this)); - blockingOnItemAddedConnection_ = blockList->onItemAdded.connect(boost::bind(&ChatController::handleBlockingItemAdded, this, _1)); - blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&ChatController::handleBlockingItemRemoved, this, _1)); + blockingOnItemAddedConnection_ = blockList->onItemAdded.connect(boost::bind(&ChatController::handleBlockingStateChanged, this)); + blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&ChatController::handleBlockingStateChanged, this)); handleBlockingStateChanged(); } } bool ChatController::isIncomingMessageFromMe(boost::shared_ptr<Message>) { return false; } void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) { if (messageEvent->isReadable()) { chatWindow_->flash(); lastWasPresence_ = false; } boost::shared_ptr<Message> message = messageEvent->getStanza(); JID from = message->getFrom(); if (!from.equals(toJID_, JID::WithResource)) { if (toJID_.equals(from, JID::WithoutResource) && toJID_.isBare()){ setToJID(from); } } chatStateTracker_->handleMessageReceived(message); chatStateNotifier_->receivedMessageFromContact(!!message->getPayload<ChatState>()); // handle XEP-0184 Message Receipts // incomming receipts if (boost::shared_ptr<DeliveryReceipt> receipt = message->getPayload<DeliveryReceipt>()) { SWIFT_LOG(debug) << "received receipt for id: " << receipt->getReceivedID() << std::endl; if (requestedReceipts_.find(receipt->getReceivedID()) != requestedReceipts_.end()) { chatWindow_->setMessageReceiptState(requestedReceipts_[receipt->getReceivedID()], ChatWindow::ReceiptReceived); requestedReceipts_.erase(receipt->getReceivedID()); } // incomming errors in response to send out receipts } else if (message->getPayload<DeliveryReceiptRequest>() && (message->getType() == Message::Error)) { if (requestedReceipts_.find(message->getID()) != requestedReceipts_.end()) { @@ -216,108 +216,99 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> me receiptMessage->setTo(toJID_); receiptMessage->addPayload(boost::make_shared<DeliveryReceipt>(message->getID())); stanzaChannel_->sendMessage(receiptMessage); } } } void ChatController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const HighlightAction& highlight) { eventController_->handleIncomingEvent(messageEvent); if (!messageEvent->getConcluded()) { highlighter_->handleHighlightAction(highlight); } } void ChatController::preSendMessageRequest(boost::shared_ptr<Message> message) { chatStateNotifier_->addChatStateRequest(message); if (userWantsReceipts_ && (contactSupportsReceipts_ != ChatWindow::No) && message) { message->addPayload(boost::make_shared<DeliveryReceiptRequest>()); } } void ChatController::setContactIsReceivingPresence(bool isReceivingPresence) { receivingPresenceFromUs_ = isReceivingPresence; } void ChatController::handleSettingChanged(const std::string& settingPath) { if (settingPath == SettingConstants::REQUEST_DELIVERYRECEIPTS.getKey()) { userWantsReceipts_ = settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS); checkForDisplayingDisplayReceiptsAlert(); } } void ChatController::checkForDisplayingDisplayReceiptsAlert() { if (userWantsReceipts_ && (contactSupportsReceipts_ == ChatWindow::No)) { - chatWindow_->setAlert(QT_TRANSLATE_NOOP("", "This chat doesn't support delivery receipts.")); + deliveryReceiptAlert_ = chatWindow_->addAlert(QT_TRANSLATE_NOOP("", "This chat doesn't support delivery receipts.")); } else if (userWantsReceipts_ && (contactSupportsReceipts_ == ChatWindow::Maybe)) { - chatWindow_->setAlert(QT_TRANSLATE_NOOP("", "This chat may not support delivery receipts. You might not receive delivery receipts for the messages you sent.")); + deliveryReceiptAlert_ = chatWindow_->addAlert(QT_TRANSLATE_NOOP("", "This chat may not support delivery receipts. You might not receive delivery receipts for the messages you sent.")); } else { - chatWindow_->cancelAlert(); + if (deliveryReceiptAlert_) { + chatWindow_->removeAlert(*deliveryReceiptAlert_); + deliveryReceiptAlert_.reset(); + } } } void ChatController::handleBlockingStateChanged() { boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList(); if (blockList->getState() == BlockList::Available) { if (isInMUC_ ? blockList->isBlocked(toJID_) : blockList->isBlocked(toJID_.toBare())) { - chatWindow_->setAlert(QT_TRANSLATE_NOOP("", "You've currently blocked this contact. To continue your conversation you have to unblock the contact first.")); + blockedContactAlert_ = chatWindow_->addAlert(QT_TRANSLATE_NOOP("", "You've currently blocked this contact. To continue your conversation you have to unblock the contact first.")); chatWindow_->setInputEnabled(false); chatWindow_->setBlockingState(ChatWindow::IsBlocked); } else { + if (blockedContactAlert_) { + chatWindow_->removeAlert(*blockedContactAlert_); + blockedContactAlert_.reset(); + } + chatWindow_->setInputEnabled(true); chatWindow_->setBlockingState(ChatWindow::IsUnblocked); } } } -void ChatController::handleBlockingItemAdded(const JID& jid) { - if (toJID_ == (isInMUC_ ? jid: jid.toBare())) { - chatWindow_->setAlert(QT_TRANSLATE_NOOP("", "You've currently blocked this contact. To continue your conversation you have to unblock the contact first.")); - chatWindow_->setInputEnabled(false); - chatWindow_->setBlockingState(ChatWindow::IsBlocked); - } -} - -void ChatController::handleBlockingItemRemoved(const JID& jid) { - if (toJID_ == (isInMUC_ ? jid: jid.toBare())) { - // FIXME: Support for different types of alerts. - chatWindow_->cancelAlert(); - chatWindow_->setInputEnabled(true); - chatWindow_->setBlockingState(ChatWindow::IsUnblocked); - } -} - void ChatController::handleBlockUserRequest() { if (isInMUC_) { eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Blocked, toJID_)); } else { eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Blocked, toJID_.toBare())); } } void ChatController::handleUnblockUserRequest() { if (isInMUC_) { eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Unblocked, toJID_)); } else { eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Unblocked, toJID_.toBare())); } } void ChatController::handleInviteToChat(const std::vector<JID>& droppedJIDs) { boost::shared_ptr<UIEvent> event(new RequestInviteToMUCUIEvent(toJID_.toBare(), droppedJIDs, RequestInviteToMUCUIEvent::Impromptu)); eventStream_->send(event); } void ChatController::handleWindowClosed() { onWindowClosed(); } void ChatController::handleUIEvent(boost::shared_ptr<UIEvent> event) { boost::shared_ptr<InviteToMUCUIEvent> inviteEvent = boost::dynamic_pointer_cast<InviteToMUCUIEvent>(event); if (inviteEvent && inviteEvent->getRoom() == toJID_.toBare()) { onConvertToMUC(detachChatWindow(), inviteEvent->getInvites(), inviteEvent->getReason()); } } void ChatController::postSendMessage(const std::string& body, boost::shared_ptr<Stanza> sentStanza) { boost::shared_ptr<Replace> replace = sentStanza->getPayload<Replace>(); diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h index 2e92be7..998b437 100644 --- a/Swift/Controllers/Chat/ChatController.h +++ b/Swift/Controllers/Chat/ChatController.h @@ -40,74 +40,74 @@ namespace Swift { virtual ChatWindow* detachChatWindow(); protected: void cancelReplaces(); JID getBaseJID(); void logMessage(const std::string& message, const JID& fromJID, const JID& toJID, const boost::posix_time::ptime& timeStamp, bool isIncoming); private: void handlePresenceChange(boost::shared_ptr<Presence> newPresence); std::string getStatusChangeString(boost::shared_ptr<Presence> presence); bool isIncomingMessageFromMe(boost::shared_ptr<Message> message); void postSendMessage(const std::string &body, boost::shared_ptr<Stanza> sentStanza); void preHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent); void postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent, const HighlightAction&); void preSendMessageRequest(boost::shared_ptr<Message>); std::string senderDisplayNameFromMessage(const JID& from); virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(boost::shared_ptr<Message>) const; void handleStanzaAcked(boost::shared_ptr<Stanza> stanza); void dayTicked() {lastWasPresence_ = false;} void handleContactNickChanged(const JID& jid, const std::string& /*oldNick*/); void handleBareJIDCapsChanged(const JID& jid); void handleFileTransferCancel(std::string /* id */); void handleFileTransferStart(std::string /* id */, std::string /* description */); void handleFileTransferAccept(std::string /* id */, std::string /* filename */); void handleSendFileRequest(std::string filename); void handleWhiteboardSessionAccept(); void handleWhiteboardSessionCancel(); void handleWhiteboardWindowShow(); void handleSettingChanged(const std::string& settingPath); void checkForDisplayingDisplayReceiptsAlert(); void handleBlockingStateChanged(); - void handleBlockingItemAdded(const JID&); - void handleBlockingItemRemoved(const JID&); - void handleBlockUserRequest(); void handleUnblockUserRequest(); void handleInviteToChat(const std::vector<JID>& droppedJIDs); void handleWindowClosed(); void handleUIEvent(boost::shared_ptr<UIEvent> event); private: NickResolver* nickResolver_; ChatStateNotifier* chatStateNotifier_; ChatStateTracker* chatStateTracker_; std::string myLastMessageUIID_; bool isInMUC_; bool lastWasPresence_; std::string lastStatusChangeString_; std::map<boost::shared_ptr<Stanza>, std::string> unackedStanzas_; std::map<std::string, std::string> requestedReceipts_; StatusShow::Type lastShownStatus_; UIEventStream* eventStream_; ChatWindow::Tristate contactSupportsReceipts_; bool receivingPresenceFromUs_; bool userWantsReceipts_; std::map<std::string, FileTransferController*> ftControllers; SettingsProvider* settings_; std::string lastWbID_; ClientBlockListManager* clientBlockListManager_; boost::bsignals::scoped_connection blockingOnStateChangedConnection_; boost::bsignals::scoped_connection blockingOnItemAddedConnection_; boost::bsignals::scoped_connection blockingOnItemRemovedConnection_; + + boost::optional<ChatWindow::AlertID> deliveryReceiptAlert_; + boost::optional<ChatWindow::AlertID> blockedContactAlert_; }; } diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h index 81d26c8..f2f5f76 100644 --- a/Swift/Controllers/UIInterfaces/ChatWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatWindow.h @@ -121,80 +121,87 @@ namespace Swift { 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 bool isVisible() const = 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 setFileTransferEnabled(Tristate enabled) = 0; virtual void setUnreadMessageCount(int count) = 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, const TimestampBehaviour timestampBehaviour) = 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; virtual void showBookmarkWindow(const MUCBookmark& bookmark) = 0; + + /** + * A handle that uniquely identities an alert message. + */ + typedef int AlertID; /** * 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). + * @return A handle to the alert message. */ - virtual void setAlert(const std::string& alertText, const std::string& buttonText = "") = 0; + virtual AlertID addAlert(const std::string& alertText, const std::string& buttonText = "") = 0; /** * Removes an alert. + * @param id An alert ID previously returned from setAlert */ - virtual void cancelAlert() = 0; + virtual void removeAlert(const AlertID id) = 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 ()> onBookmarkRequest; boost::signal<void (Form::ref)> onConfigureRequest; boost::signal<void ()> onDestroyRequest; boost::signal<void (const std::vector<JID>&)> onInviteToChat; 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; boost::signal<void ()> onLogCleared; // File transfer related boost::signal<void (std::string /* id */)> onFileTransferCancel; boost::signal<void (std::string /* id */, std::string /* description */)> onFileTransferStart; diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h index 5e6606b..ef7216b 100644 --- a/Swift/Controllers/UnitTest/MockChatWindow.h +++ b/Swift/Controllers/UnitTest/MockChatWindow.h @@ -24,72 +24,72 @@ namespace Swift { 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*/, const TimestampBehaviour /*timestampBehaviour*/) {} // 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 bool isVisible() const { return true; } 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(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 AlertID addAlert(const std::string& /*alertText*/, const std::string& /*buttonText*/) { return 0; } + virtual void removeAlert(const AlertID /*id*/) {} virtual void setCorrectionEnabled(Tristate /*enabled*/) {} virtual void setFileTransferEnabled(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*/) {} virtual void showBookmarkWindow(const MUCBookmark& /*bookmark*/) {} 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 ""; } std::string name_; std::string lastMessageBody_; std::vector<SecurityLabelsCatalog::Item> labels_; bool labelsEnabled_; SecurityLabelsCatalog::Item label_; Roster* roster_; }; } diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 47eeac0..574a0a2 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -25,107 +25,95 @@ #include <QMimeData> #include <QPushButton> #include <QSplitter> #include <QString> #include <QTextDocument> #include <QTextEdit> #include <QTime> #include <QToolButton> #include <QUrl> #include <QMimeData> #include <Swiften/Base/Log.h> #include <Swift/Controllers/Roster/ContactRosterItem.h> #include <Swift/Controllers/Roster/Roster.h> #include <Swift/Controllers/Roster/RosterItem.h> #include <Swift/Controllers/Settings/SettingsProvider.h> #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/Controllers/UIEvents/SendFileUIEvent.h> #include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> #include <SwifTools/TabComplete.h> #include <Swift/QtUI/Roster/QtOccupantListWidget.h> #include <Swift/QtUI/QtAddBookmarkWindow.h> #include <Swift/QtUI/QtPlainChatView.h> #include <Swift/QtUI/QtSettingsProvider.h> #include <Swift/QtUI/QtScaledAvatarCache.h> #include <Swift/QtUI/QtTextEdit.h> #include <Swift/QtUI/QtUISettingConstants.h> #include <Swift/QtUI/QtUtilities.h> #include <Swift/QtUI/QtWebKitChatView.h> namespace Swift { -QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), contact_(contact), eventStream_(eventStream), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false) { +QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), contact_(contact), nextAlertId_(0), eventStream_(eventStream), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false) { settings_ = settings; unreadCount_ = 0; inputEnabled_ = true; completer_ = NULL; affiliationEditor_ = NULL; theme_ = theme; isCorrection_ = false; labelModel_ = NULL; correctionEnabled_ = Maybe; fileTransferEnabled_ = Maybe; updateTitleWithUnreadCount(); #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(); + alertLayout_ = new QVBoxLayout(this); + layout->addLayout(alertLayout_); 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_, 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_); @@ -182,129 +170,154 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt settings_->onSettingChanged.connect(boost::bind(&QtChatWindow::handleSettingChanged, this, _1)); messageLog_->showEmoticons(settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS)); } QtChatWindow::~QtChatWindow() { if (mucConfigurationWindow_) { delete mucConfigurationWindow_.data(); } } void QtChatWindow::handleSettingChanged(const std::string& setting) { if (setting == QtUISettingConstants::SHOW_EMOTICONS.getKey()) { bool showEmoticons = settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS); messageLog_->showEmoticons(showEmoticons); } } void QtChatWindow::handleLogCleared() { onLogCleared(); } 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()); +QtChatWindow::AlertID QtChatWindow::addAlert(const std::string& alertText, const std::string& buttonText) { + QWidget* alertWidget = new QWidget(this); + QHBoxLayout* alertLayout = new QHBoxLayout(alertWidget); + alertLayout_->addWidget(alertWidget); + QLabel* 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_); + + alertLabel->setText(alertText.c_str()); if (buttonText.empty()) { alertButton_->hide(); } else { alertButton_->setText(buttonText.c_str()); alertButton_->show(); } - alertWidget_->show(); + + AlertID id = nextAlertId_++; + alertWidgets_[id] = alertWidget; + return id; } -void QtChatWindow::cancelAlert() { - alertWidget_->hide(); +void QtChatWindow::removeAlert(const AlertID id) { + std::map<AlertID, QWidget*>::iterator i = alertWidgets_.find(id); + if (i != alertWidgets_.end()) { + alertLayout_->removeWidget(i->second); + delete i->second; + alertWidgets_.erase(i); + } } void QtChatWindow::setTabComplete(TabComplete* completer) { completer_ = completer; } void QtChatWindow::handleKeyPressEvent(QKeyEvent* event) { event->ignore(); if (event->isAccepted()) { return; } event->accept(); int key = event->key(); if (key == Qt::Key_Tab) { tabComplete(); } else if ((key == Qt::Key_Up) && input_->toPlainText().isEmpty() && !(lastSentMessage_.isEmpty())) { beginCorrection(); } else if (key == Qt::Key_Down && isCorrection_ && input_->textCursor().atBlockEnd()) { cancelCorrection(); } else if (key == Qt::Key_Down || key == Qt::Key_Up) { /* Drop */ } else { messageLog_->handleKeyPressEvent(event); } } void QtChatWindow::beginCorrection() { if (correctionEnabled_ == ChatWindow::Maybe) { - setAlert(Q2PSTRING(tr("This chat may not support message correction. If you send a correction anyway, it may appear as a duplicate message"))); + correctingAlert_ = addAlert(Q2PSTRING(tr("This chat may not support message correction. If you send a correction anyway, it may appear as a duplicate message"))); } else if (correctionEnabled_ == ChatWindow::No) { - setAlert(Q2PSTRING(tr("This chat does not support message correction. If you send a correction anyway, it will appear as a duplicate message"))); + correctingAlert_ = addAlert(Q2PSTRING(tr("This chat does not support message correction. If you send a correction anyway, it will appear as a duplicate message"))); } QTextCursor cursor = input_->textCursor(); cursor.select(QTextCursor::Document); cursor.beginEditBlock(); cursor.insertText(QString(lastSentMessage_)); cursor.endEditBlock(); isCorrection_ = true; correctingLabel_->show(); input_->setStyleSheet(alertStyleSheet_); labelsWidget_->setEnabled(false); } void QtChatWindow::cancelCorrection() { - cancelAlert(); + if (correctingAlert_) { + removeAlert(*correctingAlert_); + correctingAlert_.reset(); + } QTextCursor cursor = input_->textCursor(); cursor.select(QTextCursor::Document); cursor.removeSelectedText(); isCorrection_ = false; correctingLabel_->hide(); input_->setStyleSheet(qApp->styleSheet()); labelsWidget_->setEnabled(true); } QByteArray QtChatWindow::getSplitterState() { return logRosterSplitter_->saveState(); } void QtChatWindow::handleChangeSplitterState(QByteArray state) { logRosterSplitter_->restoreState(state); } void QtChatWindow::handleSplitterMoved(int, int) { emit splitterMoved(); } void QtChatWindow::tabComplete() { if (!completer_) { return; } QTextCursor cursor; if (tabCompleteCursor_.hasSelection()) { cursor = tabCompleteCursor_; } else { cursor = input_->textCursor(); while(cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor) && cursor.document()->characterAt(cursor.position() - 1) != ' ') { } } QString root = cursor.selectedText(); if (root.isEmpty()) { diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index b3a3f5e..45eaa6f 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -102,120 +102,122 @@ namespace Swift { void setUnreadMessageCount(int count); 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, const TimestampBehaviour timestampBehaviour); 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); virtual void showBookmarkWindow(const MUCBookmark& bookmark); public slots: void handleChangeSplitterState(QByteArray state); void handleFontResized(int fontSizeSteps); - void setAlert(const std::string& alertText, const std::string& buttonText = ""); - void cancelAlert(); + AlertID addAlert(const std::string& alertText, const std::string& buttonText = ""); + void removeAlert(const AlertID id); void setCorrectionEnabled(Tristate enabled); void setFileTransferEnabled(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 handleLogCleared(); void returnPressed(); void handleInputChanged(); void handleCursorPositionChanged(); void handleKeyPressEvent(QKeyEvent* event); void handleSplitterMoved(int pos, int index); void handleAlertButtonClicked(); void handleActionButtonClicked(); void handleAffiliationEditorAccepted(); void handleCurrentLabelChanged(int); private: void updateTitleWithUnreadCount(); void tabComplete(); void beginCorrection(); void cancelCorrection(); void handleSettingChanged(const std::string& setting); void handleOccupantSelectionChanged(RosterItem* item); void handleAppendedToLog(); int unreadCount_; bool contactIsTyping_; LastLineTracker lastLineTracker_; QString contact_; QString lastSentMessage_; QTextCursor tabCompleteCursor_; QtChatView* messageLog_; QtChatTheme* theme_; QtTextEdit* input_; QWidget* midBar_; QBoxLayout* subjectLayout_; QComboBox* labelsWidget_; QtOccupantListWidget* treeWidget_; QLabel* correctingLabel_; - QLabel* alertLabel_; - QWidget* alertWidget_; + boost::optional<AlertID> correctingAlert_; + QVBoxLayout* alertLayout_; + std::map<AlertID, QWidget*> alertWidgets_; + AlertID nextAlertId_; QPushButton* alertButton_; TabComplete* completer_; QLineEdit* subject_; bool isCorrection_; bool inputClearing_; bool tabCompletion_; UIEventStream* eventStream_; bool inputEnabled_; QSplitter *logRosterSplitter_; Tristate correctionEnabled_; Tristate fileTransferEnabled_; QString alertStyleSheet_; QPointer<QtMUCConfigurationWindow> mucConfigurationWindow_; QPointer<QtAffiliationEditor> affiliationEditor_; SettingsProvider* settings_; std::vector<ChatWindow::RoomAction> availableRoomActions_; QPalette defaultLabelsPalette_; LabelModel* labelModel_; BlockingState blockingState_; bool impromptu_; bool isMUC_; bool supportsImpromptuChat_; }; } |
Swift