// For license of this file, see /LICENSE.md. #include "gui/messagepreviewer.h" #include "database/databasequeries.h" #include "gui/itemdetails.h" #include "gui/webbrowser.h" #include "miscellaneous/application.h" #include "services/abstract/gui/custommessagepreviewer.h" #include "services/abstract/label.h" #include "services/abstract/labelsnode.h" #include "services/abstract/serviceroot.h" #include #include #include #include #include #include #include #include void MessagePreviewer::createConnections() { installEventFilter(this); connect(m_actionMarkRead = m_toolBar->addAction(qApp->icons()->fromTheme(QSL("mail-mark-read")), tr("Mark article read")), &QAction::triggered, this, &MessagePreviewer::markMessageAsRead); connect(m_actionMarkUnread = m_toolBar->addAction(qApp->icons()->fromTheme(QSL("mail-mark-unread")), tr("Mark article unread")), &QAction::triggered, this, &MessagePreviewer::markMessageAsUnread); connect(m_actionSwitchImportance = m_toolBar->addAction(qApp->icons()->fromTheme(QSL("mail-mark-important")), tr("Switch article importance")), &QAction::triggered, this, &MessagePreviewer::switchMessageImportance); } MessagePreviewer::MessagePreviewer(QWidget* parent) : QWidget(parent), m_mainLayout(new QGridLayout(this)), m_viewerLayout(new QStackedLayout()), m_toolBar(new QToolBar(this)), m_msgBrowser(new WebBrowser(nullptr, this)), m_separator(nullptr), m_btnLabels(QList()), m_itemDetails(new ItemDetails(this)), m_toolbarVisible(true) { m_toolBar->setOrientation(Qt::Orientation::Vertical); // NOTE: To make sure that if we have many labels and short message // that whole toolbar is visible. // m_toolBar->setSizePolicy(m_toolBar->sizePolicy().horizontalPolicy(), QSizePolicy::Policy::MinimumExpanding); // This layout holds standard article browser on index 0 // and optional custom browser on index 1. m_viewerLayout->addWidget(m_msgBrowser); m_viewerLayout->addWidget(m_itemDetails); m_mainLayout->setContentsMargins(3, 3, 3, 3); m_mainLayout->addLayout(m_viewerLayout, 0, 1, 1, 1); m_mainLayout->addWidget(m_toolBar, 0, 0, -1, 1); createConnections(); m_actionSwitchImportance->setCheckable(true); } MessagePreviewer::~MessagePreviewer() { // Make sure that previewer does not delete any custom article // viewers as those are responsibility to free by their accounts. auto* wdg = m_viewerLayout->widget(INDEX_CUSTOM); if (wdg != nullptr) { wdg->setParent(nullptr); m_viewerLayout->removeWidget(wdg); } } void MessagePreviewer::reloadFontSettings() { m_msgBrowser->reloadFontSettings(); } void MessagePreviewer::setToolbarsVisible(bool visible) { m_toolbarVisible = visible; m_toolBar->setVisible(visible); m_msgBrowser->setNavigationBarVisible(visible); qApp->settings()->setValue(GROUP(GUI), GUI::MessageViewerToolbarsVisible, visible); } WebBrowser* MessagePreviewer::webBrowser() const { return m_msgBrowser; } void MessagePreviewer::clear() { updateLabels(true); ensureDefaultBrowserVisible(); m_msgBrowser->clear(false); hide(); m_root.clear(); m_message = Message(); } void MessagePreviewer::showItemDetails(RootItem* item) { m_toolBar->setVisible(false); m_message = Message(); m_root = item; ensureItemDetailsVisible(); m_itemDetails->loadItemDetails(item); show(); } void MessagePreviewer::loadUrl(const QString& url) { m_toolBar->setVisible(m_toolbarVisible); m_message = Message(); m_root.clear(); ensureDefaultBrowserVisible(); m_msgBrowser->loadUrl(url); } void MessagePreviewer::loadMessage(const Message& message, RootItem* root) { m_toolBar->setVisible(m_toolbarVisible); bool same_message = message.m_id == m_message.m_id && m_root == root; m_message = message; m_root = root; if (!m_root.isNull()) { updateButtons(); updateLabels(false); show(); if (!same_message) { const QString msg_feed_id = message.m_feedId; const auto* feed = root->getParentServiceRoot() ->getItemFromSubTree([msg_feed_id](const RootItem* it) { return it->kind() == RootItem::Kind::Feed && it->customId() == msg_feed_id; }) ->toFeed(); if (feed != nullptr && feed->openArticlesDirectly() && !m_message.m_url.isEmpty()) { ensureDefaultBrowserVisible(); m_msgBrowser->setVerticalScrollBarPosition(0.0); m_msgBrowser->loadUrl(m_message.m_url); } else { CustomMessagePreviewer* custom_previewer = root->getParentServiceRoot()->customMessagePreviewer(); if (custom_previewer != nullptr) { auto* current_custom_previewer = m_viewerLayout->widget(INDEX_CUSTOM); if (current_custom_previewer != nullptr) { if (current_custom_previewer != custom_previewer) { m_viewerLayout->removeWidget(current_custom_previewer); m_viewerLayout->addWidget(custom_previewer); } } else { m_viewerLayout->addWidget(custom_previewer); } m_viewerLayout->setCurrentIndex(INDEX_CUSTOM); custom_previewer->loadMessage(message, root); } else { ensureDefaultBrowserVisible(); m_msgBrowser->loadMessages({message}, m_root); } } } } } void MessagePreviewer::switchLabel(bool assign) { auto lbl = qobject_cast(sender())->label(); if (lbl == nullptr) { return; } if (assign) { lbl->assignToMessage(m_message); m_message.m_assignedLabelsIds.append(lbl->customId()); } else { lbl->deassignFromMessage(m_message); m_message.m_assignedLabelsIds.removeOne(lbl->customId()); } emit setMessageLabelIds(m_message.m_id, m_message.m_assignedLabelsIds); } void MessagePreviewer::markMessageAsRead() { markMessageAsReadUnread(RootItem::ReadStatus::Read); } void MessagePreviewer::markMessageAsUnread() { markMessageAsReadUnread(RootItem::ReadStatus::Unread); } void MessagePreviewer::markMessageAsReadUnread(RootItem::ReadStatus read) { if (!m_root.isNull()) { if (m_root->getParentServiceRoot()->onBeforeSetMessagesRead(m_root.data(), QList() << m_message, read)) { DatabaseQueries::markMessagesReadUnread(qApp->database() ->driver() ->connection(objectName(), DatabaseDriver::DesiredStorageType::FromSettings), QStringList() << QString::number(m_message.m_id), read); m_root->getParentServiceRoot()->onAfterSetMessagesRead(m_root.data(), QList() << m_message, read); m_message.m_isRead = read == RootItem::ReadStatus::Read; emit markMessageRead(m_message.m_id, read); updateButtons(); } } } void MessagePreviewer::switchMessageImportance(bool checked) { if (!m_root.isNull()) { if (m_root->getParentServiceRoot() ->onBeforeSwitchMessageImportance(m_root.data(), QList() << ImportanceChange(m_message, m_message.m_isImportant ? RootItem::Importance::NotImportant : RootItem::Importance::Important))) { DatabaseQueries::switchMessagesImportance(qApp->database() ->driver() ->connection(objectName(), DatabaseDriver::DesiredStorageType::FromSettings), QStringList() << QString::number(m_message.m_id)); m_root->getParentServiceRoot() ->onAfterSwitchMessageImportance(m_root.data(), QList() << ImportanceChange(m_message, m_message.m_isImportant ? RootItem::Importance::NotImportant : RootItem::Importance::Important)); emit markMessageImportant(m_message.m_id, checked ? RootItem::Importance::Important : RootItem::Importance::NotImportant); m_message.m_isImportant = checked; } } } void MessagePreviewer::updateButtons() { m_actionSwitchImportance->setChecked(m_message.m_isImportant); m_actionMarkRead->setEnabled(!m_message.m_isRead); m_actionMarkUnread->setEnabled(m_message.m_isRead); } void MessagePreviewer::updateLabels(bool only_clear) { for (auto& lbl : m_btnLabels) { m_toolBar->removeAction(lbl); lbl->deleteLater(); } m_btnLabels.clear(); if (m_separator != nullptr) { m_toolBar->removeAction(m_separator); } if (only_clear) { return; } if (m_root.data() != nullptr && !m_root.data()->getParentServiceRoot()->labelsNode()->labels().isEmpty()) { m_separator = m_toolBar->addSeparator(); auto lbls = m_root.data()->getParentServiceRoot()->labelsNode()->labels(); std::sort(lbls.begin(), lbls.end(), [](Label* lhs, Label* rhs) { return lhs->title().compare(rhs->title(), Qt::CaseSensitivity::CaseInsensitive) < 0; }); for (auto* label : lbls) { LabelToolbarAction* act_label = new LabelToolbarAction(this); act_label->setIcon(Label::generateIcon(label->color())); act_label->setText(QSL(" ") + label->title()); act_label->setCheckable(true); act_label->setChecked(m_message.m_assignedLabelsIds.contains(label->customId())); act_label->setToolTip(label->title()); act_label->setLabel(label); m_toolBar->addAction(act_label); m_btnLabels.append(act_label); connect(act_label, &QAction::toggled, this, &MessagePreviewer::switchLabel); } } } void MessagePreviewer::ensureItemDetailsVisible() { if (m_viewerLayout->count() > INDEX_CUSTOM) { m_viewerLayout->removeWidget(m_viewerLayout->widget(INDEX_CUSTOM)); } m_viewerLayout->setCurrentIndex(INDEX_ITEMS); } void MessagePreviewer::ensureDefaultBrowserVisible() { if (m_viewerLayout->count() > INDEX_CUSTOM) { m_viewerLayout->removeWidget(m_viewerLayout->widget(INDEX_CUSTOM)); } m_viewerLayout->setCurrentIndex(INDEX_DEFAULT); } LabelToolbarAction::LabelToolbarAction(QObject* parent) : QAction(parent), m_label(nullptr) {} Label* LabelToolbarAction::label() const { return m_label.data(); } void LabelToolbarAction::setLabel(Label* label) { m_label = label; }