// For license of this file, see /LICENSE.md. #include "gui/messagebrowser.h" #include "gui/messagebox.h" #include "gui/messagetextbrowser.h" #include "gui/reusable/searchtextwidget.h" #include "miscellaneous/application.h" #include "miscellaneous/settings.h" #include "network-web/webfactory.h" #include "services/abstract/serviceroot.h" #include #include #include #include #include MessageBrowser::MessageBrowser(bool should_resize_to_fit, QWidget* parent) : QWidget(parent), m_txtBrowser(new MessageTextBrowser(this)), m_searchWidget(new SearchTextWidget(this)), m_layout(new QVBoxLayout(this)) { m_layout->setContentsMargins(3, 3, 3, 3); m_layout->addWidget(m_txtBrowser, 1); m_layout->addWidget(m_searchWidget); if (should_resize_to_fit) { m_txtBrowser->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::MinimumExpanding); } connect(m_searchWidget, &SearchTextWidget::searchCancelled, this, [this]() { m_txtBrowser->textCursor().clearSelection(); m_txtBrowser->moveCursor(QTextCursor::MoveOperation::Left); }); connect(m_searchWidget, &SearchTextWidget::searchForText, this, [this](const QString& text, bool backwards) { if (backwards) { m_txtBrowser->find(text, QTextDocument::FindFlag::FindBackward); } else { m_txtBrowser->find(text); } }); connect(m_txtBrowser, &QTextBrowser::anchorClicked, this, &MessageBrowser::onAnchorClicked); connect(m_txtBrowser, QOverload::of(&QTextBrowser::highlighted), [=](const QUrl& url) { Q_UNUSED(url) QToolTip::showText(QCursor::pos(), tr("Click this link to download it or open it with external browser."), this); }); m_searchWidget->hide(); installEventFilter(this); reloadFontSettings(); } void MessageBrowser::clear(bool also_hide) { m_txtBrowser->clear(); m_pictures.clear(); m_searchWidget->hide(); if (also_hide) { hide(); } } QString MessageBrowser::prepareHtmlForMessage(const Message& message) { QString html = QString("

%1

").arg(message.m_title); if (!message.m_url.isEmpty()) { html += QString("[url] %1
").arg(message.m_url); } for (const Enclosure& enc : message.m_enclosures) { QString enc_url; if (!enc.m_url.contains(QRegularExpression(QSL("^(http|ftp|\\/)")))) { enc_url = QString(INTERNAL_URL_PASSATTACHMENT) + QL1S("/?") + enc.m_url; } else { enc_url = enc.m_url; } html += QString("[%2] %1
").arg(enc_url, enc.m_mimeType); } QRegularExpression imgTagRegex("\\]*src\\s*=\\s*[\"\']([^\"\']*)[\"\'][^\\>]*\\>", QRegularExpression::PatternOption::CaseInsensitiveOption | QRegularExpression::PatternOption::InvertedGreedinessOption); QRegularExpressionMatchIterator i = imgTagRegex.globalMatch(message.m_contents); QString pictures_html; while (i.hasNext()) { QRegularExpressionMatch match = i.next(); m_pictures.append(match.captured(1)); pictures_html += QString("
[%1] %2").arg(tr("image"), match.captured(1)); } if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::DisplayImagePlaceholders)).toBool()) { html += message.m_contents; } else { QString cnts = message.m_contents; html += cnts.replace(imgTagRegex, QString()); } html += pictures_html; /*html = html .replace(QSL("\r\n"), QSL("\n")) .replace(QL1C('\r'), QL1C('\n')) .remove(QL1C('\n'));*/ // TODO: If FgInteresting not defined by the skin // use current pallette/Highlight color perhaps.E return QSL("" "" "%1" "") .arg(html, qApp->skins()->currentSkin().colorForModel(SkinEnums::PaletteColors::FgInteresting).value().name()); } bool MessageBrowser::eventFilter(QObject* watched, QEvent* event) { Q_UNUSED(watched) if (event->type() == QEvent::Type::KeyPress) { auto* key_event = static_cast(event); if (key_event->matches(QKeySequence::StandardKey::Find)) { m_searchWidget->clear(); m_searchWidget->show(); m_searchWidget->setFocus(); return true; } else if (key_event->key() == Qt::Key::Key_Escape) { m_searchWidget->cancelSearch(); return true; } } return false; } void MessageBrowser::onAnchorClicked(const QUrl& url) { if (url.toString().startsWith(INTERNAL_URL_PASSATTACHMENT) && m_root != nullptr && m_root->getParentServiceRoot()->downloadAttachmentOnMyOwn(url)) { return; } if (!url.isEmpty()) { bool open_externally_now = qApp->settings()->value(GROUP(Browser), SETTING(Browser::OpenLinksInExternalBrowserRightAway)).toBool(); if (open_externally_now) { qApp->web()->openUrlInExternalBrowser(url.toString()); } else { // User clicked some URL. Open it in external browser or download? MsgBox box(qApp->mainFormWidget()); box.setText(tr("You clicked some link. You can download the link contents or open it in external web browser.")); box.setInformativeText(tr("What action do you want to take?")); box.setDetailedText(url.toString()); QAbstractButton* btn_open = box.addButton(tr("Open in external browser"), QMessageBox::ButtonRole::ActionRole); QAbstractButton* btn_download = box.addButton(tr("Download"), QMessageBox::ButtonRole::ActionRole); QAbstractButton* btn_cancel = box.addButton(QMessageBox::StandardButton::Cancel); bool always; MsgBox::setCheckBox(&box, tr("Always open links in external browser."), &always); box.setDefaultButton(QMessageBox::StandardButton::Cancel); box.exec(); if (box.clickedButton() != box.button(QMessageBox::StandardButton::Cancel)) { // Store selected checkbox value. qApp->settings()->setValue(GROUP(Browser), Browser::OpenLinksInExternalBrowserRightAway, always); } if (box.clickedButton() == btn_open) { qApp->web()->openUrlInExternalBrowser(url.toString()); } else if (box.clickedButton() == btn_download) { qApp->downloadManager()->download(url); } btn_download->deleteLater(); btn_open->deleteLater(); btn_cancel->deleteLater(); } } else { MsgBox::show(qApp->mainFormWidget(), QMessageBox::Warning, tr("Incorrect link"), tr("Selected hyperlink is invalid.")); } } void MessageBrowser::reloadFontSettings() { const Settings* settings = qApp->settings(); QFont fon; fon.fromString(settings->value(GROUP(Messages), SETTING(Messages::PreviewerFontStandard)).toString()); m_txtBrowser->setFont(fon); } void MessageBrowser::loadUrl(const QString& url) { onAnchorClicked(url); } void MessageBrowser::loadMessage(const Message& message, RootItem* root) { Q_UNUSED(root) auto html = prepareHtmlForMessage(message); m_txtBrowser->setHtml(html); m_txtBrowser->verticalScrollBar()->triggerAction(QScrollBar::SliderAction::SliderToMinimum); m_searchWidget->hide(); } double MessageBrowser::verticalScrollBarPosition() const { return m_txtBrowser->verticalScrollBar()->value(); } void MessageBrowser::setVerticalScrollBarPosition(double pos) { m_txtBrowser->verticalScrollBar()->setValue(pos); }