diff --git a/core/account.cpp b/core/account.cpp index b9f39f5..68c547f 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -468,6 +468,7 @@ void Core::Account::logMessage(const QXmppMessage& msg, const QString& reason) qDebug() << "- state: " << msg.state(); qDebug() << "- stamp: " << msg.stamp(); qDebug() << "- id: " << msg.id(); + qDebug() << "- outOfBandUrl: " << msg.outOfBandUrl(); qDebug() << "- isAttentionRequested: " << msg.isAttentionRequested(); qDebug() << "- isReceiptRequested: " << msg.isReceiptRequested(); qDebug() << "- receiptId: " << msg.receiptId(); @@ -607,6 +608,7 @@ void Core::Account::initializeMessage(Shared::Message& target, const QXmppMessag target.setTo(source.to()); target.setBody(source.body()); target.setForwarded(forwarded); + target.setOutOfBandUrl(source.outOfBandUrl()); if (guessing) { if (target.getFromJid() == getLogin() + "@" + getServer()) { outgoing = true; @@ -624,7 +626,7 @@ void Core::Account::initializeMessage(Shared::Message& target, const QXmppMessag void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg) { - if (msg.id().size() > 0 && msg.body().size() > 0) { + if (msg.id().size() > 0 && (msg.body().size() > 0 || msg.outOfBandUrl().size() > 0)) { std::map::const_iterator itr = achiveQueries.find(queryId); QString jid = itr->second; RosterItem* item = 0; diff --git a/core/archive.cpp b/core/archive.cpp index d38e3c5..9615914 100644 --- a/core/archive.cpp +++ b/core/archive.cpp @@ -57,6 +57,7 @@ void Core::Archive::open(const QString& account) } mdb_env_set_maxdbs(environment, 4); + mdb_env_set_mapsize(environment, 1UL * 1024UL * 1024UL * 1024UL); mdb_env_open(environment, path.toStdString().c_str(), 0, 0664); MDB_txn *txn; diff --git a/core/conference.cpp b/core/conference.cpp index 4e01c25..1305419 100644 --- a/core/conference.cpp +++ b/core/conference.cpp @@ -137,7 +137,7 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name) if (resource == jid) { qDebug() << "Room" << jid << "is reporting of adding itself to the list participants. Not sure what to do with that yet, skipping"; } else { - QXmppPresence pres = room->participantPresence(jid); + QXmppPresence pres = room->participantPresence(p_name); QDateTime lastInteraction = pres.lastUserInteraction(); if (!lastInteraction.isValid()) { lastInteraction = QDateTime::currentDateTime(); @@ -161,7 +161,7 @@ void Core::Conference::onRoomParticipantChanged(const QString& p_name) if (resource == jid) { qDebug() << "Room" << jid << "is reporting of changing his own presence. Not sure what to do with that yet, skipping"; } else { - QXmppPresence pres = room->participantPresence(jid); + QXmppPresence pres = room->participantPresence(p_name); QDateTime lastInteraction = pres.lastUserInteraction(); if (!lastInteraction.isValid()) { lastInteraction = QDateTime::currentDateTime(); diff --git a/core/rosteritem.cpp b/core/rosteritem.cpp index 1c9f5e5..be6ceee 100644 --- a/core/rosteritem.cpp +++ b/core/rosteritem.cpp @@ -71,7 +71,7 @@ void Core::RosterItem::setName(const QString& n) void Core::RosterItem::addMessageToArchive(const Shared::Message& msg) { - if (msg.getId().size() > 0 && msg.getBody().size() > 0) { + if (msg.storable()) { hisoryCache.push_back(msg); } } @@ -184,7 +184,7 @@ void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg) { const QString& id = msg.getId(); if (id.size() > 0) { - if (msg.getBody().size() > 0) { + if (msg.storable()) { switch (archiveState) { case empty: if (archive->addElement(msg)) { diff --git a/global.cpp b/global.cpp index 8ea64b1..1561983 100644 --- a/global.cpp +++ b/global.cpp @@ -240,6 +240,7 @@ void Shared::Message::serialize(QDataStream& data) const data << t; data << outgoing; data << forwarded; + data << oob; } void Shared::Message::deserialize(QDataStream& data) @@ -257,6 +258,7 @@ void Shared::Message::deserialize(QDataStream& data) type = static_cast(t); data >> outgoing; data >> forwarded; + data >> oob; } QString Shared::generateUUID() @@ -274,6 +276,26 @@ void Shared::Message::setCurrentTime() time = QDateTime::currentDateTime(); } +QString Shared::Message::getOutOfBandUrl() const +{ + return oob; +} + +bool Shared::Message::hasOutOfBandUrl() const +{ + return oob.size() > 0; +} + +void Shared::Message::setOutOfBandUrl(const QString& url) +{ + oob = url; +} + +bool Shared::Message::storable() const +{ + return id.size() > 0 && (body.size() > 0 || oob.size()) > 0; +} + QIcon Shared::availabilityIcon(Shared::Availability av, bool big) { const std::deque& fallback = QApplication::palette().window().color().lightnessF() > 0.5 ? diff --git a/global.h b/global.h index bcfce33..5a8c902 100644 --- a/global.h +++ b/global.h @@ -128,6 +128,7 @@ public: void setForwarded(bool fwd); void setType(Type t); void setCurrentTime(); + void setOutOfBandUrl(const QString& url); QString getFrom() const; QString getFromJid() const; @@ -142,6 +143,9 @@ public: bool getOutgoing() const; bool getForwarded() const; Type getType() const; + bool hasOutOfBandUrl() const; + bool storable() const; + QString getOutOfBandUrl() const; QString getPenPalJid() const; QString getPenPalResource() const; @@ -162,6 +166,7 @@ private: Type type; bool outgoing; bool forwarded; + QString oob; }; static const std::deque fallbackAvailabilityThemeIconsLightBig = { diff --git a/main.cpp b/main.cpp index b627ab8..ae50a8d 100644 --- a/main.cpp +++ b/main.cpp @@ -36,7 +36,7 @@ int main(int argc, char *argv[]) QCoreApplication::setOrganizationName("Macaw"); QCoreApplication::setOrganizationDomain("macaw.me"); QCoreApplication::setApplicationName("Squawk"); - QCoreApplication::setApplicationVersion("0.0.1"); + QCoreApplication::setApplicationVersion("0.0.3"); QIcon icon; icon.addFile(":images/logo.svg", QSize(16, 16)); diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 3039215..2887403 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -30,6 +30,7 @@ set(squawkUI_SRC widgets/accounts.cpp widgets/account.cpp widgets/joinconference.cpp + widgets/message.cpp ) # Tell CMake to create the helloworld executable diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 9673983..26ac8ad 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -35,6 +35,8 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co line(new MessageLine(muc)), m_ui(new Ui::Conversation()), ker(), + res(), + vis(), thread(), statusIcon(0), statusLabel(0), @@ -51,6 +53,9 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co statusLabel = m_ui->statusLabel; connect(&ker, SIGNAL(enterPressed()), this, SLOT(onEnterPressed())); + connect(&res, SIGNAL(resized()), this, SLOT(onScrollResize())); + connect(&vis, SIGNAL(shown()), this, SLOT(onScrollResize())); + connect(&vis, SIGNAL(hidden()), this, SLOT(onScrollResize())); connect(m_ui->sendButton, SIGNAL(clicked(bool)), this, SLOT(onEnterPressed())); connect(line, SIGNAL(resize(int)), this, SLOT(onMessagesResize(int))); //connect(m_ui->attachButton, SIGNAL(clicked(bool)), this, SLOT(onAttach())); @@ -59,9 +64,11 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co QScrollBar* vs = m_ui->scrollArea->verticalScrollBar(); m_ui->scrollArea->setWidget(line); + vs->installEventFilter(&vis); vs->setBackgroundRole(QPalette::Base); vs->setAutoFillBackground(true); connect(vs, SIGNAL(valueChanged(int)), this, SLOT(onSliderValueChanged(int))); + m_ui->scrollArea->installEventFilter(&res); applyVisualEffects(); } @@ -236,6 +243,7 @@ void Conversation::showEvent(QShowEvent* event) emit shown(); QWidget::showEvent(event); + } void Conversation::onAttach() @@ -262,3 +270,49 @@ void Conversation::setStatus(const QString& status) { statusLabel->setText(status); } + +void Conversation::onScrollResize() +{ + if (everShown) { + int size = m_ui->scrollArea->width(); + QScrollBar* bar = m_ui->scrollArea->verticalScrollBar(); + if (bar->isVisible()) { + size -= bar->width(); + } + line->setMaximumWidth(size); + } +} + +Resizer::Resizer(QWidget* parent): + QObject(parent) +{ + +} + +bool Resizer::eventFilter(QObject* obj, QEvent* event) +{ + if (event->type() == QEvent::Resize) { + emit resized(); + } + + return false; +} + +bool VisibilityCatcher::eventFilter(QObject* obj, QEvent* event) +{ + if (event->type() == QEvent::Show) { + emit shown(); + } + + if (event->type() == QEvent::Hide) { + emit hidden(); + } + + return false; +} + +VisibilityCatcher::VisibilityCatcher(QWidget* parent): +QObject(parent) +{ +} + diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 1c47758..4d84423 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -42,6 +42,31 @@ signals: void enterPressed(); }; +class Resizer : public QObject { + Q_OBJECT +public: + Resizer(QWidget* parent = nullptr); + +protected: + bool eventFilter(QObject* obj, QEvent* event) override; + +signals: + void resized(); +}; + +class VisibilityCatcher : public QObject { + Q_OBJECT +public: + VisibilityCatcher(QWidget* parent = nullptr); + +protected: + bool eventFilter(QObject* obj, QEvent* event) override; + +signals: + void hidden(); + void shown(); +}; + class Conversation : public QWidget { Q_OBJECT @@ -75,6 +100,7 @@ protected slots: void onSliderValueChanged(int value); void onAttach(); void onFileSelected(); + void onScrollResize(); public: const bool isMuc; @@ -93,6 +119,8 @@ protected: MessageLine* line; QScopedPointer m_ui; KeyEnterReceiver ker; + Resizer res; + VisibilityCatcher vis; QString thread; QLabel* statusIcon; QLabel* statusLabel; diff --git a/ui/widgets/conversation.ui b/ui/widgets/conversation.ui index afd6d8c..5ae4a95 100644 --- a/ui/widgets/conversation.ui +++ b/ui/widgets/conversation.ui @@ -191,7 +191,7 @@ Qt::ScrollBarAlwaysOff - QAbstractScrollArea::AdjustToContents + QAbstractScrollArea::AdjustIgnored true @@ -202,7 +202,7 @@ 0 0 572 - 116 + 123 @@ -339,7 +339,8 @@ - + + .. true diff --git a/ui/widgets/message.cpp b/ui/widgets/message.cpp new file mode 100644 index 0000000..965e548 --- /dev/null +++ b/ui/widgets/message.cpp @@ -0,0 +1,89 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "message.h" + +const QRegExp urlReg("^(?!setBackgroundRole(QPalette::AlternateBase); + body->setAutoFillBackground(true); + + QString bd = msg.getBody(); + //bd.replace(imgReg, ""); + bd.replace(urlReg, "\\1"); + text->setText(bd);; + text->setTextInteractionFlags(text->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); + text->setWordWrap(true); + text->setOpenExternalLinks(true); + + QFont dFont = date->font(); + dFont.setItalic(true); + dFont.setPointSize(dFont.pointSize() - 2); + date->setFont(dFont); + date->setForegroundRole(QPalette::ToolTipText); + + QFont f; + f.setBold(true); + sender->setFont(f); + + bodyLayout->addWidget(sender); + bodyLayout->addWidget(text); + bodyLayout->addWidget(date); + + shadow->setBlurRadius(10); + shadow->setXOffset(1); + shadow->setYOffset(1); + shadow->setColor(Qt::black); + body->setGraphicsEffect(shadow); + + if (outgoing) { + addWidget(body); + addStretch(); + } else { + sender->setAlignment(Qt::AlignRight); + date->setAlignment(Qt::AlignRight); + addStretch(); + addWidget(body); + } +} + +Message::~Message() +{ +} + +QString Message::getId() const +{ + return msg.getId(); +} + +void Message::setSender(const QString& p_sender) +{ + sender->setText(p_sender); +} diff --git a/ui/widgets/message.h b/ui/widgets/message.h new file mode 100644 index 0000000..80f979b --- /dev/null +++ b/ui/widgets/message.h @@ -0,0 +1,53 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MESSAGE_H +#define MESSAGE_H + +#include +#include +#include +#include +#include + +#include "../../global.h" + +/** + * @todo write docs + */ +class Message : public QHBoxLayout +{ + Q_OBJECT +public: + Message(const Shared::Message& source, bool outgoing, const QString& sender, QWidget* parent = nullptr); + ~Message(); + + void setSender(const QString& sender); + QString getId() const; + +private: + Shared::Message msg; + QWidget* body; + QVBoxLayout* bodyLayout; + QLabel* date; + QLabel* sender; + QLabel* text; + QGraphicsDropShadowEffect* shadow; +}; + +#endif // MESSAGE_H diff --git a/ui/widgets/messageline.cpp b/ui/widgets/messageline.cpp index 168e56b..64627ca 100644 --- a/ui/widgets/messageline.cpp +++ b/ui/widgets/messageline.cpp @@ -18,17 +18,15 @@ #include "messageline.h" #include -#include #include -const QRegExp urlReg("^(?!addStretch(); @@ -81,14 +78,56 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg) return invalid; } - Shared::Message* copy = new Shared::Message(msg); - std::pair result = messageOrder.insert(std::make_pair(msg.getTime(), copy)); + QString sender; + bool outgoing; + + if (room) { + if (msg.getFromResource() == myName) { + sender = myName; + outgoing = false; + } else { + sender = msg.getFromResource(); + outgoing = true; + } + } else { + if (msg.getOutgoing()) { + sender = myName; + outgoing = false; + } else { + QString jid = msg.getFromJid(); + std::map::iterator itr = palNames.find(jid); + if (itr != palNames.end()) { + sender = itr->second; + } else { + sender = jid; + } + outgoing = true; + } + } + + Message* message = new Message(msg, outgoing, sender); + + std::pair result = messageOrder.insert(std::make_pair(msg.getTime(), message)); 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; + delete message; return invalid; } - messageIndex.insert(std::make_pair(id, copy)); + if (outgoing) { + if (room) { + + } else { + QString jid = msg.getFromJid(); + std::map::iterator pItr = palMessages.find(jid); + if (pItr == palMessages.end()) { + pItr = palMessages.insert(std::make_pair(jid, Index())).first; + } + pItr->second.insert(std::make_pair(id, message)); + } + } else { + myMessages.insert(std::make_pair(id, message)); + } + messageIndex.insert(std::make_pair(id, message)); int index = std::distance(messageOrder.begin(), result.first); //need to make with binary indexed tree Position res = invalid; if (index == 0) { @@ -103,83 +142,11 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg) index += 1; } - QVBoxLayout* vBox = new QVBoxLayout(); - QHBoxLayout* hBox = new QHBoxLayout(); - QWidget* message = new QWidget(); - message->setLayout(vBox); - message->setBackgroundRole(QPalette::AlternateBase); - message->setAutoFillBackground(true); - - - QString bd = msg.getBody(); - //bd.replace(imgReg, ""); - bd.replace(urlReg, "\\1"); - QLabel* body = new QLabel(bd); - body->setTextInteractionFlags(body->textInteractionFlags() | Qt::TextSelectableByMouse); - QLabel* sender = new QLabel(); - QLabel* time = new QLabel(msg.getTime().toLocalTime().toString()); - QFont dFont = time->font(); - dFont.setItalic(true); - dFont.setPointSize(dFont.pointSize() - 2); - time->setFont(dFont); - time->setForegroundRole(QPalette::ToolTipText); - QFont f; - f.setBold(true); - sender->setFont(f); - - body->setWordWrap(true); - body->setOpenExternalLinks(true); - - vBox->addWidget(sender); - vBox->addWidget(body); - vBox->addWidget(time); - - QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect; - effect->setBlurRadius(10); - effect->setXOffset(1); - effect->setYOffset(1); - effect->setColor(Qt::black); - - message->setGraphicsEffect(effect); - - if (room) { - if (msg.getFromResource() == myName) { - //body->setAlignment(Qt::AlignRight); - sender->setAlignment(Qt::AlignRight); - time->setAlignment(Qt::AlignRight); - sender->setText(myName); - hBox->addStretch(); - hBox->addWidget(message); - } else { - sender->setText(msg.getFromResource()); - hBox->addWidget(message); - hBox->addStretch(); - } - } else { - if (msg.getOutgoing()) { - //body->setAlignment(Qt::AlignRight); - sender->setAlignment(Qt::AlignRight); - time->setAlignment(Qt::AlignRight); - sender->setText(myName); - hBox->addStretch(); - hBox->addWidget(message); - } else { - QString jid = msg.getFromJid(); - std::map::iterator itr = palNames.find(jid); - if (itr != palNames.end()) { - sender->setText(itr->second); - } else { - sender->setText(jid); - } - hBox->addWidget(message); - hBox->addStretch(); - } - } if (res == end) { - layout->addLayout(hBox); + layout->addLayout(message); } else { - layout->insertLayout(index, hBox); + layout->insertLayout(index, message); } return res; @@ -188,6 +155,9 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg) void MessageLine::setMyName(const QString& name) { myName = name; + for (Index::const_iterator itr = myMessages.begin(), end = myMessages.end(); itr != end; ++itr) { + itr->second->setSender(name); + } } void MessageLine::setPalName(const QString& jid, const QString& name) @@ -198,6 +168,13 @@ void MessageLine::setPalName(const QString& jid, const QString& name) } else { itr->second = name; } + + std::map::iterator pItr = palMessages.find(jid); + if (pItr != palMessages.end()) { + for (Index::const_iterator itr = pItr->second.begin(), end = pItr->second.end(); itr != end; ++itr) { + itr->second->setSender(name); + } + } } void MessageLine::resizeEvent(QResizeEvent* event) diff --git a/ui/widgets/messageline.h b/ui/widgets/messageline.h index 59f971e..2715696 100644 --- a/ui/widgets/messageline.h +++ b/ui/widgets/messageline.h @@ -29,7 +29,9 @@ #include #include #include + #include "../global.h" +#include "message.h" class MessageLine : public QWidget { @@ -66,10 +68,12 @@ private: return a->getTime() < b->getTime(); } }; - typedef std::map Order; - typedef std::map Index; + typedef std::map Order; + typedef std::map Index; Index messageIndex; Order messageOrder; + Index myMessages; + std::map palMessages; QVBoxLayout* layout; QString myName;