From e12dc3a73a53e53b7f03585cf22f75ff5aab1e66 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Mon, 2 Sep 2019 22:58:58 +0200 Subject: [PATCH] Utility functions to sort text messages by timestamp (ascending/descending) * compare function for text message * resort function * text message edit can display HTML text messages with ascending/descending timestamp * made CTextMessageListModel -> CListModelTimestampObject --- src/blackgui/models/listmodelbase.cpp | 7 ++++++ src/blackgui/models/listmodelbase.h | 4 +++ src/blackgui/models/listmodelbasenetwork.cpp | 2 +- .../models/listmodeltimestampobjects.cpp | 10 +++++++- .../models/listmodeltimestampobjects.h | 3 +++ src/blackgui/models/textmessagelistmodel.cpp | 2 +- src/blackgui/models/textmessagelistmodel.h | 8 +++--- src/blackgui/textmessagetextedit.cpp | 25 +++++++++++-------- src/blackgui/textmessagetextedit.h | 13 +++++++--- src/blackgui/views/textmessageview.cpp | 12 ++++++--- src/blackgui/views/textmessageview.h | 8 ++++-- src/blackgui/views/viewbase.cpp | 18 ++++++++++--- src/blackgui/views/viewbase.h | 6 +++++ src/blackmisc/network/textmessage.cpp | 21 ++++++++++++++-- src/blackmisc/network/textmessage.h | 3 +++ src/blackmisc/timestampbased.h | 2 +- 16 files changed, 113 insertions(+), 31 deletions(-) diff --git a/src/blackgui/models/listmodelbase.cpp b/src/blackgui/models/listmodelbase.cpp index 386813ea9..bd1845cc0 100644 --- a/src/blackgui/models/listmodelbase.cpp +++ b/src/blackgui/models/listmodelbase.cpp @@ -502,6 +502,13 @@ namespace BlackGui this->sort(this->getSortColumn(), this->getSortOrder()); } + template + void CListModelBase::resort() + { + // sort the values + this->updateContainerMaybeAsync(m_container, true); + } + template void CListModelBase::sort(int column, Qt::SortOrder order) { diff --git a/src/blackgui/models/listmodelbase.h b/src/blackgui/models/listmodelbase.h index 4763987db..41f8059e0 100644 --- a/src/blackgui/models/listmodelbase.h +++ b/src/blackgui/models/listmodelbase.h @@ -107,6 +107,10 @@ namespace BlackGui //! Sort by given sort order \sa getSortColumn() \sa getSortOrder() void sort(); + //! Sort by given sort order \sa getSortColumn() \sa getSortOrder() + //! \remark always sorts, even if columns did no change + void resort(); + //! Truncate to given number void truncate(int maxNumber, bool forceSort = false); diff --git a/src/blackgui/models/listmodelbasenetwork.cpp b/src/blackgui/models/listmodelbasenetwork.cpp index 13cddbad0..f8bf87a79 100644 --- a/src/blackgui/models/listmodelbasenetwork.cpp +++ b/src/blackgui/models/listmodelbasenetwork.cpp @@ -16,7 +16,7 @@ namespace BlackGui // https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl template class CListModelBase; template class CListModelBase; - template class CListModelBase; + template class CListModelBase; template class CListModelBase; } // namespace diff --git a/src/blackgui/models/listmodeltimestampobjects.cpp b/src/blackgui/models/listmodeltimestampobjects.cpp index d567e6803..dbf2cdd92 100644 --- a/src/blackgui/models/listmodeltimestampobjects.cpp +++ b/src/blackgui/models/listmodeltimestampobjects.cpp @@ -19,11 +19,18 @@ namespace BlackGui { namespace Models { - template + template CListModelTimestampObjects::CListModelTimestampObjects(const QString &translationContext, QObject *parent) : CListModelBase(translationContext, parent) { } + template + bool CListModelTimestampObjects::isSortedByTimestampProperty() const + { + const CPropertyIndex pi = this->getSortProperty(); + return ITimestampBased::canHandleIndex(pi); + } + template void CListModelTimestampObjects::addTimestampColumns() { @@ -32,6 +39,7 @@ namespace BlackGui } template class CListModelTimestampObjects; + template class CListModelTimestampObjects; template class CListModelTimestampObjects; template class CListModelTimestampObjects; template class CListModelTimestampObjects; diff --git a/src/blackgui/models/listmodeltimestampobjects.h b/src/blackgui/models/listmodeltimestampobjects.h index b3a528b49..4d53bc312 100644 --- a/src/blackgui/models/listmodeltimestampobjects.h +++ b/src/blackgui/models/listmodeltimestampobjects.h @@ -35,6 +35,9 @@ namespace BlackGui //! Container element type using ObjectType = typename T::value_type; + //! Sorted by one of the timestamp columns? + bool isSortedByTimestampProperty() const; + protected: //! Constructor CListModelTimestampObjects(const QString &translationContext, QObject *parent = nullptr); diff --git a/src/blackgui/models/textmessagelistmodel.cpp b/src/blackgui/models/textmessagelistmodel.cpp index e2f074e26..088beea02 100644 --- a/src/blackgui/models/textmessagelistmodel.cpp +++ b/src/blackgui/models/textmessagelistmodel.cpp @@ -27,7 +27,7 @@ namespace BlackGui namespace Models { CTextMessageListModel::CTextMessageListModel(TextMessageMode mode, QObject *parent) : - CListModelBase("ModelTextMessageList", parent), m_textMessageMode(NotSet) + CListModelTimestampObjects("ModelTextMessageList", parent), m_textMessageMode(NotSet) { this->setTextMessageMode(mode); diff --git a/src/blackgui/models/textmessagelistmodel.h b/src/blackgui/models/textmessagelistmodel.h index bc6e52b5a..657e660e3 100644 --- a/src/blackgui/models/textmessagelistmodel.h +++ b/src/blackgui/models/textmessagelistmodel.h @@ -11,8 +11,8 @@ #ifndef BLACKGUI_MODELS_TEXTMESSAGELISTMODEL_H #define BLACKGUI_MODELS_TEXTMESSAGELISTMODEL_H +#include "blackgui/models/listmodeltimestampobjects.h" #include "blackgui/blackguiexport.h" -#include "blackgui/models/listmodelbase.h" #include "blackmisc/network/textmessagelist.h" class QObject; @@ -23,7 +23,8 @@ namespace BlackGui namespace Models { //! Text message list model - class BLACKGUI_EXPORT CTextMessageListModel : public CListModelBase + class BLACKGUI_EXPORT CTextMessageListModel : + public CListModelTimestampObjects { Q_OBJECT @@ -40,7 +41,7 @@ namespace BlackGui explicit CTextMessageListModel(TextMessageMode stationMode, QObject *parent = nullptr); //! Destructor - virtual ~CTextMessageListModel() {} + virtual ~CTextMessageListModel() override {} //! Set mode void setTextMessageMode(TextMessageMode mode); @@ -56,4 +57,5 @@ namespace BlackGui }; } // ns } // ns + #endif // guard diff --git a/src/blackgui/textmessagetextedit.cpp b/src/blackgui/textmessagetextedit.cpp index 8a849ccb6..90d603a64 100644 --- a/src/blackgui/textmessagetextedit.cpp +++ b/src/blackgui/textmessagetextedit.cpp @@ -45,12 +45,12 @@ namespace BlackGui m_actionWordWrap->setCheckable(true); connect(m_actionClearTextEdit, &QAction::triggered, this, &CTextMessageTextEdit::clear); - connect(m_actionAll, &QAction::triggered, this, &CTextMessageTextEdit::keepLastNMessages); - connect(m_actionLast10, &QAction::triggered, this, &CTextMessageTextEdit::keepLastNMessages); - connect(m_actionLast25, &QAction::triggered, this, &CTextMessageTextEdit::keepLastNMessages); - connect(m_actionWithSender, &QAction::triggered, this, &CTextMessageTextEdit::setVisibleFields); + connect(m_actionAll, &QAction::triggered, this, &CTextMessageTextEdit::keepLastNMessages); + connect(m_actionLast10, &QAction::triggered, this, &CTextMessageTextEdit::keepLastNMessages); + connect(m_actionLast25, &QAction::triggered, this, &CTextMessageTextEdit::keepLastNMessages); + connect(m_actionWithSender, &QAction::triggered, this, &CTextMessageTextEdit::setVisibleFields); connect(m_actionWithRecipient, &QAction::triggered, this, &CTextMessageTextEdit::setVisibleFields); - connect(m_actionWordWrap, &QAction::triggered, this, &CTextMessageTextEdit::setWordWrap); + connect(m_actionWordWrap, &QAction::triggered, this, &CTextMessageTextEdit::setWordWrap); connect(this, &QTextEdit::customContextMenuRequested, this, &CTextMessageTextEdit::showContextMenuForTextEdit); } @@ -63,14 +63,13 @@ namespace BlackGui if (maxMessages < 0 && m_keepMaxMessages >= 0) { maxMessages = m_keepMaxMessages; } if (maxMessages >= 0) { - m_messages.push_frontMaxElements(textMessage, maxMessages); + m_messages.push_backMaxElements(textMessage, maxMessages); } else { - m_messages.push_front(textMessage); + m_messages.push_back(textMessage); } - const QString html(toHtml(m_messages, m_withSender, m_withRecipient)); - m_textDocument.setHtml(html); + this->redrawHtml(); } int CTextMessageTextEdit::count() const @@ -86,8 +85,14 @@ namespace BlackGui void CTextMessageTextEdit::redrawHtml() { - const QString html(toHtml(m_messages, m_withSender, m_withRecipient)); + const QString html( + this->toHtml( + m_latestFirst ? m_messages.reversed() : m_messages, + m_withSender, + m_withRecipient) + ); m_textDocument.setHtml(html); + this->moveCursor(m_latestFirst ? QTextCursor::Start : QTextCursor::End); } void CTextMessageTextEdit::setStyleSheetForContent(const QString &styleSheet) diff --git a/src/blackgui/textmessagetextedit.h b/src/blackgui/textmessagetextedit.h index ca76daf86..8d67346bd 100644 --- a/src/blackgui/textmessagetextedit.h +++ b/src/blackgui/textmessagetextedit.h @@ -35,7 +35,7 @@ namespace BlackGui CTextMessageTextEdit(QWidget *parent = nullptr); //! Destructor - virtual ~CTextMessageTextEdit(); + virtual ~CTextMessageTextEdit() override; //! Insert a message void insertTextMessage(const BlackMisc::Network::CTextMessage &textMessage, int maxMessages = -1); @@ -55,6 +55,12 @@ namespace BlackGui //! Redraw HTML void redrawHtml(); + //! Order latest first/latest last + void setLatestFirst(bool latestFirst) { m_latestFirst = latestFirst; } + + //! Lastest first + bool isLatestFirst() const { return m_latestFirst; } + private: //! Context menu void showContextMenuForTextEdit(const QPoint &pt); @@ -77,9 +83,10 @@ namespace BlackGui BlackMisc::Network::CTextMessageList m_messages; QTextDocument m_textDocument; int m_keepMaxMessages = -1; //!< max number of messages to keep, or -1 to keep all messages - bool m_withSender = true; + bool m_latestFirst = false; + bool m_withSender = true; bool m_withRecipient = false; - bool m_wordWrap = true; + bool m_wordWrap = true; QAction *m_actionClearTextEdit = nullptr; QAction *m_actionLast10 = nullptr; diff --git a/src/blackgui/views/textmessageview.cpp b/src/blackgui/views/textmessageview.cpp index 4f728632e..eb4d75bc8 100644 --- a/src/blackgui/views/textmessageview.cpp +++ b/src/blackgui/views/textmessageview.cpp @@ -21,15 +21,19 @@ namespace BlackGui CTextMessageView::CTextMessageView(QWidget *parent) : CViewBase(parent) { this->standardInit(new CTextMessageListModel(CTextMessageListModel::FromTo, this)); - this->m_menus |= MenuClear; + m_menus |= MenuClear; } void CTextMessageView::setTextMessageMode(CTextMessageListModel::TextMessageMode mode) { - Q_ASSERT(this->m_model); - this->m_model->setTextMessageMode(mode); + Q_ASSERT(m_model); + m_model->setTextMessageMode(mode); this->setSortIndicator(); } - } + bool CTextMessageView::isSortedByTimestampProperty() const + { + return m_model->isSortedByTimestampProperty(); + } + } // namespace } // namespace diff --git a/src/blackgui/views/textmessageview.h b/src/blackgui/views/textmessageview.h index e60395ed2..2af6d4fdd 100644 --- a/src/blackgui/views/textmessageview.h +++ b/src/blackgui/views/textmessageview.h @@ -32,7 +32,11 @@ namespace BlackGui //! Set display mode void setTextMessageMode(BlackGui::Models::CTextMessageListModel::TextMessageMode mode); + + //! Sorted by a timestamp property + bool isSortedByTimestampProperty() const; }; - } -} + } // namespace +} // namespace + #endif // guard diff --git a/src/blackgui/views/viewbase.cpp b/src/blackgui/views/viewbase.cpp index 75c0369f6..04fa45352 100644 --- a/src/blackgui/views/viewbase.cpp +++ b/src/blackgui/views/viewbase.cpp @@ -107,8 +107,8 @@ namespace BlackGui Q_UNUSED(sort); ModelClass *model = this->derivedModel(); - auto sortColumn = model->getSortColumn(); - auto sortOrder = model->getSortOrder(); + const auto sortColumn = model->getSortColumn(); + const auto sortOrder = model->getSortOrder(); this->showLoadIndicator(container.size()); CWorker *worker = CWorker::fromTask(this, "ViewSort", [model, container, sortColumn, sortOrder]() { @@ -300,7 +300,7 @@ namespace BlackGui if (!hasSelection()) { return 0; } int c = 0; - int lastUpdatedRow = -1; + int lastUpdatedRow = -1; int firstUpdatedRow = -1; const CPropertyIndexList propertyIndexes(vm.indexes()); const QModelIndexList indexes = this->selectedRows(); @@ -503,6 +503,18 @@ namespace BlackGui m_model->sortByPropertyIndex(propertyIndex, order); } + template + void CViewBase::sort() + { + m_model->sort(); + } + + template + void CViewBase::resort() + { + m_model->resort(); + } + template QJsonObject CViewBase::toJson(bool selectedOnly) const { diff --git a/src/blackgui/views/viewbase.h b/src/blackgui/views/viewbase.h index 8d2e9ea7b..854910340 100644 --- a/src/blackgui/views/viewbase.h +++ b/src/blackgui/views/viewbase.h @@ -750,6 +750,12 @@ namespace BlackGui virtual Qt::SortOrder getSortOrder() const override { return m_model->getSortOrder(); } //! @} + //! Sort if columns or order changed + void sort(); + + //! Resort ("forced sorting") + void resort(); + //! Column count int columnCount() const; diff --git a/src/blackmisc/network/textmessage.cpp b/src/blackmisc/network/textmessage.cpp index 6df8ddfee..e2f979ab2 100644 --- a/src/blackmisc/network/textmessage.cpp +++ b/src/blackmisc/network/textmessage.cpp @@ -277,7 +277,7 @@ namespace BlackMisc const ColumnIndex i = index.frontCasted(); switch (i) { - case IndexSenderCallsign: return m_senderCallsign.propertyByIndex(index.copyFrontRemoved()); + case IndexSenderCallsign: return m_senderCallsign.propertyByIndex(index.copyFrontRemoved()); case IndexRecipientCallsign: return m_recipientCallsign.propertyByIndex(index.copyFrontRemoved()); case IndexRecipientCallsignOrFrequency: return CVariant::fromValue(this->getRecipientCallsignOrFrequency()); case IndexMessage: return CVariant::fromValue(m_message); @@ -293,11 +293,28 @@ namespace BlackMisc const ColumnIndex i = index.frontCasted(); switch (i) { - case IndexSenderCallsign: m_senderCallsign.setPropertyByIndex(index.copyFrontRemoved(), variant); break; + case IndexSenderCallsign: m_senderCallsign.setPropertyByIndex(index.copyFrontRemoved(), variant); break; case IndexRecipientCallsign: m_recipientCallsign.setPropertyByIndex(index.copyFrontRemoved(), variant); break; case IndexMessage: m_message = variant.value(); break; default: CValueObject::setPropertyByIndex(index, variant); break; } } + + int CTextMessage::comparePropertyByIndex(const CPropertyIndex &index, const CTextMessage &compareValue) const + { + if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::comparePropertyByIndex(index, compareValue); } + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexSenderCallsign: return m_senderCallsign.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getSenderCallsign()); + case IndexRecipientCallsign: return m_recipientCallsign.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getRecipientCallsign()); + case IndexRecipientCallsignOrFrequency: + if (this->isRadioMessage()) { return this->getFrequency().compare(compareValue.getFrequency()); } + return m_recipientCallsign.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getRecipientCallsign()); + default: return CValueObject::comparePropertyByIndex(index, *this); + } + Q_ASSERT_X(false, Q_FUNC_INFO, "No comparison"); + return 0; + } } // namespace } // namespace diff --git a/src/blackmisc/network/textmessage.h b/src/blackmisc/network/textmessage.h index 2ca35625b..f4e3470dd 100644 --- a/src/blackmisc/network/textmessage.h +++ b/src/blackmisc/network/textmessage.h @@ -180,6 +180,9 @@ namespace BlackMisc //! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex void setPropertyByIndex(const BlackMisc::CPropertyIndex &index, const CVariant &variant); + //! \copydoc BlackMisc::Mixin::Index::comparePropertyByIndex + int comparePropertyByIndex(const CPropertyIndex &index, const CTextMessage &compareValue) const; + //! \copydoc BlackMisc::Mixin::String::toQString QString convertToQString(bool i18n = false) const; diff --git a/src/blackmisc/timestampbased.h b/src/blackmisc/timestampbased.h index 86640603c..c056317b5 100644 --- a/src/blackmisc/timestampbased.h +++ b/src/blackmisc/timestampbased.h @@ -138,10 +138,10 @@ namespace BlackMisc //! Any of the timestamp indexes static bool isAnyTimestampIndex(int index); - protected: //! Can given index be handled static bool canHandleIndex(const CPropertyIndex &index); + protected: //! Constructor ITimestampBased();