From 6bd059ce63bb52632b866b7a43cc96d430d09c1a Mon Sep 17 00:00:00 2001 From: Black Hat Date: Mon, 9 Jul 2018 10:45:26 +0800 Subject: [PATCH] Code reformatting && tooltip. --- main.cpp | 52 +- matrix/controller.cpp | 88 ++-- matrix/controller.h | 154 +++--- matrix/imageprovider.cpp | 87 ++-- matrix/imageprovider.h | 27 +- matrix/imageproviderconnection.cpp | 9 +- matrix/imageproviderconnection.h | 35 +- matrix/messageeventmodel.cpp | 761 +++++++++++++---------------- matrix/messageeventmodel.h | 83 ++-- matrix/roomlistmodel.cpp | 140 +++--- matrix/roomlistmodel.h | 50 +- qml/Login.qml | 20 +- qml/Room.qml | 4 +- qml/component/ButtonDelegate.qml | 4 +- qml/component/ImageStatus.qml | 46 +- qml/component/MaterialIcon.qml | 10 +- qml/component/SideNav.qml | 8 +- qml/component/SideNavButton.qml | 10 +- qml/form/DetailForm.qml | 8 +- qml/form/ListForm.qml | 19 +- qml/form/RoomForm.qml | 40 +- qml/main.qml | 25 +- 22 files changed, 826 insertions(+), 854 deletions(-) diff --git a/main.cpp b/main.cpp index 7150ccf..36b3e73 100644 --- a/main.cpp +++ b/main.cpp @@ -1,50 +1,50 @@ #include -#include #include +#include #include -#include "room.h" #include "matrix/controller.h" -#include "matrix/roomlistmodel.h" #include "matrix/imageprovider.h" #include "matrix/messageeventmodel.h" +#include "matrix/roomlistmodel.h" +#include "room.h" using namespace QMatrixClient; -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { #if defined(Q_OS_WIN) - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif - QGuiApplication app(argc, argv); + QGuiApplication app(argc, argv); -// Enable this if you need proxy. -// QNetworkProxy proxy; -// proxy.setType(QNetworkProxy::HttpProxy); -// proxy.setHostName("localhost"); -// proxy.setPort(1082); -// QNetworkProxy::setApplicationProxy(proxy); + // Enable this if you need proxy. + // QNetworkProxy proxy; + // proxy.setType(QNetworkProxy::HttpProxy); + // proxy.setHostName("localhost"); + // proxy.setPort(1082); + // QNetworkProxy::setApplicationProxy(proxy); - qmlRegisterType(); qRegisterMetaType ("Room*"); + qmlRegisterType(); + qRegisterMetaType("Room*"); - qmlRegisterType("Matrique", 0, 1, "Controller"); - qmlRegisterType("Matrique", 0, 1, "RoomListModel"); - qmlRegisterType("Matrique", 0, 1, "MessageEventModel"); - qRegisterMetaType("User*"); + qmlRegisterType("Matrique", 0, 1, "Controller"); + qmlRegisterType("Matrique", 0, 1, "RoomListModel"); + qmlRegisterType("Matrique", 0, 1, "MessageEventModel"); + qRegisterMetaType("User*"); - QQmlApplicationEngine engine; + QQmlApplicationEngine engine; - ImageProvider* m_provider = new ImageProvider(); + ImageProvider *m_provider = new ImageProvider(); - engine.rootContext()->setContextProperty("imageProvider", m_provider->getConnection()); + engine.rootContext()->setContextProperty("imageProvider", + m_provider->getConnection()); - engine.addImageProvider(QLatin1String("mxc"), m_provider); + engine.addImageProvider(QLatin1String("mxc"), m_provider); - engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); + engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); - if (engine.rootObjects().isEmpty()) - return -1; + if (engine.rootObjects().isEmpty()) return -1; - return app.exec(); + return app.exec(); } diff --git a/matrix/controller.cpp b/matrix/controller.cpp index 0b86fb5..d882ef5 100644 --- a/matrix/controller.cpp +++ b/matrix/controller.cpp @@ -3,67 +3,71 @@ #include "connection.h" Controller::Controller(QObject *parent) : QObject(parent) { - connect(m_connection, &QMatrixClient::Connection::connected, this, &Controller::connected); - connect(m_connection, &QMatrixClient::Connection::resolveError, this, &Controller::reconnect); - connect(m_connection, &QMatrixClient::Connection::syncError, this, &Controller::reconnect); - connect(m_connection, &QMatrixClient::Connection::syncDone, this, &Controller::resync); - connect(m_connection, &QMatrixClient::Connection::connected, this, &Controller::connectionChanged); + connect(m_connection, &QMatrixClient::Connection::connected, this, + &Controller::connected); + connect(m_connection, &QMatrixClient::Connection::resolveError, this, + &Controller::reconnect); + connect(m_connection, &QMatrixClient::Connection::syncError, this, + &Controller::reconnect); + connect(m_connection, &QMatrixClient::Connection::syncDone, this, + &Controller::resync); + connect(m_connection, &QMatrixClient::Connection::connected, this, + &Controller::connectionChanged); } -Controller::~Controller() { - m_connection->stopSync(); -} +Controller::~Controller() { m_connection->stopSync(); } void Controller::login() { - if (!isLogin) { - qDebug() << "UserID:" << userID; - qDebug() << "Token:" << token; + if (!isLogin) { + qDebug() << "UserID:" << userID; + qDebug() << "Token:" << token; - m_connection->setHomeserver(QUrl(homeserver)); - m_connection->connectWithToken(userID, token, ""); - } + m_connection->setHomeserver(QUrl(homeserver)); + m_connection->connectWithToken(userID, token, ""); + } } -void Controller::loginWithCredentials(QString serverAddr, QString user, QString pass) { - if(!isLogin) { - qDebug() << "Server:" << serverAddr; - qDebug() << "User:" << user; - qDebug() << "Pass:" << pass; +void Controller::loginWithCredentials(QString serverAddr, QString user, + QString pass) { + if (!isLogin) { + qDebug() << "Server:" << serverAddr; + qDebug() << "User:" << user; + qDebug() << "Pass:" << pass; - if(!user.isEmpty() && !pass.isEmpty()) { - qDebug() << "Using given credential."; - m_connection->setHomeserver(QUrl(serverAddr)); - m_connection->connectToServer(user, pass, ""); - } - } else { - qDebug() << "You are already logged in."; + if (!user.isEmpty() && !pass.isEmpty()) { + qDebug() << "Using given credential."; + m_connection->setHomeserver(QUrl(serverAddr)); + m_connection->connectToServer(user, pass, ""); } + } else { + qDebug() << "You are already logged in."; + } } void Controller::logout() { - qDebug() << "Logging out."; - setUserID(""); - setToken(""); - setIsLogin(false); + qDebug() << "Logging out."; + setUserID(""); + setToken(""); + setIsLogin(false); } void Controller::connected() { - qDebug() << "Logged in."; - setHomeserver(m_connection->homeserver().toString()); - setUserID(m_connection->userId()); - setToken(m_connection->accessToken()); - m_connection->loadState(); - resync(); - setIsLogin(true); + qDebug() << "Logged in."; + setHomeserver(m_connection->homeserver().toString()); + setUserID(m_connection->userId()); + setToken(m_connection->accessToken()); + m_connection->loadState(); + resync(); + setIsLogin(true); } void Controller::resync() { - qDebug() << "Syncing Matrix."; - m_connection->sync(30000); - m_connection->saveState(); + qDebug() << "Syncing Matrix."; + m_connection->sync(30000); + m_connection->saveState(); } void Controller::reconnect() { - qDebug() << "Connection lost. Reconnecting..."; - m_connection->connectWithToken(userID, token, ""); + qDebug() << "Connection lost. Reconnecting..."; + m_connection->connectWithToken(userID, token, ""); } diff --git a/matrix/controller.h b/matrix/controller.h index a34d3f9..0cf5f14 100644 --- a/matrix/controller.h +++ b/matrix/controller.h @@ -3,99 +3,101 @@ #include #include "connection.h" -#include "user.h" #include "roomlistmodel.h" +#include "user.h" namespace QMatrixClient { - class Connection; +class Connection; } -class Controller : public QObject -{ - Q_OBJECT +class Controller : public QObject { + Q_OBJECT - Q_PROPERTY(QMatrixClient::Connection* connection READ getConnection NOTIFY connectionChanged) - Q_PROPERTY(bool isLogin READ getIsLogin WRITE setIsLogin NOTIFY isLoginChanged) - Q_PROPERTY(QString homeserver READ getHomeserver WRITE setHomeserver NOTIFY homeserverChanged) - Q_PROPERTY(QString userID READ getUserID WRITE setUserID NOTIFY userIDChanged) - Q_PROPERTY(QByteArray token READ getToken WRITE setToken NOTIFY tokenChanged) - Q_PROPERTY(bool busy READ getBusy WRITE setBusy NOTIFY busyChanged) + Q_PROPERTY(QMatrixClient::Connection* connection READ getConnection NOTIFY + connectionChanged) + Q_PROPERTY( + bool isLogin READ getIsLogin WRITE setIsLogin NOTIFY isLoginChanged) + Q_PROPERTY(QString homeserver READ getHomeserver WRITE setHomeserver NOTIFY + homeserverChanged) + Q_PROPERTY(QString userID READ getUserID WRITE setUserID NOTIFY userIDChanged) + Q_PROPERTY(QByteArray token READ getToken WRITE setToken NOTIFY tokenChanged) + Q_PROPERTY(bool busy READ getBusy WRITE setBusy NOTIFY busyChanged) - public: - explicit Controller(QObject *parent = nullptr); - ~Controller(); + public: + explicit Controller(QObject* parent = nullptr); + ~Controller(); - // All the Q_INVOKABLEs. - Q_INVOKABLE void login(); - Q_INVOKABLE void loginWithCredentials(QString, QString, QString); - Q_INVOKABLE void logout(); + // All the Q_INVOKABLEs. + Q_INVOKABLE void login(); + Q_INVOKABLE void loginWithCredentials(QString, QString, QString); + Q_INVOKABLE void logout(); - // All the non-Q_INVOKABLE functions. + // All the non-Q_INVOKABLE functions. - // All the Q_PROPERTYs. - QMatrixClient::Connection* m_connection = new QMatrixClient::Connection(); - QMatrixClient::Connection* getConnection() { return m_connection; } + // All the Q_PROPERTYs. + QMatrixClient::Connection* m_connection = new QMatrixClient::Connection(); + QMatrixClient::Connection* getConnection() { return m_connection; } - bool isLogin = false; - bool getIsLogin() { return isLogin; } - void setIsLogin(bool n) { - if(n != isLogin) { - isLogin = n; - emit isLoginChanged(); - } - } + bool isLogin = false; + bool getIsLogin() { return isLogin; } + void setIsLogin(bool n) { + if (n != isLogin) { + isLogin = n; + emit isLoginChanged(); + } + } - QString userID; - QString getUserID() { return userID; } - void setUserID(QString n) { - if(n != userID) { - userID = n; - emit userIDChanged(); - } - } + QString userID; + QString getUserID() { return userID; } + void setUserID(QString n) { + if (n != userID) { + userID = n; + emit userIDChanged(); + } + } - QByteArray token; - QByteArray getToken() { return token; } - void setToken(QByteArray n) { - if(n != token) { - token = n; - emit tokenChanged(); - } - } + QByteArray token; + QByteArray getToken() { return token; } + void setToken(QByteArray n) { + if (n != token) { + token = n; + emit tokenChanged(); + } + } - QString homeserver; - QString getHomeserver() { return homeserver; } - void setHomeserver(QString n) { - if (n != homeserver) { - homeserver = n; - emit homeserverChanged(); - } - } + QString homeserver; + QString getHomeserver() { return homeserver; } + void setHomeserver(QString n) { + if (n != homeserver) { + homeserver = n; + emit homeserverChanged(); + } + } - bool busy = false; - bool getBusy() { return busy; } - void setBusy(bool b) { - if (b != busy) { - busy = b; - emit busyChanged(); - } - } + bool busy = false; + bool getBusy() { return busy; } + void setBusy(bool b) { + if (b != busy) { + busy = b; + emit busyChanged(); + } + } - private: - void connected(); - void resync(); - void reconnect(); + private: + void connected(); + void resync(); + void reconnect(); - signals: - void connectionChanged(); - void isLoginChanged(); - void userIDChanged(); - void tokenChanged(); - void homeserverChanged(); - void busyChanged(); - void errorOccured(); + signals: + void connectionChanged(); + void isLoginChanged(); + void userIDChanged(); + void tokenChanged(); + void homeserverChanged(); + void busyChanged(); + void errorOccured(); - public slots: + public slots: }; -#endif // CONTROLLER_H +#endif // CONTROLLER_H diff --git a/matrix/imageprovider.cpp b/matrix/imageprovider.cpp index be76a40..1840450 100644 --- a/matrix/imageprovider.cpp +++ b/matrix/imageprovider.cpp @@ -1,8 +1,8 @@ #include "imageprovider.h" -#include -#include #include +#include +#include #include "jobs/mediathumbnailjob.h" @@ -11,58 +11,57 @@ using QMatrixClient::MediaThumbnailJob; ImageProvider::ImageProvider(QObject* parent) - : QQuickImageProvider(QQmlImageProviderBase::Image, - QQmlImageProviderBase::ForceAsynchronousImageLoading) -{ + : QQuickImageProvider( + QQmlImageProviderBase::Image, + QQmlImageProviderBase::ForceAsynchronousImageLoading) { #if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0)) - qRegisterMetaType(); + qRegisterMetaType(); #endif - m_connection = new ImageProviderConnection(); + m_connection = new ImageProviderConnection(); } -QImage ImageProvider::requestImage(const QString& id, - QSize* pSize, const QSize& requestedSize) -{ - if (!id.startsWith("mxc://")) - { - qWarning() << "ImageProvider: won't fetch an invalid id:" << id - << "doesn't follow server/mediaId pattern"; - return {}; - } +QImage ImageProvider::requestImage(const QString& id, QSize* pSize, + const QSize& requestedSize) { + if (!id.startsWith("mxc://")) { + qWarning() << "ImageProvider: won't fetch an invalid id:" << id + << "doesn't follow server/mediaId pattern"; + return {}; + } - QUrl mxcUri { id }; - qDebug() << "ImageProvider::requestImage:" << mxcUri.toString(); + QUrl mxcUri{id}; - MediaThumbnailJob* job = nullptr; - QReadLocker locker(&m_lock); + MediaThumbnailJob* job = nullptr; + QReadLocker locker(&m_lock); #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) - QMetaObject::invokeMethod(m_connection, - [=] { return m_connection->getConnection()->getThumbnail(mxcUri, requestedSize); }, - Qt::BlockingQueuedConnection, &job); + QMetaObject::invokeMethod( + m_connection, + [=] { + return m_connection->getConnection()->getThumbnail(mxcUri, + requestedSize); + }, + Qt::BlockingQueuedConnection, &job); #else - QMetaObject::invokeMethod(m_connection, "getThumbnail", - Qt::BlockingQueuedConnection, Q_RETURN_ARG(MediaThumbnailJob*, job), - Q_ARG(QUrl, mxcUri), Q_ARG(QSize, requestedSize)); + QMetaObject::invokeMethod(m_connection, "getThumbnail", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(MediaThumbnailJob*, job), + Q_ARG(QUrl, mxcUri), Q_ARG(QSize, requestedSize)); #endif - if (!job) - { - qDebug() << "ImageProvider: failed to send a request"; - return {}; - } - QImage result; - { - QWaitCondition condition; // The most compact way to block on a signal - job->connect(job, &MediaThumbnailJob::finished, job, [&] { - result = job->thumbnail(); - condition.wakeAll(); - }); - condition.wait(&m_lock); - } + if (!job) { + qDebug() << "ImageProvider: failed to send a request"; + return {}; + } + QImage result; + { + QWaitCondition condition; // The most compact way to block on a signal + job->connect(job, &MediaThumbnailJob::finished, job, [&] { + result = job->thumbnail(); + condition.wakeAll(); + }); + condition.wait(&m_lock); + } - if(pSize != nullptr) - *pSize = result.size(); + if (pSize != nullptr) *pSize = result.size(); - return result; + return result; } - diff --git a/matrix/imageprovider.h b/matrix/imageprovider.h index dafb2b7..6133b09 100644 --- a/matrix/imageprovider.h +++ b/matrix/imageprovider.h @@ -1,28 +1,27 @@ #ifndef IMAGEPROVIDER_H #define IMAGEPROVIDER_H -#include -#include #include +#include +#include #include "connection.h" #include "imageproviderconnection.h" -class ImageProvider: public QQuickImageProvider -{ - public: - explicit ImageProvider(QObject* parent = nullptr); +class ImageProvider : public QQuickImageProvider { + public: + explicit ImageProvider(QObject* parent = nullptr); - QImage requestImage(const QString& id, QSize* pSize, - const QSize& requestedSize) override; + QImage requestImage(const QString& id, QSize* pSize, + const QSize& requestedSize) override; - void initializeEngine(QQmlEngine *engine, const char *uri); + void initializeEngine(QQmlEngine* engine, const char* uri); - ImageProviderConnection* getConnection() { return m_connection; } + ImageProviderConnection* getConnection() { return m_connection; } - private: - QReadWriteLock m_lock; - ImageProviderConnection* m_connection; + private: + QReadWriteLock m_lock; + ImageProviderConnection* m_connection; }; -#endif // IMAGEPROVIDER_H +#endif // IMAGEPROVIDER_H diff --git a/matrix/imageproviderconnection.cpp b/matrix/imageproviderconnection.cpp index 920c06f..40f3db0 100644 --- a/matrix/imageproviderconnection.cpp +++ b/matrix/imageproviderconnection.cpp @@ -1,9 +1,6 @@ #include "imageproviderconnection.h" -ImageProviderConnection::ImageProviderConnection(QObject* parent) : QObject(parent) { +ImageProviderConnection::ImageProviderConnection(QObject* parent) + : QObject(parent) {} -} - -ImageProviderConnection::~ImageProviderConnection() { - -} +ImageProviderConnection::~ImageProviderConnection() {} diff --git a/matrix/imageproviderconnection.h b/matrix/imageproviderconnection.h index 7d153c6..56a2ede 100644 --- a/matrix/imageproviderconnection.h +++ b/matrix/imageproviderconnection.h @@ -5,25 +5,26 @@ #include "connection.h" -class ImageProviderConnection : public QObject -{ - Q_OBJECT +class ImageProviderConnection : public QObject { + Q_OBJECT - Q_PROPERTY(QMatrixClient::Connection* connection READ getConnection WRITE setConnection NOTIFY connectionChanged) + Q_PROPERTY(QMatrixClient::Connection* connection READ getConnection WRITE + setConnection NOTIFY connectionChanged) - public: - explicit ImageProviderConnection(QObject* parent = nullptr); - ~ImageProviderConnection(); + public: + explicit ImageProviderConnection(QObject* parent = nullptr); + ~ImageProviderConnection(); - QMatrixClient::Connection* getConnection() { return m_connection; } - void setConnection(QMatrixClient::Connection* connection) { - emit connectionChanged(); - m_connection = connection; - } - private: - QMatrixClient::Connection* m_connection; - signals: - void connectionChanged(); + QMatrixClient::Connection* getConnection() { return m_connection; } + void setConnection(QMatrixClient::Connection* connection) { + emit connectionChanged(); + m_connection = connection; + } + + private: + QMatrixClient::Connection* m_connection; + signals: + void connectionChanged(); }; -#endif // IMAGEPROVIDERCONNECTION_H +#endif // IMAGEPROVIDERCONNECTION_H diff --git a/matrix/messageeventmodel.cpp b/matrix/messageeventmodel.cpp index 9243ae6..e4e2edf 100644 --- a/matrix/messageeventmodel.cpp +++ b/matrix/messageeventmodel.cpp @@ -1,452 +1,379 @@ #include "messageeventmodel.h" -#include #include -#include // for qmlRegisterType() +#include +#include // for qmlRegisterType() -#include "events/roommemberevent.h" -#include "events/simplestateevents.h" #include "events/redactionevent.h" #include "events/roomavatarevent.h" +#include "events/roommemberevent.h" +#include "events/simplestateevents.h" #include "connection.h" -#include "user.h" #include "settings.h" +#include "user.h" MessageEventModel::MessageEventModel(QObject* parent) - : QAbstractListModel(parent) -{ - qmlRegisterType(); - qRegisterMetaType(); + : QAbstractListModel(parent) { + qmlRegisterType(); + qRegisterMetaType(); } -MessageEventModel::~MessageEventModel() -{ +MessageEventModel::~MessageEventModel() {} +void MessageEventModel::setRoom(QMatrixClient::Room* room) { + if (room == m_currentRoom) return; + + beginResetModel(); + if (m_currentRoom) { + m_currentRoom->disconnect(this); + qDebug() << "Disconnected from" << m_currentRoom->id(); + } + + m_currentRoom = room; + if (room) { + lastReadEventId = room->readMarkerEventId(); + using namespace QMatrixClient; + connect(m_currentRoom, &Room::aboutToAddNewMessages, this, + [=](RoomEventsRange events) { + beginInsertRows(QModelIndex(), 0, int(events.size()) - 1); + }); + connect(m_currentRoom, &Room::aboutToAddHistoricalMessages, this, + [=](RoomEventsRange events) { + if (rowCount() > 0) nextNewerRow = rowCount() - 1; + beginInsertRows(QModelIndex(), rowCount(), + rowCount() + int(events.size()) - 1); + }); + connect(m_currentRoom, &Room::addedMessages, this, [=] { + if (nextNewerRow > -1) { + const auto idx = index(nextNewerRow); + emit dataChanged(idx, idx); + nextNewerRow = -1; + } + endInsertRows(); + }); + connect(m_currentRoom, &Room::readMarkerMoved, this, [this] { + refreshEventRoles( + std::exchange(lastReadEventId, m_currentRoom->readMarkerEventId()), + {ReadMarkerRole}); + refreshEventRoles(lastReadEventId, {ReadMarkerRole}); + }); + connect( + m_currentRoom, &Room::replacedEvent, this, + [this](const RoomEvent* newEvent) { refreshEvent(newEvent->id()); }); + connect(m_currentRoom, &Room::fileTransferProgress, this, + &MessageEventModel::refreshEvent); + connect(m_currentRoom, &Room::fileTransferCompleted, this, + &MessageEventModel::refreshEvent); + connect(m_currentRoom, &Room::fileTransferFailed, this, + &MessageEventModel::refreshEvent); + connect(m_currentRoom, &Room::fileTransferCancelled, this, + &MessageEventModel::refreshEvent); + qDebug() << "Connected to room" << room->id() << "as" + << room->localUser()->id(); + } else + lastReadEventId.clear(); + endResetModel(); + emit roomChanged(); } -void MessageEventModel::setRoom(QMatrixClient::Room* room) -{ - if (room == m_currentRoom) - return; - - beginResetModel(); - if( m_currentRoom ) - { - m_currentRoom->disconnect( this ); - qDebug() << "Disconnected from" << m_currentRoom->id(); - } - - m_currentRoom = room; - if( room ) - { - lastReadEventId = room->readMarkerEventId(); - using namespace QMatrixClient; - connect(m_currentRoom, &Room::aboutToAddNewMessages, this, - [=](RoomEventsRange events) - { - beginInsertRows(QModelIndex(), 0, int(events.size()) - 1); - }); - connect(m_currentRoom, &Room::aboutToAddHistoricalMessages, this, - [=](RoomEventsRange events) - { - if (rowCount() > 0) - nextNewerRow = rowCount() - 1; - beginInsertRows(QModelIndex(), rowCount(), - rowCount() + int(events.size()) - 1); - }); - connect(m_currentRoom, &Room::addedMessages, this, - [=] { - if (nextNewerRow > -1) - { - const auto idx = index(nextNewerRow); - emit dataChanged(idx, idx); - nextNewerRow = -1; - } - endInsertRows(); - }); - connect(m_currentRoom, &Room::readMarkerMoved, this, [this] { - refreshEventRoles( - std::exchange(lastReadEventId, - m_currentRoom->readMarkerEventId()), - {ReadMarkerRole}); - refreshEventRoles(lastReadEventId, {ReadMarkerRole}); - }); - connect(m_currentRoom, &Room::replacedEvent, this, - [this] (const RoomEvent* newEvent) { - refreshEvent(newEvent->id()); - }); - connect(m_currentRoom, &Room::fileTransferProgress, - this, &MessageEventModel::refreshEvent); - connect(m_currentRoom, &Room::fileTransferCompleted, - this, &MessageEventModel::refreshEvent); - connect(m_currentRoom, &Room::fileTransferFailed, - this, &MessageEventModel::refreshEvent); - connect(m_currentRoom, &Room::fileTransferCancelled, - this, &MessageEventModel::refreshEvent); - qDebug() << "Connected to room" << room->id() - << "as" << room->localUser()->id(); - } else - lastReadEventId.clear(); - endResetModel(); - emit roomChanged(); -} - -void MessageEventModel::refreshEvent(const QString& eventId) -{ - refreshEventRoles(eventId, {}); +void MessageEventModel::refreshEvent(const QString& eventId) { + refreshEventRoles(eventId, {}); } void MessageEventModel::refreshEventRoles(const QString& eventId, - const QVector roles) -{ - const auto it = m_currentRoom->findInTimeline(eventId); - if (it != m_currentRoom->timelineEdge()) - { - const auto row = it - m_currentRoom->messageEvents().rbegin(); - emit dataChanged(index(row), index(row), roles); - } + const QVector roles) { + const auto it = m_currentRoom->findInTimeline(eventId); + if (it != m_currentRoom->timelineEdge()) { + const auto row = it - m_currentRoom->messageEvents().rbegin(); + emit dataChanged(index(row), index(row), roles); + } } -inline bool hasValidTimestamp(const QMatrixClient::TimelineItem& ti) -{ - return ti->timestamp().isValid(); +inline bool hasValidTimestamp(const QMatrixClient::TimelineItem& ti) { + return ti->timestamp().isValid(); } -QDateTime MessageEventModel::makeMessageTimestamp(QMatrixClient::Room::rev_iter_t baseIt) const -{ - const auto& timeline = m_currentRoom->messageEvents(); - auto ts = baseIt->event()->timestamp(); - if (ts.isValid()) - return ts; +QDateTime MessageEventModel::makeMessageTimestamp( + QMatrixClient::Room::rev_iter_t baseIt) const { + const auto& timeline = m_currentRoom->messageEvents(); + auto ts = baseIt->event()->timestamp(); + if (ts.isValid()) return ts; - // The event is most likely redacted or just invalid. - // Look for the nearest date around and slap zero time to it. - using QMatrixClient::TimelineItem; - auto rit = std::find_if(baseIt, timeline.rend(), - hasValidTimestamp); - if (rit != timeline.rend()) - return { rit->event()->timestamp().date(), {0,0}, Qt::LocalTime }; - auto it = std::find_if(baseIt.base(), timeline.end(), hasValidTimestamp); - if (it != timeline.end()) - return { it->event()->timestamp().date(), {0,0}, Qt::LocalTime }; + // The event is most likely redacted or just invalid. + // Look for the nearest date around and slap zero time to it. + using QMatrixClient::TimelineItem; + auto rit = std::find_if(baseIt, timeline.rend(), hasValidTimestamp); + if (rit != timeline.rend()) + return {rit->event()->timestamp().date(), {0, 0}, Qt::LocalTime}; + auto it = std::find_if(baseIt.base(), timeline.end(), hasValidTimestamp); + if (it != timeline.end()) + return {it->event()->timestamp().date(), {0, 0}, Qt::LocalTime}; - // What kind of room is that?.. - qCritical() << "No valid timestamps in the room timeline!"; - return {}; + // What kind of room is that?.. + qCritical() << "No valid timestamps in the room timeline!"; + return {}; } -QString MessageEventModel::makeDateString(QMatrixClient::Room::rev_iter_t baseIt) const -{ - auto date = makeMessageTimestamp(baseIt).toLocalTime().date(); - if (QMatrixClient::SettingsGroup("UI") - .value("banner_human_friendly_date", true).toBool()) - { - if (date == QDate::currentDate()) - return tr("Today"); - if (date == QDate::currentDate().addDays(-1)) - return tr("Yesterday"); - if (date == QDate::currentDate().addDays(-2)) - return tr("The day before yesterday"); - if (date > QDate::currentDate().addDays(-7)) - return date.toString("dddd"); - } - return date.toString(Qt::DefaultLocaleShortDate); +QString MessageEventModel::makeDateString( + QMatrixClient::Room::rev_iter_t baseIt) const { + auto date = makeMessageTimestamp(baseIt).toLocalTime().date(); + if (QMatrixClient::SettingsGroup("UI") + .value("banner_human_friendly_date", true) + .toBool()) { + if (date == QDate::currentDate()) return tr("Today"); + if (date == QDate::currentDate().addDays(-1)) return tr("Yesterday"); + if (date == QDate::currentDate().addDays(-2)) + return tr("The day before yesterday"); + if (date > QDate::currentDate().addDays(-7)) return date.toString("dddd"); + } + return date.toString(Qt::DefaultLocaleShortDate); } -int MessageEventModel::rowCount(const QModelIndex& parent) const -{ - if(!m_currentRoom || parent.isValid()) - return 0; - return m_currentRoom->timelineSize(); +int MessageEventModel::rowCount(const QModelIndex& parent) const { + if (!m_currentRoom || parent.isValid()) return 0; + return m_currentRoom->timelineSize(); } -QVariant MessageEventModel::data(const QModelIndex& index, int role) const -{ - if( !m_currentRoom || - index.row() < 0 || index.row() >= m_currentRoom->timelineSize()) - return QVariant(); - - const auto timelineIt = m_currentRoom->messageEvents().rbegin() + index.row(); - const auto& ti = *timelineIt; - - using namespace QMatrixClient; - if( role == Qt::DisplayRole ) - { - if (ti->isRedacted()) - { - auto reason = ti->redactedBecause()->reason(); - if (reason.isEmpty()) - return tr("Redacted"); - else - return tr("Redacted: %1") - .arg(ti->redactedBecause()->reason()); - } - - if( ti->type() == EventType::RoomMessage ) - { - using namespace MessageEventContent; - - auto* e = ti.viewAs(); - if (e->hasTextContent() && e->mimeType().name() != "text/plain") - return static_cast(e->content())->body; - if (e->hasFileContent()) - { - auto fileCaption = e->content()->fileInfo()->originalName; - if (fileCaption.isEmpty()) - fileCaption = m_currentRoom->prettyPrint(e->plainBody()); - if (fileCaption.isEmpty()) - return tr("a file"); - } - return m_currentRoom->prettyPrint(e->plainBody()); - } - if( ti->type() == EventType::RoomMember ) - { - auto* e = ti.viewAs(); - // FIXME: Rewind to the name that was at the time of this event - QString subjectName = m_currentRoom->roomMembername(e->userId()); - // The below code assumes senderName output in AuthorRole - switch( e->membership() ) - { - case MembershipType::Invite: - if (e->repeatsState()) - return tr("reinvited %1 to the room").arg(subjectName); - // [[fallthrough]] - case MembershipType::Join: - { - if (e->repeatsState()) - return tr("joined the room (repeated)"); - if (!e->prev_content() || - e->membership() != e->prev_content()->membership) - { - return e->membership() == MembershipType::Invite - ? tr("invited %1 to the room").arg(subjectName) - : tr("joined the room"); - } - QString text {}; - if (e->displayName() != e->prev_content()->displayName) - { - if (e->displayName().isEmpty()) - text = tr("cleared the display name"); - else - text = tr("changed the display name to %1") - .arg(e->displayName()); - } - if (e->avatarUrl() != e->prev_content()->avatarUrl) - { - if (!text.isEmpty()) - text += " and "; - if (e->avatarUrl().isEmpty()) - text += tr("cleared the avatar"); - else - text += tr("updated the avatar"); - } - return text; - } - case MembershipType::Leave: - if (e->prev_content() && - e->prev_content()->membership == MembershipType::Ban) - { - if (e->senderId() != e->userId()) - return tr("unbanned %1").arg(subjectName); - else - return tr("self-unbanned"); - } - if (e->senderId() != e->userId()) - return tr("has put %1 out of the room").arg(subjectName); - else - return tr("left the room"); - case MembershipType::Ban: - if (e->senderId() != e->userId()) - return tr("banned %1 from the room").arg(subjectName); - else - return tr("self-banned from the room"); - case MembershipType::Knock: - return tr("knocked"); - case MembershipType::Undefined: - return tr("made something unknown"); - } - } - if( ti->type() == EventType::RoomAliases ) - { - auto* e = ti.viewAs(); - return tr("set aliases to: %1").arg(e->aliases().join(", ")); - } - if( ti->type() == EventType::RoomCanonicalAlias ) - { - auto* e = ti.viewAs(); - if (e->alias().isEmpty()) - return tr("cleared the room main alias"); - else - return tr("set the room main alias to: %1").arg(e->alias()); - } - if( ti->type() == EventType::RoomName ) - { - auto* e = ti.viewAs(); - if (e->name().isEmpty()) - return tr("cleared the room name"); - else - return tr("set the room name to: %1").arg(e->name()); - } - if( ti->type() == EventType::RoomTopic ) - { - auto* e = ti.viewAs(); - if (e->topic().isEmpty()) - return tr("cleared the topic"); - else - return tr("set the topic to: %1").arg(e->topic()); - } - if( ti->type() == EventType::RoomAvatar ) - { - return tr("changed the room avatar"); - } - if( ti->type() == EventType::RoomEncryption ) - { - return tr("activated End-to-End Encryption"); - } - return tr("Unknown Event"); - } - - if( role == Qt::ToolTipRole ) - { - return ti->originalJson(); - } - - if( role == EventTypeRole ) - { - if (ti->isStateEvent()) - return "state"; - - if (ti->type() == EventType::RoomMessage) - { - switch (ti.viewAs()->msgtype()) - { - case MessageEventType::Emote: - return "emote"; - case MessageEventType::Notice: - return "notice"; - case MessageEventType::Image: - return "image"; - case MessageEventType::File: - case MessageEventType::Audio: - case MessageEventType::Video: - return "file"; - default: - return "message"; - } - } - - return "other"; - } - - if( role == TimeRole ) - return makeMessageTimestamp(timelineIt); - - if( role == SectionRole ) - return makeDateString(timelineIt); // FIXME: move date rendering to QML - - if( role == AuthorRole ) - { - auto userId = ti->senderId(); - // FIXME: It shouldn't be User, it should be its state "as of event" - return QVariant::fromValue(m_currentRoom->user(userId)); - } - - if (role == ContentTypeRole) - { - if (ti->type() == EventType::RoomMessage) - { - const auto& contentType = - ti.viewAs()->mimeType().name(); - return contentType == "text/plain" ? "text/html" : contentType; - } - return "text/plain"; - } - - if (role == ContentRole) - { - if (ti->isRedacted()) - { - auto reason = ti->redactedBecause()->reason(); - if (reason.isEmpty()) - return tr("Redacted"); - else - return tr("Redacted: %1") - .arg(ti->redactedBecause()->reason()); - } - - if( ti->type() == EventType::RoomMessage ) - { - using namespace MessageEventContent; - - auto* e = ti.viewAs(); - switch (e->msgtype()) - { - case MessageEventType::Image: - case MessageEventType::File: - case MessageEventType::Audio: - case MessageEventType::Video: - return QVariant::fromValue(e->content()->originalJson); - default: - ; - } - } - } - - if( role == ReadMarkerRole ) - return ti->id() == lastReadEventId; - - if( role == SpecialMarksRole ) - { - if (ti->isStateEvent() && ti.viewAs()->repeatsState()) - return "hidden"; - return ti->isRedacted() ? "redacted" : ""; - } - - if( role == EventIdRole ) - return ti->id(); - - if( role == LongOperationRole ) - { - if (ti->type() == EventType::RoomMessage && - ti.viewAs()->hasFileContent()) - { - auto info = m_currentRoom->fileTransferInfo(ti->id()); - return QVariant::fromValue(info); - } - } - - auto aboveEventIt = timelineIt + 1; // FIXME: shouldn't be here, because #312 - if (aboveEventIt != m_currentRoom->timelineEdge()) - { - if( role == AboveSectionRole ) - return makeDateString(aboveEventIt); - - if( role == AboveAuthorRole ) - return QVariant::fromValue( - m_currentRoom->user((*aboveEventIt)->senderId())); - } - +QVariant MessageEventModel::data(const QModelIndex& index, int role) const { + if (!m_currentRoom || index.row() < 0 || + index.row() >= m_currentRoom->timelineSize()) return QVariant(); + + const auto timelineIt = m_currentRoom->messageEvents().rbegin() + index.row(); + const auto& ti = *timelineIt; + + using namespace QMatrixClient; + if (role == Qt::DisplayRole) { + if (ti->isRedacted()) { + auto reason = ti->redactedBecause()->reason(); + if (reason.isEmpty()) + return tr("Redacted"); + else + return tr("Redacted: %1").arg(ti->redactedBecause()->reason()); + } + + if (ti->type() == EventType::RoomMessage) { + using namespace MessageEventContent; + + auto* e = ti.viewAs(); + if (e->hasTextContent() && e->mimeType().name() != "text/plain") + return static_cast(e->content())->body; + if (e->hasFileContent()) { + auto fileCaption = e->content()->fileInfo()->originalName; + if (fileCaption.isEmpty()) + fileCaption = m_currentRoom->prettyPrint(e->plainBody()); + if (fileCaption.isEmpty()) return tr("a file"); + } + return m_currentRoom->prettyPrint(e->plainBody()); + } + if (ti->type() == EventType::RoomMember) { + auto* e = ti.viewAs(); + // FIXME: Rewind to the name that was at the time of this event + QString subjectName = m_currentRoom->roomMembername(e->userId()); + // The below code assumes senderName output in AuthorRole + switch (e->membership()) { + case MembershipType::Invite: + if (e->repeatsState()) + return tr("reinvited %1 to the room").arg(subjectName); + // [[fallthrough]] + case MembershipType::Join: { + if (e->repeatsState()) return tr("joined the room (repeated)"); + if (!e->prev_content() || + e->membership() != e->prev_content()->membership) { + return e->membership() == MembershipType::Invite + ? tr("invited %1 to the room").arg(subjectName) + : tr("joined the room"); + } + QString text{}; + if (e->displayName() != e->prev_content()->displayName) { + if (e->displayName().isEmpty()) + text = tr("cleared the display name"); + else + text = tr("changed the display name to %1").arg(e->displayName()); + } + if (e->avatarUrl() != e->prev_content()->avatarUrl) { + if (!text.isEmpty()) text += " and "; + if (e->avatarUrl().isEmpty()) + text += tr("cleared the avatar"); + else + text += tr("updated the avatar"); + } + return text; + } + case MembershipType::Leave: + if (e->prev_content() && + e->prev_content()->membership == MembershipType::Ban) { + if (e->senderId() != e->userId()) + return tr("unbanned %1").arg(subjectName); + else + return tr("self-unbanned"); + } + if (e->senderId() != e->userId()) + return tr("has put %1 out of the room").arg(subjectName); + else + return tr("left the room"); + case MembershipType::Ban: + if (e->senderId() != e->userId()) + return tr("banned %1 from the room").arg(subjectName); + else + return tr("self-banned from the room"); + case MembershipType::Knock: + return tr("knocked"); + case MembershipType::Undefined: + return tr("made something unknown"); + } + } + if (ti->type() == EventType::RoomAliases) { + auto* e = ti.viewAs(); + return tr("set aliases to: %1").arg(e->aliases().join(", ")); + } + if (ti->type() == EventType::RoomCanonicalAlias) { + auto* e = ti.viewAs(); + if (e->alias().isEmpty()) + return tr("cleared the room main alias"); + else + return tr("set the room main alias to: %1").arg(e->alias()); + } + if (ti->type() == EventType::RoomName) { + auto* e = ti.viewAs(); + if (e->name().isEmpty()) + return tr("cleared the room name"); + else + return tr("set the room name to: %1").arg(e->name()); + } + if (ti->type() == EventType::RoomTopic) { + auto* e = ti.viewAs(); + if (e->topic().isEmpty()) + return tr("cleared the topic"); + else + return tr("set the topic to: %1").arg(e->topic()); + } + if (ti->type() == EventType::RoomAvatar) { + return tr("changed the room avatar"); + } + if (ti->type() == EventType::RoomEncryption) { + return tr("activated End-to-End Encryption"); + } + return tr("Unknown Event"); + } + + if (role == Qt::ToolTipRole) { + return ti->originalJson(); + } + + if (role == EventTypeRole) { + if (ti->isStateEvent()) return "state"; + + if (ti->type() == EventType::RoomMessage) { + switch (ti.viewAs()->msgtype()) { + case MessageEventType::Emote: + return "emote"; + case MessageEventType::Notice: + return "notice"; + case MessageEventType::Image: + return "image"; + case MessageEventType::File: + case MessageEventType::Audio: + case MessageEventType::Video: + return "file"; + default: + return "message"; + } + } + + return "other"; + } + + if (role == TimeRole) return makeMessageTimestamp(timelineIt); + + if (role == SectionRole) + return makeDateString(timelineIt); // FIXME: move date rendering to QML + + if (role == AuthorRole) { + auto userId = ti->senderId(); + // FIXME: It shouldn't be User, it should be its state "as of event" + return QVariant::fromValue(m_currentRoom->user(userId)); + } + + if (role == ContentTypeRole) { + if (ti->type() == EventType::RoomMessage) { + const auto& contentType = + ti.viewAs()->mimeType().name(); + return contentType == "text/plain" ? "text/html" : contentType; + } + return "text/plain"; + } + + if (role == ContentRole) { + if (ti->isRedacted()) { + auto reason = ti->redactedBecause()->reason(); + if (reason.isEmpty()) + return tr("Redacted"); + else + return tr("Redacted: %1").arg(ti->redactedBecause()->reason()); + } + + if (ti->type() == EventType::RoomMessage) { + using namespace MessageEventContent; + + auto* e = ti.viewAs(); + switch (e->msgtype()) { + case MessageEventType::Image: + case MessageEventType::File: + case MessageEventType::Audio: + case MessageEventType::Video: + return QVariant::fromValue(e->content()->originalJson); + default:; + } + } + } + + if (role == ReadMarkerRole) return ti->id() == lastReadEventId; + + if (role == SpecialMarksRole) { + if (ti->isStateEvent() && ti.viewAs()->repeatsState()) + return "hidden"; + return ti->isRedacted() ? "redacted" : ""; + } + + if (role == EventIdRole) return ti->id(); + + if (role == LongOperationRole) { + if (ti->type() == EventType::RoomMessage && + ti.viewAs()->hasFileContent()) { + auto info = m_currentRoom->fileTransferInfo(ti->id()); + return QVariant::fromValue(info); + } + } + + auto aboveEventIt = timelineIt + 1; // FIXME: shouldn't be here, because #312 + if (aboveEventIt != m_currentRoom->timelineEdge()) { + if (role == AboveSectionRole) return makeDateString(aboveEventIt); + + if (role == AboveAuthorRole) + return QVariant::fromValue( + m_currentRoom->user((*aboveEventIt)->senderId())); + } + + return QVariant(); } - -QHash MessageEventModel::roleNames() const -{ - QHash roles = QAbstractItemModel::roleNames(); - roles[EventTypeRole] = "eventType"; - roles[EventIdRole] = "eventId"; - roles[TimeRole] = "time"; - roles[SectionRole] = "section"; - roles[AboveSectionRole] = "aboveSection"; - roles[AuthorRole] = "author"; - roles[AboveAuthorRole] = "aboveAuthor"; - roles[ContentRole] = "content"; - roles[ContentTypeRole] = "contentType"; - roles[HighlightRole] = "highlight"; - roles[ReadMarkerRole] = "readMarker"; - roles[SpecialMarksRole] = "marks"; - roles[LongOperationRole] = "progressInfo"; - roles[EventResolvedTypeRole] = "eventResolvedType"; - return roles; +QHash MessageEventModel::roleNames() const { + QHash roles = QAbstractItemModel::roleNames(); + roles[EventTypeRole] = "eventType"; + roles[EventIdRole] = "eventId"; + roles[TimeRole] = "time"; + roles[SectionRole] = "section"; + roles[AboveSectionRole] = "aboveSection"; + roles[AuthorRole] = "author"; + roles[AboveAuthorRole] = "aboveAuthor"; + roles[ContentRole] = "content"; + roles[ContentTypeRole] = "contentType"; + roles[HighlightRole] = "highlight"; + roles[ReadMarkerRole] = "readMarker"; + roles[SpecialMarksRole] = "marks"; + roles[LongOperationRole] = "progressInfo"; + roles[EventResolvedTypeRole] = "eventResolvedType"; + return roles; } diff --git a/matrix/messageeventmodel.h b/matrix/messageeventmodel.h index f5d57a1..a505668 100644 --- a/matrix/messageeventmodel.h +++ b/matrix/messageeventmodel.h @@ -4,54 +4,55 @@ #include #include "room.h" -class MessageEventModel: public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(QMatrixClient::Room* room READ getRoom WRITE setRoom NOTIFY roomChanged) +class MessageEventModel : public QAbstractListModel { + Q_OBJECT + Q_PROPERTY( + QMatrixClient::Room* room READ getRoom WRITE setRoom NOTIFY roomChanged) - public: - enum EventRoles { - EventTypeRole = Qt::UserRole + 1, - EventIdRole, - TimeRole, - SectionRole, - AboveSectionRole, - AuthorRole, - AboveAuthorRole, - ContentRole, - ContentTypeRole, - HighlightRole, - ReadMarkerRole, - SpecialMarksRole, - LongOperationRole, - // For debugging - EventResolvedTypeRole, - }; + public: + enum EventRoles { + EventTypeRole = Qt::UserRole + 1, + EventIdRole, + TimeRole, + SectionRole, + AboveSectionRole, + AuthorRole, + AboveAuthorRole, + ContentRole, + ContentTypeRole, + HighlightRole, + ReadMarkerRole, + SpecialMarksRole, + LongOperationRole, + // For debugging + EventResolvedTypeRole, + }; - explicit MessageEventModel(QObject* parent = nullptr); - ~MessageEventModel(); + explicit MessageEventModel(QObject* parent = nullptr); + ~MessageEventModel(); - QMatrixClient::Room* getRoom() { return m_currentRoom; } - void setRoom(QMatrixClient::Room* room); + QMatrixClient::Room* getRoom() { return m_currentRoom; } + void setRoom(QMatrixClient::Room* room); - Q_INVOKABLE int rowCount(const QModelIndex& parent = QModelIndex()) const override; - QVariant data(const QModelIndex& index, int role) const override; - QHash roleNames() const; + Q_INVOKABLE int rowCount( + const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role) const override; + QHash roleNames() const; - private slots: - void refreshEvent(const QString& eventId); + private slots: + void refreshEvent(const QString& eventId); - private: - QMatrixClient::Room* m_currentRoom = nullptr; - QString lastReadEventId; - int nextNewerRow = -1; + private: + QMatrixClient::Room* m_currentRoom = nullptr; + QString lastReadEventId; + int nextNewerRow = -1; - QDateTime makeMessageTimestamp(QMatrixClient::Room::rev_iter_t baseIt) const; - QString makeDateString(QMatrixClient::Room::rev_iter_t baseIt) const; - void refreshEventRoles(const QString& eventId, const QVector roles); + QDateTime makeMessageTimestamp(QMatrixClient::Room::rev_iter_t baseIt) const; + QString makeDateString(QMatrixClient::Room::rev_iter_t baseIt) const; + void refreshEventRoles(const QString& eventId, const QVector roles); - signals: - void roomChanged(); + signals: + void roomChanged(); }; -#endif // MESSAGEEVENTMODEL_H +#endif // MESSAGEEVENTMODEL_H diff --git a/matrix/roomlistmodel.cpp b/matrix/roomlistmodel.cpp index 84f4ccc..862d41c 100644 --- a/matrix/roomlistmodel.cpp +++ b/matrix/roomlistmodel.cpp @@ -1,103 +1,83 @@ #include "roomlistmodel.h" +#include #include #include -#include -RoomListModel::RoomListModel(QObject* parent) - : QAbstractListModel(parent) -{ +RoomListModel::RoomListModel(QObject* parent) : QAbstractListModel(parent) {} -} +RoomListModel::~RoomListModel() {} -RoomListModel::~RoomListModel() -{ -} - -void RoomListModel::setConnection(QMatrixClient::Connection* connection) -{ - beginResetModel(); - m_connection = connection; - m_rooms.clear(); - connect( connection, &QMatrixClient::Connection::newRoom, this, &RoomListModel::addRoom ); - for( QMatrixClient::Room* room: connection->roomMap().values() ) { - connect( room, &QMatrixClient::Room::namesChanged, this, &RoomListModel::namesChanged ); - m_rooms.append(room); - } - endResetModel(); -} - -QMatrixClient::Room* RoomListModel::roomAt(int row) -{ - return m_rooms.at(row); -} - -void RoomListModel::addRoom(QMatrixClient::Room* room) -{ - beginInsertRows(QModelIndex(), m_rooms.count(), m_rooms.count()); - connect( room, &QMatrixClient::Room::namesChanged, this, &RoomListModel::namesChanged ); +void RoomListModel::setConnection(QMatrixClient::Connection* connection) { + beginResetModel(); + m_connection = connection; + m_rooms.clear(); + connect(connection, &QMatrixClient::Connection::newRoom, this, + &RoomListModel::addRoom); + for (QMatrixClient::Room* room : connection->roomMap().values()) { + connect(room, &QMatrixClient::Room::namesChanged, this, + &RoomListModel::namesChanged); m_rooms.append(room); - endInsertRows(); + } + endResetModel(); } -int RoomListModel::rowCount(const QModelIndex& parent) const -{ - if( parent.isValid() ) - return 0; - return m_rooms.count(); +QMatrixClient::Room* RoomListModel::roomAt(int row) { return m_rooms.at(row); } + +void RoomListModel::addRoom(QMatrixClient::Room* room) { + beginInsertRows(QModelIndex(), m_rooms.count(), m_rooms.count()); + connect(room, &QMatrixClient::Room::namesChanged, this, + &RoomListModel::namesChanged); + m_rooms.append(room); + endInsertRows(); } -QVariant RoomListModel::data(const QModelIndex& index, int role) const -{ - if( !index.isValid() ) - return QVariant(); +int RoomListModel::rowCount(const QModelIndex& parent) const { + if (parent.isValid()) return 0; + return m_rooms.count(); +} - if( index.row() >= m_rooms.count() ) - { - qDebug() << "UserListModel: something wrong here..."; - return QVariant(); - } - QMatrixClient::Room* room = m_rooms.at(index.row()); - if( role == Qt::DisplayRole ) - { - return room->displayName(); - } - if( role == Qt::ForegroundRole ) - { - if( room->highlightCount() > 0 ) - return QBrush(QColor("orange")); - return QVariant(); - } - if( role == Qt::DecorationRole ) - { - if ( room->avatarUrl().toString() != "" ) { - return room->avatarUrl(); - } - return QVariant(); - } - if ( role == Qt::StatusTipRole ) - { - return room->topic(); +QVariant RoomListModel::data(const QModelIndex& index, int role) const { + if (!index.isValid()) return QVariant(); + + if (index.row() >= m_rooms.count()) { + qDebug() << "UserListModel: something wrong here..."; + return QVariant(); + } + QMatrixClient::Room* room = m_rooms.at(index.row()); + if (role == Qt::DisplayRole) { + return room->displayName(); + } + if (role == Qt::ForegroundRole) { + if (room->highlightCount() > 0) return QBrush(QColor("orange")); + return QVariant(); + } + if (role == Qt::DecorationRole) { + if (room->avatarUrl().toString() != "") { + return room->avatarUrl(); } return QVariant(); + } + if (role == Qt::StatusTipRole) { + return room->topic(); + } + return QVariant(); } -void RoomListModel::namesChanged(QMatrixClient::Room* room) -{ - int row = m_rooms.indexOf(room); - emit dataChanged(index(row), index(row)); +void RoomListModel::namesChanged(QMatrixClient::Room* room) { + int row = m_rooms.indexOf(room); + emit dataChanged(index(row), index(row)); } -void RoomListModel::unreadMessagesChanged(QMatrixClient::Room* room) -{ - int row = m_rooms.indexOf(room); - emit dataChanged(index(row), index(row)); +void RoomListModel::unreadMessagesChanged(QMatrixClient::Room* room) { + int row = m_rooms.indexOf(room); + emit dataChanged(index(row), index(row)); } QHash RoomListModel::roleNames() const { - QHash roles; - roles[Qt::DisplayRole] = "name"; - roles[Qt::DecorationRole] = "avatar"; - roles[Qt::StatusTipRole] = "topic"; - return roles; + QHash roles; + roles[Qt::DisplayRole] = "name"; + roles[Qt::DecorationRole] = "avatar"; + roles[Qt::StatusTipRole] = "topic"; + return roles; } diff --git a/matrix/roomlistmodel.h b/matrix/roomlistmodel.h index d378558..177b463 100644 --- a/matrix/roomlistmodel.h +++ b/matrix/roomlistmodel.h @@ -2,39 +2,41 @@ #define ROOMLISTMODEL_H #include -#include "room.h" #include "connection.h" +#include "room.h" -class RoomListModel: public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(QMatrixClient::Connection *connection READ getConnection WRITE setConnection) +class RoomListModel : public QAbstractListModel { + Q_OBJECT + Q_PROPERTY(QMatrixClient::Connection* connection READ getConnection WRITE + setConnection) - public: - RoomListModel(QObject* parent=0); - virtual ~RoomListModel(); + public: + RoomListModel(QObject* parent = 0); + virtual ~RoomListModel(); - QMatrixClient::Connection* getConnection() {return m_connection;} - void setConnection(QMatrixClient::Connection* connection); + QMatrixClient::Connection* getConnection() { return m_connection; } + void setConnection(QMatrixClient::Connection* connection); - Q_INVOKABLE QMatrixClient::Room* roomAt(int row); + Q_INVOKABLE QMatrixClient::Room* roomAt(int row); - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - Q_INVOKABLE int rowCount(const QModelIndex& parent=QModelIndex()) const override; + QVariant data(const QModelIndex& index, + int role = Qt::DisplayRole) const override; + Q_INVOKABLE int rowCount( + const QModelIndex& parent = QModelIndex()) const override; - QHash roleNames() const; + QHash roleNames() const; - private slots: - void namesChanged(QMatrixClient::Room* room); - void unreadMessagesChanged(QMatrixClient::Room* room); - void addRoom(QMatrixClient::Room* room); + private slots: + void namesChanged(QMatrixClient::Room* room); + void unreadMessagesChanged(QMatrixClient::Room* room); + void addRoom(QMatrixClient::Room* room); - private: - QMatrixClient::Connection* m_connection = nullptr; - QList m_rooms; + private: + QMatrixClient::Connection* m_connection = nullptr; + QList m_rooms; - signals: - void connectionChanged(); + signals: + void connectionChanged(); }; -#endif // ROOMLISTMODEL_H +#endif // ROOMLISTMODEL_H diff --git a/qml/Login.qml b/qml/Login.qml index d607067..55d79eb 100644 --- a/qml/Login.qml +++ b/qml/Login.qml @@ -1,8 +1,8 @@ -import QtQuick 2.10 -import QtQuick.Layouts 1.3 +import QtQuick 2.11 +import QtQuick.Layouts 1.11 import QtGraphicalEffects 1.0 -import QtQuick.Controls 2.3 -import QtQuick.Controls.Material 2.3 +import QtQuick.Controls 2.4 +import QtQuick.Controls.Material 2.4 import Qt.labs.settings 1.0 import "qrc:/qml/component" @@ -60,13 +60,13 @@ Page { id: mainCol width: parent.width - ImageStatus { - Layout.preferredWidth: 96 - Layout.preferredHeight: 96 - Layout.alignment: Qt.AlignHCenter + // ImageStatus { + // Layout.preferredWidth: 96 + // Layout.preferredHeight: 96 + // Layout.alignment: Qt.AlignHCenter - source: "qrc:/asset/img/avatar.png" - } + // source: "qrc:/asset/img/avatar.png" + // } TextField { id: serverField diff --git a/qml/Room.qml b/qml/Room.qml index 785e8d5..68b2dad 100644 --- a/qml/Room.qml +++ b/qml/Room.qml @@ -1,6 +1,6 @@ import QtQuick 2.11 -import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 import Matrique 0.1 import "qrc:/qml/form" diff --git a/qml/component/ButtonDelegate.qml b/qml/component/ButtonDelegate.qml index 278921a..547b50c 100644 --- a/qml/component/ButtonDelegate.qml +++ b/qml/component/ButtonDelegate.qml @@ -1,5 +1,5 @@ -import QtQuick 2.3 -import QtQuick.Controls 2.3 +import QtQuick 2.11 +import QtQuick.Controls 2.4 ItemDelegate { id: itemDelegate diff --git a/qml/component/ImageStatus.qml b/qml/component/ImageStatus.qml index ce9d30f..7dfa39b 100644 --- a/qml/component/ImageStatus.qml +++ b/qml/component/ImageStatus.qml @@ -1,17 +1,22 @@ import QtQuick 2.11 import QtQuick.Controls 2.4 import QtGraphicalEffects 1.0 +import QtQuick.Controls.Material 2.4 Item { property bool opaqueBackground: false - property alias source: avatar.source + property bool round: true + property string source: "" + property string displayText: "" + readonly property bool showImage: source + readonly property bool showInitial: !showImage && displayText id: item Rectangle { width: item.width height: item.width - radius: item.width / 2 + radius: round ? item.width / 2 : 0 color: "white" visible: opaqueBackground } @@ -20,6 +25,8 @@ Item { id: avatar width: item.width height: item.width + visible: showImage + source: item.source mipmap: true layer.enabled: true @@ -35,9 +42,42 @@ Item { anchors.centerIn: parent width: avatar.width height: avatar.width - radius: avatar.width / 2 + radius: round? avatar.width / 2 : 0 } } } } + + Label { + anchors.fill: parent + color: "white" + visible: showInitial + text: showInitial ? getInitials(displayText)[0] : "" + font.pixelSize: 22 + font.bold: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + background: Rectangle { + anchors.fill: parent + radius: round? width / 2 : 0 + color: showInitial ? stringToColor(displayText) : Material.accent + } + } + + function getInitials(text) { + return text.toUpperCase().replace(/[^a-zA-Z- ]/g, "").match(/\b\w/g); + } + + function stringToColor(str) { + var hash = 0; + for (var i = 0; i < str.length; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + } + var colour = '#'; + for (var i = 0; i < 3; i++) { + var value = (hash >> (i * 8)) & 0xFF; + colour += ('00' + value.toString(16)).substr(-2); + } + return colour; + } } diff --git a/qml/component/MaterialIcon.qml b/qml/component/MaterialIcon.qml index d2fcd58..ecf74d2 100644 --- a/qml/component/MaterialIcon.qml +++ b/qml/component/MaterialIcon.qml @@ -1,7 +1,7 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.3 -import QtQuick.Controls.Material 2.3 +import QtQuick 2.11 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 +import QtQuick.Controls.Material 2.4 Item { property alias icon: iconText.text @@ -17,5 +17,5 @@ Item { color: item.color horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter - } + } } diff --git a/qml/component/SideNav.qml b/qml/component/SideNav.qml index 330f483..5109b58 100644 --- a/qml/component/SideNav.qml +++ b/qml/component/SideNav.qml @@ -1,7 +1,7 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.3 -import QtQuick.Controls.Material 2.3 +import QtQuick 2.11 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 +import QtQuick.Controls.Material 2.4 Item { Rectangle { diff --git a/qml/component/SideNavButton.qml b/qml/component/SideNavButton.qml index 7a217d2..43cd3c9 100644 --- a/qml/component/SideNavButton.qml +++ b/qml/component/SideNavButton.qml @@ -1,7 +1,7 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.3 -import QtQuick.Controls.Material 2.3 +import QtQuick 2.11 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 +import QtQuick.Controls.Material 2.4 Item { property var page @@ -29,7 +29,7 @@ Item { anchors.fill: parent onClicked: { - if(page != null && stackView.currentItem !== page) { + if(!page && stackView.currentItem !== page) { if(stackView.depth === 1) { stackView.replace(page) } else { diff --git a/qml/form/DetailForm.qml b/qml/form/DetailForm.qml index 837f551..cef4a22 100644 --- a/qml/form/DetailForm.qml +++ b/qml/form/DetailForm.qml @@ -1,7 +1,7 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.3 -import QtQuick.Controls.Material 2.3 +import QtQuick 2.11 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.4 +import QtQuick.Controls.Material 2.4 import "qrc:/qml/component" Item { diff --git a/qml/form/ListForm.qml b/qml/form/ListForm.qml index 6b33a0c..eba7feb 100644 --- a/qml/form/ListForm.qml +++ b/qml/form/ListForm.qml @@ -2,8 +2,8 @@ import QtQuick 2.11 import QtQuick.Controls 2.4 import QtQuick.Layouts 1.11 import QtGraphicalEffects 1.0 -import QtQuick.Controls.Material 2.3 -import QtQml.Models 2.3 +import QtQuick.Controls.Material 2.4 +import QtQml.Models 2.4 import Matrique 0.1 import "qrc:/qml/component" @@ -27,6 +27,10 @@ Item { height: 80 onClicked: listView.currentIndex = index + ToolTip.visible: pressed + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + ToolTip.text: name + contentItem: RowLayout { anchors.fill: parent anchors.margins: 16 @@ -36,7 +40,8 @@ Item { Layout.preferredWidth: height Layout.fillHeight: true - source: avatar == null || avatar == "" ? "qrc:/asset/img/avatar.png" : "image://mxc/" + avatar + source: avatar ? "image://mxc/" + avatar : "" + displayText: name opaqueBackground: true } @@ -52,10 +57,10 @@ Item { Layout.fillHeight: true text: { - if (name != "") { + if (name) { return name; } - if (alias != "") { + if (alias) { return alias; } return id @@ -69,7 +74,7 @@ Item { Layout.fillWidth: true Layout.fillHeight: true - text: topic === "" ? "No topic yet." : topic + text: topic ? topic : "No topic yet." elide: Text.ElideRight wrapMode: Text.NoWrap } @@ -133,7 +138,7 @@ Item { } Rectangle { - width: searchField.activeFocus || searchField.text != "" ? parent.width : 0 + width: searchField.activeFocus || searchField.text ? parent.width : 0 height: parent.height color: "white" diff --git a/qml/form/RoomForm.qml b/qml/form/RoomForm.qml index 6269d0e..022a282 100644 --- a/qml/form/RoomForm.qml +++ b/qml/form/RoomForm.qml @@ -1,7 +1,7 @@ -import QtQuick 2.10 -import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.3 -import QtQuick.Controls.Material 2.3 +import QtQuick 2.11 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 +import QtQuick.Controls.Material 2.4 import QtGraphicalEffects 1.0 import Matrique 0.1 import "qrc:/qml/component" @@ -16,7 +16,7 @@ Item { background: Item { anchors.fill: parent - visible: currentRoom == null + visible: !currentRoom Pane { anchors.fill: parent } @@ -32,7 +32,7 @@ Item { anchors.fill: parent spacing: 0 - visible: currentRoom != null + visible: currentRoom Pane { z: 10 @@ -52,7 +52,8 @@ Item { ImageStatus { Layout.preferredWidth: parent.height Layout.fillHeight: true - source: currentRoom != null && currentRoom.avatarUrl != "" ? "image://mxc/" + currentRoom.avatarUrl : "qrc:/asset/img/avatar.png" + source: currentRoom && currentRoom.avatarUrl != "" ? "image://mxc/" + currentRoom.avatarUrl : null + displayText: currentRoom ? currentRoom.displayName : "" } ColumnLayout { @@ -61,7 +62,7 @@ Item { Label { Layout.fillWidth: true - text: currentRoom != null ? currentRoom.displayName : "" + text: currentRoom ? currentRoom.displayName : "" font.pointSize: 16 elide: Text.ElideRight wrapMode: Text.NoWrap @@ -69,7 +70,7 @@ Item { Label { Layout.fillWidth: true - text: currentRoom != null ? currentRoom.topic : "" + text: currentRoom ? currentRoom.topic : "" elide: Text.ElideRight wrapMode: Text.NoWrap } @@ -88,12 +89,14 @@ Item { displayMarginEnd: 40 verticalLayoutDirection: ListView.BottomToTop spacing: 12 + model: MessageEventModel{ id: messageEventModel room: currentRoom onModelReset: currentRoom.getPreviousContent(50) } + delegate: Row { readonly property bool sentByMe: author === currentRoom.localUser @@ -102,18 +105,28 @@ Item { anchors.right: sentByMe ? parent.right : undefined spacing: 6 - Image { + ImageStatus { id: avatar width: height height: 40 - mipmap: true + round: false visible: !sentByMe - source: author.avatarUrl != "" ? "image://mxc/" + author.avatarUrl : "qrc:/asset/img/avatar.png" + source: author.avatarUrl != "" ? "image://mxc/" + author.avatarUrl : null + displayText: author.displayName + + MouseArea { + id: mouseArea + anchors.fill: parent + + ToolTip.visible: pressed + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + ToolTip.text: author.displayName + } } Rectangle { width: Math.min(messageText.implicitWidth + 24, - messageListView.width - (!sentByMe ? avatar.width + messageRow.spacing : 0)) + messageListView.width - (!sentByMe ? avatar.width + messageRow.spacing : 0)) height: messageText.implicitHeight + 24 color: sentByMe ? "lightgrey" : Material.accent @@ -121,6 +134,7 @@ Item { id: messageText text: display color: sentByMe ? "black" : "white" + linkColor: sentByMe ? Material.accent : "white" anchors.fill: parent anchors.margins: 12 wrapMode: Label.Wrap diff --git a/qml/main.qml b/qml/main.qml index 1e7eeb0..5e45dc1 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -1,6 +1,6 @@ import QtQuick 2.11 import QtQuick.Controls 2.4 -import QtQuick.Layouts 1.4 +import QtQuick.Layouts 1.11 import QtQuick.Controls.Material 2.4 import QtGraphicalEffects 1.0 import Qt.labs.settings 1.0 @@ -28,16 +28,16 @@ ApplicationWindow { property alias token: matriqueController.token } -// Platform.SystemTrayIcon { -// visible: true -// iconSource: "qrc:/asset/img/icon.png" + // Platform.SystemTrayIcon { + // visible: true + // iconSource: "qrc:/asset/img/icon.png" -// onActivated: { -// window.show() -// window.raise() -// window.requestActivate() -// } -// } + // onActivated: { + // window.show() + // window.raise() + // window.requestActivate() + // } + // } Controller { id: matriqueController @@ -118,7 +118,8 @@ ApplicationWindow { anchors.fill: parent anchors.margins: 15 - source: matriqueController.connection.localUser != null ? "image://mxc/" + matriqueController.connection.localUser.avatarUrl : "qrc:/asset/img/avatar.png" + source: matriqueController.connection.localUser && matriqueController.connection.localUser.avatarUrl ? "image://mxc/" + matriqueController.connection.localUser.avatarUrl : "" + displayText: matriqueController.connection.localUser && matriqueController.connection.localUser.displayText ? matriqueController.connection.localUser.displayText : "N" opaqueBackground: false } @@ -158,7 +159,7 @@ ApplicationWindow { imageProvider.connection = matriqueController.connection console.log(matriqueController.homeserver, matriqueController.userID, matriqueController.token) - if (matriqueController.userID != "" && matriqueController.token != "") { + if (matriqueController.userID && matriqueController.token) { console.log("Perform auto-login."); matriqueController.login(); } else {