diff --git a/core/account.cpp b/core/account.cpp index 33623a2..54cf38f 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -7,12 +7,14 @@ using namespace Core; Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, QObject* parent): QObject(parent), name(p_name), + achiveQueries(), client(), config(), presence(), state(Shared::disconnected), groups(), - cm(new QXmppCarbonManager()) + cm(new QXmppCarbonManager()), + am(new QXmppMamManager()) { config.setUser(p_login); config.setDomain(p_server); @@ -35,6 +37,12 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(cm, SIGNAL(messageReceived(const QXmppMessage&)), this, SLOT(onCarbonMessageReceived(const QXmppMessage&))); QObject::connect(cm, SIGNAL(messageSent(const QXmppMessage&)), this, SLOT(onCarbonMessageSent(const QXmppMessage&))); + + client.addExtension(am); + + QObject::connect(am, SIGNAL(archivedMessageReceived(const QString&, const QXmppMessage&)), this, SLOT(onMamMessageReceived(const QString&, const QXmppMessage&))); + QObject::connect(am, SIGNAL(resultsRecieved(const QString&, const QXmppResultSetReply&, bool)), + this, SLOT(onMamResultsReceived(const QString&, const QXmppResultSetReply&, bool))); } Account::~Account() @@ -397,15 +405,15 @@ void Core::Account::sendMessage(const Shared::Message& data) void Core::Account::onCarbonMessageReceived(const QXmppMessage& msg) { - handleChatMessage(msg, false, true); + handleChatMessage(msg, false, true); } void Core::Account::onCarbonMessageSent(const QXmppMessage& msg) { - handleChatMessage(msg, true, true); + handleChatMessage(msg, true, true); } -bool Core::Account::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded) +bool Core::Account::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) { QString body(msg.body()); if (body.size() != 0) { @@ -417,6 +425,13 @@ bool Core::Account::handleChatMessage(const QXmppMessage& msg, bool outgoing, bo sMsg.setTo(msg.to()); sMsg.setBody(body); sMsg.setForwarded(forwarded); + if (guessing) { + if (sMsg.getFromJid() == getLogin() + "@" + getServer()) { + outgoing = true; + } else { + outgoing = false; + } + } sMsg.setOutgoing(outgoing); if (time.isValid()) { sMsg.setTime(time); @@ -435,3 +450,32 @@ bool Core::Account::handleChatMessage(const QXmppMessage& msg, bool outgoing, bo } return false; } + +void Core::Account::onMamMessageReceived(const QString& bareJid, const QXmppMessage& msg) +{ + handleChatMessage(msg, false, true, true); +} + +void Core::Account::requestAchive(const QString& jid) +{ + QXmppResultSetQuery query; + query.setMax(100); + QDateTime from = QDateTime::currentDateTime().addDays(-7); + + QString q = am->retrieveArchivedMessages("", "", jid, from, QDateTime(), query); + achiveQueries.insert(std::make_pair(q, jid)); +} + +void Core::Account::onMamResultsReceived(const QString& queryId, const QXmppResultSetReply& resultSetReply, bool complete) +{ + std::map::const_iterator itr = achiveQueries.find(queryId); + QString jid = itr->second; + achiveQueries.erase(itr); + if (!complete) { + QXmppResultSetQuery q; + q.setAfter(resultSetReply.last()); + q.setMax(100); + QString nQ = am->retrieveArchivedMessages("", "", jid, QDateTime::currentDateTime().addDays(-7), QDateTime(), q); + achiveQueries.insert(std::make_pair(nQ, jid)); + } +} diff --git a/core/account.h b/core/account.h index d15d382..1691be9 100644 --- a/core/account.h +++ b/core/account.h @@ -7,6 +7,7 @@ #include #include +#include #include #include "../global.h" @@ -39,6 +40,7 @@ public: void setAvailability(Shared::Availability avail); QString getFullJid() const; void sendMessage(const Shared::Message& data); + void requestAchive(const QString& jid); signals: void connectionStateChanged(int); @@ -55,12 +57,14 @@ signals: private: QString name; + std::map achiveQueries; QXmppClient client; QXmppConfiguration config; QXmppPresence presence; Shared::ConnectionState state; std::map> groups; QXmppCarbonManager* cm; + QXmppMamManager* am; private slots: void onClientConnected(); @@ -74,10 +78,12 @@ private slots: void onMessageReceived(const QXmppMessage& message); void onCarbonMessageReceived(const QXmppMessage& message); void onCarbonMessageSent(const QXmppMessage& message); + void onMamMessageReceived(const QString& bareJid, const QXmppMessage& message); + void onMamResultsReceived(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete); private: void addedAccount(const QString &bareJid); - bool handleChatMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false); + bool handleChatMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false); }; } diff --git a/core/squawk.cpp b/core/squawk.cpp index 0f2f9a2..25d8eb5 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -220,3 +220,13 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da itr->second->sendMessage(data); } + +void Core::Squawk::requestArchive(const QString& account, const QString& jid) +{ + AccountsMap::const_iterator itr = amap.find(account); + if (itr == amap.end()) { + qDebug("An attempt to request an archive of non existing account, skipping"); + return; + } + itr->second->requestAchive(jid); +} diff --git a/core/squawk.h b/core/squawk.h index 2e19802..9b8235e 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -45,6 +45,7 @@ public slots: void disconnectAccount(const QString& account); void changeState(int state); void sendMessage(const QString& account, const Shared::Message& data); + void requestArchive(const QString& account, const QString& jid); private: typedef std::deque Accounts; diff --git a/main.cpp b/main.cpp index 2944202..15fcf5c 100644 --- a/main.cpp +++ b/main.cpp @@ -35,6 +35,7 @@ int main(int argc, char *argv[]) QObject::connect(&w, SIGNAL(disconnectAccount(const QString&)), squawk, SLOT(disconnectAccount(const QString&))); QObject::connect(&w, SIGNAL(changeState(int)), squawk, SLOT(changeState(int))); QObject::connect(&w, SIGNAL(sendMessage(const QString&, const Shared::Message&)), squawk, SLOT(sendMessage(const QString&, const Shared::Message&))); + QObject::connect(&w, SIGNAL(requestArchive(const QString&, const QString&)), squawk, SLOT(requestArchive(const QString&, const QString&))); QObject::connect(squawk, SIGNAL(newAccount(const QMap&)), &w, SLOT(newAccount(const QMap&))); QObject::connect(squawk, SIGNAL(accountAvailabilityChanged(const QString&, int)), &w, SLOT(accountAvailabilityChanged(const QString&, int))); diff --git a/ui/conversation.cpp b/ui/conversation.cpp index a0e945b..4ac86f6 100644 --- a/ui/conversation.cpp +++ b/ui/conversation.cpp @@ -24,11 +24,12 @@ Conversation::Conversation(Models::Contact* p_contact, QWidget* parent): QWidget(parent), contact(p_contact), - m_ui(new Ui::Conversation), line(new MessageLine()), + m_ui(new Ui::Conversation()), ker(), activePalResource(), - thread() + thread(), + scroll(nothing) { m_ui->setupUi(this); m_ui->splitter->setSizes({300, 0}); @@ -51,9 +52,11 @@ Conversation::Conversation(Models::Contact* p_contact, QWidget* parent): } line->setMyName(p_contact->getAccountName()); + connect(line, SIGNAL(resize(int)), this, SLOT(onMessagesResize(int))); m_ui->scrollArea->setWidget(line); m_ui->scrollArea->verticalScrollBar()->setBackgroundRole(QPalette::Base); + m_ui->scrollArea->verticalScrollBar()->setAutoFillBackground(true);; } Conversation::~Conversation() @@ -110,10 +113,17 @@ void Conversation::addMessage(const Shared::Message& data) { int pos = m_ui->scrollArea->verticalScrollBar()->sliderPosition(); int max = m_ui->scrollArea->verticalScrollBar()->maximum(); - line->message(data); + MessageLine::Position place = line->message(data); + if (place == MessageLine::invalid) { + return; + } - if (pos == max) { - m_ui->scrollArea->verticalScrollBar()->setSliderPosition(m_ui->scrollArea->verticalScrollBar()->maximum()); + if (scroll == nothing) { + if (pos == max) { + scroll = down; + } else if (place != MessageLine::end) { //todo make some better handling of that situation + scroll = keep; + } } if (!data.getOutgoing()) { const QString& res = data.getPenPalResource(); @@ -172,13 +182,28 @@ void Conversation::onEnterPressed() Shared::Message msg(Shared::Message::chat); msg.setFromJid(aJid); msg.setFromResource(contact->getAccountResource()); - qDebug() << "sending message from " << contact->getAccountResource(); msg.setToJid(contact->getJid()); msg.setToResource(activePalResource); msg.setBody(body); msg.setOutgoing(true); msg.generateRandomId(); - line->message(msg); + addMessage(msg); emit sendMessage(msg); } } + +void Conversation::onMessagesResize(int amount) +{ + switch (scroll) { + case down: + m_ui->scrollArea->verticalScrollBar()->setValue(m_ui->scrollArea->verticalScrollBar()->maximum()); + break; + case keep: + m_ui->scrollArea->verticalScrollBar()->setValue(m_ui->scrollArea->verticalScrollBar()->value() - amount); + break; + default: + break; + } + scroll = nothing; +} + diff --git a/ui/conversation.h b/ui/conversation.h index 99e6c26..b9b8b1c 100644 --- a/ui/conversation.h +++ b/ui/conversation.h @@ -68,14 +68,21 @@ protected: protected slots: void onContactChanged(Models::Item* item, int row, int col); void onEnterPressed(); + void onMessagesResize(int amount); private: + enum Scroll { + nothing, + keep, + down + }; Models::Contact* contact; MessageLine* line; QScopedPointer m_ui; KeyEnterReceiver ker; QString activePalResource; QString thread; + Scroll scroll; }; #endif // CONVERSATION_H diff --git a/ui/messageline.cpp b/ui/messageline.cpp index c73d6e4..4fd7205 100644 --- a/ui/messageline.cpp +++ b/ui/messageline.cpp @@ -17,6 +17,7 @@ */ #include "messageline.h" +#include MessageLine::MessageLine(QWidget* parent): QWidget(parent), @@ -24,7 +25,8 @@ MessageLine::MessageLine(QWidget* parent): messageOrder(), layout(new QVBoxLayout()), myName(), - palNames() + palNames(), + views() { setLayout(layout); setBackgroundRole(QPalette::Base); @@ -33,10 +35,38 @@ MessageLine::MessageLine(QWidget* parent): MessageLine::~MessageLine() { + for (Index::const_iterator itr = messageIndex.begin(), end = messageIndex.end(); itr != end; ++itr) { + delete itr->second; + } } -void MessageLine::message(const Shared::Message& msg) +MessageLine::Position MessageLine::message(const Shared::Message& msg) { + QString id = msg.getId(); + Index::iterator itr = messageIndex.find(id); + if (itr != messageIndex.end()) { + qDebug() << "received more then one message with the same id, skipping yet the new one"; + return invalid; + } + + Shared::Message* copy = new Shared::Message(msg); + std::pair result = messageOrder.insert(std::make_pair(msg.getTime(), copy)); + if (!result.second) { + qDebug() << "Error appending a message into a message list - seems like the time of that message exactly matches the time of some other message, can't put them in order, skipping yet"; + delete copy; + return invalid; + } + messageIndex.insert(std::make_pair(id, copy)); + int index = std::distance(messageOrder.begin(), result.first); //need to make with binary indexed tree + Position res = invalid; + if (index == 0) { + res = beggining; + } else if (index == messageIndex.size() - 1) { + res = end; + } else { + res = middle; + } + QVBoxLayout* vBox = new QVBoxLayout(); QHBoxLayout* hBox = new QHBoxLayout(); QWidget* message = new QWidget(); @@ -73,7 +103,13 @@ void MessageLine::message(const Shared::Message& msg) hBox->addStretch(); } - layout->addLayout(hBox); + if (res == end) { + layout->addLayout(hBox); + } else { + layout->insertLayout(index, hBox); + } + + return res; } void MessageLine::setMyName(const QString& name) @@ -90,3 +126,9 @@ void MessageLine::setPalName(const QString& jid, const QString& name) itr->second = name; } } + +void MessageLine::resizeEvent(QResizeEvent* event) +{ + QWidget::resizeEvent(event); + emit resize(event->size().height() - event->oldSize().height()); +} diff --git a/ui/messageline.h b/ui/messageline.h index 021f60d..c0fd0d5 100644 --- a/ui/messageline.h +++ b/ui/messageline.h @@ -21,30 +21,52 @@ #include #include +#include #include +#include #include "../global.h" -#include "../order.h" - class MessageLine : public QWidget { Q_OBJECT public: + enum Position { + beggining, + middle, + end, + invalid + }; MessageLine(QWidget* parent = 0); ~MessageLine(); - void message(const Shared::Message& msg); + Position message(const Shared::Message& msg); void setMyName(const QString& name); void setPalName(const QString& jid, const QString& name); +signals: + void resize(int amount); + +protected: + void resizeEvent(QResizeEvent * event) override; + private: - typedef W::Order Order; - std::map messageIndex; + struct Comparator { + bool operator()(const Shared::Message& a, const Shared::Message& b) const { + return a.getTime() < b.getTime(); + } + bool operator()(const Shared::Message* a, const Shared::Message* b) const { + return a->getTime() < b->getTime(); + } + }; + typedef std::map Order; + typedef std::map Index; + Index messageIndex; Order messageOrder; QVBoxLayout* layout; QString myName; std::map palNames; + std::deque views; }; #endif // MESSAGELINE_H diff --git a/ui/squawk.cpp b/ui/squawk.cpp index da9f551..0d91ca3 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -198,6 +198,7 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) if (res.size() > 0) { itr->second->setPalResource(res); } + requestArchive(account, jid); } } } diff --git a/ui/squawk.h b/ui/squawk.h index f854fb2..b9d53bd 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -31,6 +31,7 @@ signals: void disconnectAccount(const QString&); void changeState(int state); void sendMessage(const QString& account, const Shared::Message& data); + void requestArchive(const QString& account, const QString& jid); public slots: void newAccount(const QMap& account);