summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Smith <git@kismith.co.uk>2011-05-02 18:38:42 (GMT)
committerKevin Smith <git@kismith.co.uk>2011-08-21 18:08:37 (GMT)
commit8d6a78665b79a382dc1871852ed7bd150263db79 (patch)
tree1fb975506939049a509cb5ea36350a9236b0b7b3
parent7c44520ffa37faa83731edd85dfe8196ba625d52 (diff)
downloadswift-contrib-8d6a78665b79a382dc1871852ed7bd150263db79.zip
swift-contrib-8d6a78665b79a382dc1871852ed7bd150263db79.tar.bz2
Warn when sending corrections without support.
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp35
-rw-r--r--Swift/Controllers/Chat/ChatController.h1
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.cpp10
-rw-r--r--Swift/Controllers/Chat/ChatControllerBase.h6
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp2
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp24
-rw-r--r--Swift/Controllers/Chat/MUCController.h3
-rw-r--r--Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp6
-rw-r--r--Swift/Controllers/MainController.cpp1
-rw-r--r--Swift/Controllers/UIInterfaces/ChatWindow.h13
-rw-r--r--Swift/Controllers/UnitTest/MockChatWindow.h3
-rw-r--r--Swift/QtUI/QtChatWindow.cpp48
-rw-r--r--Swift/QtUI/QtChatWindow.h9
-rw-r--r--Swiften/Elements/DiscoInfo.cpp2
-rw-r--r--Swiften/Elements/DiscoInfo.h1
15 files changed, 146 insertions, 18 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index 513b446..f4aa745 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -12,16 +12,16 @@
#include <Swift/Controllers/Intl.h>
#include <Swiften/Base/format.h>
#include <Swiften/Base/Algorithm.h>
-#include "Swiften/Avatars/AvatarManager.h"
-#include "Swiften/Chat/ChatStateNotifier.h"
-#include "Swiften/Chat/ChatStateTracker.h"
-#include "Swiften/Client/StanzaChannel.h"
-#include "Swiften/Disco/EntityCapsProvider.h"
-#include "Swift/Controllers/UIInterfaces/ChatWindow.h"
-#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"
-#include "Swiften/Client/NickResolver.h"
-#include "Swift/Controllers/XMPPEvents/EventController.h"
+#include <Swiften/Avatars/AvatarManager.h>
+#include <Swiften/Chat/ChatStateNotifier.h>
+#include <Swiften/Chat/ChatStateTracker.h>
+#include <Swiften/Client/StanzaChannel.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
+#include <Swiften/Client/NickResolver.h>
+#include <Swift/Controllers/XMPPEvents/EventController.h>
#include <Swift/Controllers/StatusUtil.h>
+#include <Swiften/Disco/EntityCapsProvider.h>
namespace Swift {
@@ -29,7 +29,7 @@ namespace Swift {
* The controller does not gain ownership of the stanzaChannel, nor the factory.
*/
ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider)
- : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory) {
+ : ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider) {
isInMUC_ = isInMUC;
lastWasPresence_ = false;
chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsProvider);
@@ -60,6 +60,7 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
chatWindow_->addSystemMessage(startMessage);
chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_));
chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_));
+ handleBareJIDCapsChanged(toJID_);
}
@@ -75,6 +76,19 @@ ChatController::~ChatController() {
delete chatStateTracker_;
}
+void ChatController::handleBareJIDCapsChanged(const JID& /*jid*/) {
+ DiscoInfo::ref disco = entityCapsProvider_->getCaps(toJID_);
+ if (disco) {
+ if (disco->hasFeature(DiscoInfo::MessageCorrectionFeature)) {
+ chatWindow_->setCorrectionEnabled(ChatWindow::Yes);
+ } else {
+ chatWindow_->setCorrectionEnabled(ChatWindow::No);
+ }
+ } else {
+ chatWindow_->setCorrectionEnabled(ChatWindow::Maybe);
+ }
+}
+
void ChatController::setToJID(const JID& jid) {
chatStateNotifier_->setContact(jid);
ChatControllerBase::setToJID(jid);
@@ -85,6 +99,7 @@ void ChatController::setToJID(const JID& jid) {
presence = jid.isBare() ? presenceOracle_->getHighestPriorityPresence(jid.toBare()) : presenceOracle_->getLastPresence(jid);
}
chatStateNotifier_->setContactIsOnline(presence && presence->getType() == Presence::Available);
+ handleBareJIDCapsChanged(toJID_);
}
bool ChatController::isIncomingMessageFromMe(boost::shared_ptr<Message>) {
diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h
index 4fafb44..f6b8763 100644
--- a/Swift/Controllers/Chat/ChatController.h
+++ b/Swift/Controllers/Chat/ChatController.h
@@ -35,6 +35,7 @@ namespace Swift {
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);
private:
NickResolver* nickResolver_;
diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp
index 5e3c45f..802a7cb 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.cpp
+++ b/Swift/Controllers/Chat/ChatControllerBase.cpp
@@ -21,6 +21,7 @@
#include "Swiften/Elements/Delay.h"
#include "Swiften/Base/foreach.h"
#include "Swift/Controllers/XMPPEvents/EventController.h"
+#include "Swiften/Disco/EntityCapsProvider.h"
#include "Swift/Controllers/UIInterfaces/ChatWindow.h"
#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"
#include "Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h"
@@ -28,10 +29,11 @@
namespace Swift {
-ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory) {
+ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory), entityCapsProvider_(entityCapsProvider) {
chatWindow_ = chatWindowFactory_->createChatWindow(toJID, eventStream);
chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this));
chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2));
+ entityCapsProvider_->onCapsChanged.connect(boost::bind(&ChatControllerBase::handleCapsChanged, this, _1));
setOnline(stanzaChannel->isAvailable() && iqRouter->isAvailable());
createDayChangeTimer();
}
@@ -40,6 +42,12 @@ ChatControllerBase::~ChatControllerBase() {
delete chatWindow_;
}
+void ChatControllerBase::handleCapsChanged(const JID& jid) {
+ if (jid.compare(toJID_, JID::WithoutResource) == 0) {
+ handleBareJIDCapsChanged(jid);
+ }
+}
+
void ChatControllerBase::createDayChangeTimer() {
if (timerFactory_) {
boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h
index 86c1ef2..79d376c 100644
--- a/Swift/Controllers/Chat/ChatControllerBase.h
+++ b/Swift/Controllers/Chat/ChatControllerBase.h
@@ -35,6 +35,7 @@ namespace Swift {
class AvatarManager;
class UIEventStream;
class EventController;
+ class EntityCapsProvider;
class ChatControllerBase : public boost::bsignals::trackable {
public:
@@ -52,8 +53,9 @@ namespace Swift {
boost::signal<void ()> onUnreadCountChanged;
int getUnreadCount();
const JID& getToJID() {return toJID_;}
+ void handleCapsChanged(const JID& jid);
protected:
- ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory);
+ ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider);
/**
* Pass the Message appended, and the stanza used to send it.
@@ -67,6 +69,7 @@ namespace Swift {
virtual bool isFromContact(const JID& from);
virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(boost::shared_ptr<Message>) const = 0;
virtual void dayTicked() {};
+ virtual void handleBareJIDCapsChanged(const JID& jid) = 0;
private:
IDGenerator idGenerator_;
@@ -94,5 +97,6 @@ namespace Swift {
EventController* eventController_;
boost::shared_ptr<Timer> dateChangeTimer_;
TimerFactory* timerFactory_;
+ EntityCapsProvider* entityCapsProvider_;
};
}
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index 8b8b993..56f7a4c 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -480,7 +480,7 @@ void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional
} else {
std::string nick = nickMaybe ? nickMaybe.get() : jid_.getNode();
MUC::ref muc = mucManager->createMUC(mucJID);
- MUCController* controller = new MUCController(jid_, muc, nick, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_);
+ MUCController* controller = new MUCController(jid_, muc, nick, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory_, eventController_, entityCapsProvider_);
mucControllers_[mucJID] = controller;
controller->setAvailableServerFeatures(serverDiscoInfo_);
controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller));
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index 93ceb13..aa0a1e7 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -30,6 +30,7 @@
#include <Swift/Controllers/Roster/Roster.h>
#include <Swift/Controllers/Roster/SetAvatar.h>
#include <Swift/Controllers/Roster/SetPresence.h>
+#include <Swiften/Disco/EntityCapsProvider.h>
#define MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS 60000
@@ -51,8 +52,9 @@ MUCController::MUCController (
UIEventStream* uiEventStream,
bool useDelayForLatency,
TimerFactory* timerFactory,
- EventController* eventController) :
- ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory), muc_(muc), nick_(nick), desiredNick_(nick) {
+ EventController* eventController,
+ EntityCapsProvider* entityCapsProvider) :
+ ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, muc->getJID(), presenceOracle, avatarManager, useDelayForLatency, uiEventStream, eventController, timerFactory, entityCapsProvider), muc_(muc), nick_(nick), desiredNick_(nick) {
parting_ = true;
joined_ = false;
lastWasPresence_ = false;
@@ -82,6 +84,7 @@ MUCController::MUCController (
if (avatarManager_ != NULL) {
avatarChangedConnection_ = (avatarManager_->onAvatarChanged.connect(boost::bind(&MUCController::handleAvatarChanged, this, _1)));
}
+ handleBareJIDCapsChanged(muc->getJID());
}
MUCController::~MUCController() {
@@ -94,6 +97,23 @@ MUCController::~MUCController() {
delete completer_;
}
+void MUCController::handleBareJIDCapsChanged(const JID& /*jid*/) {
+ ChatWindow::Tristate support = ChatWindow::Yes;
+ bool any = false;
+ foreach (const std::string& nick, currentOccupants_) {
+ DiscoInfo::ref disco = entityCapsProvider_->getCaps(toJID_.toBare().toString() + "/" + nick);
+ if (disco && disco->hasFeature(DiscoInfo::MessageCorrectionFeature)) {
+ any = true;
+ } else {
+ support = ChatWindow::Maybe;
+ }
+ }
+ if (!any) {
+ support = ChatWindow::No;
+ }
+ chatWindow_->setCorrectionEnabled(support);
+}
+
/**
* Join the MUC if not already in it.
*/
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index c6901df..3a79920 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -41,7 +41,7 @@ namespace Swift {
class MUCController : public ChatControllerBase {
public:
- MUCController(const JID& self, MUC::ref muc, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController);
+ MUCController(const JID& self, MUC::ref muc, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider);
~MUCController();
boost::signal<void ()> onUserLeft;
boost::signal<void ()> onUserJoined;
@@ -83,6 +83,7 @@ namespace Swift {
bool shouldUpdateJoinParts();
void dayTicked() {lastWasPresence_ = false;}
void processUserPart();
+ void handleBareJIDCapsChanged(const JID& jid);
private:
MUC::ref muc_;
diff --git a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
index 5f5e44d..ad5ceac 100644
--- a/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/MUCControllerTest.cpp
@@ -25,6 +25,7 @@
#include "Swiften/Presence/PresenceOracle.h"
#include "Swiften/Network/TimerFactory.h"
#include "Swiften/Elements/MUCUserPayload.h"
+#include "Swiften/Disco/DummyEntityCapsProvider.h"
using namespace Swift;
@@ -56,12 +57,14 @@ public:
TimerFactory* timerFactory = NULL;
window_ = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>();
mucRegistry_ = new MUCRegistry();
+ entityCapsProvider_ = new DummyEntityCapsProvider();
muc_ = MUC::ref(new MUC(stanzaChannel_, iqRouter_, directedPresenceSender_, JID("teaparty@rooms.wonderland.lit"), mucRegistry_));
mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(muc_->getJID(), uiEventStream_).Return(window_);
- controller_ = new MUCController (self_, muc_, nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_);
+ controller_ = new MUCController (self_, muc_, nick_, stanzaChannel_, iqRouter_, chatWindowFactory_, presenceOracle_, avatarManager_, uiEventStream_, false, timerFactory, eventController_, entityCapsProvider_);
};
void tearDown() {
+ delete entityCapsProvider_;
delete controller_;
delete eventController_;
delete presenceOracle_;
@@ -240,6 +243,7 @@ private:
UIEventStream* uiEventStream_;
MockChatWindow* window_;
MUCRegistry* mucRegistry_;
+ DummyEntityCapsProvider* entityCapsProvider_;
};
CPPUNIT_TEST_SUITE_REGISTRATION(MUCControllerTest);
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 92bec85..2690343 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -288,6 +288,7 @@ void MainController::handleConnected() {
discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc"));
discoInfo.addFeature(DiscoInfo::ChatStatesFeature);
discoInfo.addFeature(DiscoInfo::SecurityLabelsFeature);
+ discoInfo.addFeature(DiscoInfo::MessageCorrectionFeature);
client_->getDiscoManager()->setCapsNode(CLIENT_NODE);
client_->getDiscoManager()->setDiscoInfo(discoInfo);
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index f65328d..e84116d 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -26,6 +26,7 @@ namespace Swift {
class ChatWindow {
public:
enum AckState {Pending, Received, Failed};
+ enum Tristate {Yes, No, Maybe};
ChatWindow() {}
virtual ~ChatWindow() {};
@@ -48,6 +49,7 @@ namespace Swift {
virtual void activate() = 0;
virtual void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) = 0;
virtual void setSecurityLabelsEnabled(bool enabled) = 0;
+ virtual void setCorrectionEnabled(Tristate enabled) = 0;
virtual void setUnreadMessageCount(int count) = 0;
virtual void convertToMUC() = 0;
// virtual TreeWidget *getTreeWidget() = 0;
@@ -59,6 +61,16 @@ namespace Swift {
virtual void replaceLastMessage(const std::string& message) = 0;
virtual void setAckState(const std::string& id, AckState state) = 0;
virtual void flash() = 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;
boost::signal<void ()> onClosed;
boost::signal<void ()> onAllMessagesRead;
@@ -66,6 +78,7 @@ namespace Swift {
boost::signal<void ()> onSendCorrectionMessageRequest;
boost::signal<void ()> onUserTyping;
boost::signal<void ()> onUserCancelsTyping;
+ boost::signal<void ()> onAlertButtonClicked;
};
}
#endif
diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h
index a5765fd..7d6828f 100644
--- a/Swift/Controllers/UnitTest/MockChatWindow.h
+++ b/Swift/Controllers/UnitTest/MockChatWindow.h
@@ -37,6 +37,9 @@ namespace Swift {
virtual void replaceMessage(const std::string&, const std::string&, const boost::posix_time::ptime&) {};
void setAckState(const std::string& /*id*/, AckState /*state*/) {};
virtual void flash() {};
+ virtual void setAlert(const std::string& /*alertText*/, const std::string& /*buttonText*/) {};
+ virtual void cancelAlert() {};
+ virtual void setCorrectionEnabled(Tristate /*enabled*/) {}
boost::signal<void ()> onClosed;
boost::signal<void ()> onAllMessagesRead;
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index 32c3067..c7a6fa8 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -30,6 +30,7 @@
#include <QTextEdit>
#include <QTime>
#include <QUrl>
+#include <QPushButton>
namespace Swift {
QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageWasSystem_(false), previousMessageWasPresence_(false), eventStream_(eventStream) {
@@ -38,6 +39,7 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
completer_ = NULL;
theme_ = theme;
isCorrection_ = false;
+ correctionEnabled_ = Maybe;
updateTitleWithUnreadCount();
QtSettingsProvider settings;
@@ -45,10 +47,24 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
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_->setPalette(palette);
+ alertLabel_->setPalette(palette);
+ alertWidget_->hide();
+
logRosterSplitter_ = new QSplitter(this);
logRosterSplitter_->setAutoFillBackground(true);
layout->addWidget(logRosterSplitter_);
-
messageLog_ = new QtChatView(theme, this);
logRosterSplitter_->addWidget(messageLog_);
@@ -104,10 +120,30 @@ QtChatWindow::~QtChatWindow() {
}
+
void QtChatWindow::handleFontResized(int fontSizeSteps) {
messageLog_->resizeFont(fontSizeSteps);
}
+void QtChatWindow::handleAlertButtonClicked() {
+ onAlertButtonClicked();
+}
+
+void QtChatWindow::setAlert(const std::string& alertText, const std::string& buttonText) {
+ alertLabel_->setText(alertText.c_str());
+ if (buttonText.empty()) {
+ alertButton_->hide();
+ } else {
+ alertButton_->setText(buttonText.c_str());
+ alertButton_->show();
+ }
+ alertWidget_->show();
+}
+
+void QtChatWindow::cancelAlert() {
+ alertWidget_->hide();
+}
+
void QtChatWindow::setTabComplete(TabComplete* completer) {
completer_ = completer;
}
@@ -146,6 +182,11 @@ void QtChatWindow::handleKeyPressEvent(QKeyEvent* 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")));
+ } 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")));
+ }
QTextCursor cursor = input_->textCursor();
cursor.select(QTextCursor::Document);
cursor.beginEditBlock();
@@ -156,6 +197,7 @@ void QtChatWindow::beginCorrection() {
}
void QtChatWindow::cancelCorrection() {
+ cancelAlert();
QTextCursor cursor = input_->textCursor();
cursor.select(QTextCursor::Document);
cursor.removeSelectedText();
@@ -237,6 +279,10 @@ void QtChatWindow::setSecurityLabelsEnabled(bool enabled) {
}
}
+void QtChatWindow::setCorrectionEnabled(Tristate enabled) {
+ correctionEnabled_ = enabled;
+}
+
SecurityLabelsCatalog::Item QtChatWindow::getSelectedSecurityLabel() {
assert(labelsWidget_->isEnabled());
return availableLabels_[labelsWidget_->currentIndex()];
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index 78d8f91..bc2d821 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -19,6 +19,7 @@ class QLineEdit;
class QComboBox;
class QLabel;
class QSplitter;
+class QPushButton;
namespace Swift {
class QtChatView;
@@ -63,6 +64,9 @@ namespace Swift {
public slots:
void handleChangeSplitterState(QByteArray state);
void handleFontResized(int fontSizeSteps);
+ void setAlert(const std::string& alertText, const std::string& buttonText = "");
+ void cancelAlert();
+ void setCorrectionEnabled(Tristate enabled);
signals:
void geometryChanged();
@@ -83,6 +87,7 @@ namespace Swift {
void handleInputChanged();
void handleKeyPressEvent(QKeyEvent* event);
void handleSplitterMoved(int pos, int index);
+ void handleAlertButtonClicked();
private:
void updateTitleWithUnreadCount();
@@ -102,6 +107,9 @@ namespace Swift {
QComboBox* labelsWidget_;
QtTreeWidget* treeWidget_;
QLabel* correctingLabel_;
+ QLabel* alertLabel_;
+ QWidget* alertWidget_;
+ QPushButton* alertButton_;
TabComplete* completer_;
std::vector<SecurityLabelsCatalog::Item> availableLabels_;
bool isCorrection_;
@@ -114,5 +122,6 @@ namespace Swift {
bool inputEnabled_;
IDGenerator id_;
QSplitter *logRosterSplitter_;
+ Tristate correctionEnabled_;
};
}
diff --git a/Swiften/Elements/DiscoInfo.cpp b/Swiften/Elements/DiscoInfo.cpp
index d6afb2d..f54b6bf 100644
--- a/Swiften/Elements/DiscoInfo.cpp
+++ b/Swiften/Elements/DiscoInfo.cpp
@@ -15,6 +15,8 @@ const std::string DiscoInfo::SecurityLabelsFeature = std::string("urn:xmpp:sec-l
const std::string DiscoInfo::SecurityLabelsCatalogFeature = std::string("urn:xmpp:sec-label:catalog:2");
const std::string DiscoInfo::JabberSearchFeature = std::string("jabber:iq:search");
const std::string DiscoInfo::CommandsFeature = std::string("http://jabber.org/protocol/commands");
+const std::string DiscoInfo::MessageCorrectionFeature = std::string("urn:xmpp:message-correct:0");
+
bool DiscoInfo::Identity::operator<(const Identity& other) const {
diff --git a/Swiften/Elements/DiscoInfo.h b/Swiften/Elements/DiscoInfo.h
index ab09d05..c5c9e1c 100644
--- a/Swiften/Elements/DiscoInfo.h
+++ b/Swiften/Elements/DiscoInfo.h
@@ -22,6 +22,7 @@ namespace Swift {
static const std::string SecurityLabelsCatalogFeature;
static const std::string JabberSearchFeature;
static const std::string CommandsFeature;
+ static const std::string MessageCorrectionFeature;
class Identity {
public: