diff --git a/.appveyor.yml b/.appveyor.yml index e8c259ab3..1a9312319 100755 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -44,7 +44,7 @@ on_success: - for /f "tokens=*" %%F in ('dir /b *.exe') do curl --upload-file %%F https://transfer.sh/%%F --silent >> ..\rssguard-wiki\Windows-development-builds.md - echo.>> ..\rssguard-wiki\Windows-development-builds.md - cd ..\rssguard-wiki - - git pull origin master - git add *.* - git commit -m "New files." + - git pull origin master - git push origin master diff --git a/resources/scripts/.travis-install-osx.sh b/resources/scripts/.travis-install-osx.sh index b32d29633..440ceb1d7 100755 --- a/resources/scripts/.travis-install-osx.sh +++ b/resources/scripts/.travis-install-osx.sh @@ -29,7 +29,7 @@ echo "\n" >> ./build-wiki/Mac-OS-X-development-builds.md cat ./build-wiki/Mac-OS-X-development-builds.md cd ./build-wiki -git pull origin master git add *.* git commit -m "New files." +git pull origin master git push origin master \ No newline at end of file diff --git a/resources/text/CHANGELOG b/resources/text/CHANGELOG index d261cf76c..cdace1d26 100644 --- a/resources/text/CHANGELOG +++ b/resources/text/CHANGELOG @@ -2,6 +2,7 @@ ————— Added: +▪ Added support for arbitrary external tools (settings category "Web browser & e-mail & proxy") which can open URLs of selected messages. (#136) ▪ Standard account is now automatically added if RSS Guard is started with empty database. ▪ Menu action "Select next unread message" in "Messages" menu now works across all feeds, so user can navigate through all unread messages with a sigle keyboard shortcut. (#132, #6) ▪ Added two bindable menu actions (in menu "Web browser & tabs") which allow to cycle among tabs. (#6) diff --git a/src/gui/dialogs/formupdate.cpp b/src/gui/dialogs/formupdate.cpp index 00b312f49..cae0d109b 100755 --- a/src/gui/dialogs/formupdate.cpp +++ b/src/gui/dialogs/formupdate.cpp @@ -27,7 +27,6 @@ #include "gui/guiutilities.h" #include -#include #if defined(Q_OS_WIN) #include diff --git a/src/gui/messagesview.cpp b/src/gui/messagesview.cpp index c567e8995..a7f12ddf5 100755 --- a/src/gui/messagesview.cpp +++ b/src/gui/messagesview.cpp @@ -32,6 +32,8 @@ #include #include #include +#include +#include MessagesView::MessagesView(QWidget* parent) @@ -180,6 +182,28 @@ void MessagesView::initializeContextMenu() { } m_contextMenu->clear(); + + QFileIconProvider icon_provider; + QMenu* menu = new QMenu(tr("Open with external tool"), m_contextMenu); + menu->setIcon(qApp->icons()->fromTheme(QSL("document-open"))); + + foreach (const QString& tool, qApp->settings()->value(GROUP(Browser), SETTING(Browser::ExternalTools)).toStringList()) { + QAction* act_tool = new QAction(QFileInfo(tool).fileName(), menu); + + act_tool->setIcon(icon_provider.icon(tool)); + act_tool->setToolTip(tool); + menu->addAction(act_tool); + + connect(act_tool, &QAction::triggered, this, &MessagesView::openSelectedMessagesWithExternalTool); + } + + if (menu->actions().isEmpty()) { + QAction* act_not_tools = new QAction("No external tools activated"); + act_not_tools->setEnabled(false); + menu->addAction(act_not_tools); + } + + m_contextMenu->addMenu(menu); m_contextMenu->addActions(QList() << qApp->mainForm()->m_ui->m_actionSendMessageViaEmail << qApp->mainForm()->m_ui->m_actionOpenSelectedSourceArticlesExternally << @@ -479,7 +503,26 @@ void MessagesView::searchMessages(const QString& pattern) { } void MessagesView::filterMessages(MessagesModel::MessageHighlighter filter) { - m_sourceModel->highlightMessages(filter); + m_sourceModel->highlightMessages(filter); +} + +void MessagesView::openSelectedMessagesWithExternalTool() { + QAction* sndr = qobject_cast(sender()); + + if (sndr != nullptr) { + const QString& tool = sndr->toolTip(); + + foreach (const QModelIndex& index, selectionModel()->selectedRows()) { + const QString& link = m_sourceModel->messageAt(m_proxyModel->mapToSource(index).row()).m_url; + + if (!link.isEmpty()) { + if (!QProcess::startDetached(tool, QStringList() << link)) { + qApp->showGuiMessage(tr("Cannot run external tool"), tr("External tool '%1' could not be started."), + QSystemTrayIcon::Critical); + } + } + } + } } void MessagesView::adjustColumns() { diff --git a/src/gui/messagesview.h b/src/gui/messagesview.h index b1aba065d..fa00eac9e 100755 --- a/src/gui/messagesview.h +++ b/src/gui/messagesview.h @@ -77,6 +77,8 @@ class MessagesView : public QTreeView { void filterMessages(MessagesModel::MessageHighlighter filter); private slots: + void openSelectedMessagesWithExternalTool(); + // Marks given indexes as selected. void reselectIndexes(const QModelIndexList& indexes); diff --git a/src/gui/settings/settingsbrowsermail.cpp b/src/gui/settings/settingsbrowsermail.cpp index 42411da9a..f28b1da8f 100755 --- a/src/gui/settings/settingsbrowsermail.cpp +++ b/src/gui/settings/settingsbrowsermail.cpp @@ -29,14 +29,18 @@ SettingsBrowserMail::SettingsBrowserMail(Settings* settings, QWidget* parent) : SettingsPanel(settings, parent), m_ui(new Ui::SettingsBrowserMail) { m_ui->setupUi(this); + GuiUtilities::setLabelAsNotice(*m_ui->label, false); GuiUtilities::setLabelAsNotice(*m_ui->m_lblExternalEmailInfo, false); GuiUtilities::setLabelAsNotice(*m_ui->m_lblProxyInfo, false); + GuiUtilities::setLabelAsNotice(*m_ui->m_lblToolInfo, false); + #if defined(USE_WEBENGINE) m_ui->m_checkOpenLinksInExternal->setVisible(false); #else connect(m_ui->m_checkOpenLinksInExternal, &QCheckBox::stateChanged, this, &SettingsBrowserMail::dirtifySettings); #endif + connect(m_ui->m_cmbProxyType, static_cast(&QComboBox::currentIndexChanged), this, &SettingsBrowserMail::dirtifySettings); connect(m_ui->m_txtProxyHost, &QLineEdit::textChanged, this, &SettingsBrowserMail::dirtifySettings); @@ -58,6 +62,13 @@ SettingsBrowserMail::SettingsBrowserMail(Settings* settings, QWidget* parent) connect(m_ui->m_cmbExternalEmailPreset, static_cast(&QComboBox::currentIndexChanged), this, &SettingsBrowserMail::changeDefaultEmailArguments); connect(m_ui->m_btnExternalEmailExecutable, &QPushButton::clicked, this, &SettingsBrowserMail::selectEmailExecutable); + connect(m_ui->m_btnAddTool, &QPushButton::clicked, this, &SettingsBrowserMail::dirtifySettings); + connect(m_ui->m_btnDeleteTool, &QPushButton::clicked, this, &SettingsBrowserMail::dirtifySettings); + connect(m_ui->m_btnAddTool, &QPushButton::clicked, this, &SettingsBrowserMail::addExternalTool); + connect(m_ui->m_btnDeleteTool, &QPushButton::clicked, this, &SettingsBrowserMail::deleteSelectedExternalTool); + connect(m_ui->m_listTools, &QListWidget::currentTextChanged, [this](const QString & current_text) { + m_ui->m_btnDeleteTool->setEnabled(!current_text.isEmpty()); + }); } SettingsBrowserMail::~SettingsBrowserMail() { @@ -107,7 +118,23 @@ void SettingsBrowserMail::onProxyTypeChanged(int index) { m_ui->m_lblProxyInfo->setEnabled(is_proxy_selected); m_ui->m_lblProxyPassword->setEnabled(is_proxy_selected); m_ui->m_lblProxyPort->setEnabled(is_proxy_selected); - m_ui->m_lblProxyUsername->setEnabled(is_proxy_selected); + m_ui->m_lblProxyUsername->setEnabled(is_proxy_selected); +} + +QStringList SettingsBrowserMail::externalTools() const { + QStringList list; + + for (int i = 0; i < m_ui->m_listTools->count(); i++) { + list.append(m_ui->m_listTools->item(i)->text()); + } + + return list; +} + +void SettingsBrowserMail::setExternalTools(const QStringList& list) { + foreach (const QString& tool, list) { + m_ui->m_listTools->addItem(tool); + } } void SettingsBrowserMail::changeDefaultEmailArguments(int index) { @@ -134,18 +161,21 @@ void SettingsBrowserMail::selectEmailExecutable() { void SettingsBrowserMail::loadSettings() { onBeginLoadSettings(); + #if !defined(USE_WEBENGINE) m_ui->m_checkOpenLinksInExternal->setChecked(settings()->value(GROUP(Browser), SETTING(Browser::OpenLinksInExternalBrowserRightAway)).toBool()); #endif - // Load settings of web browser GUI. + + // Load settings of web browser GUI. m_ui->m_cmbExternalBrowserPreset->addItem(tr("Opera 12 or older"), QSL("-nosession %1")); m_ui->m_txtExternalBrowserExecutable->setText(settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalBrowserExecutable)).toString()); m_ui->m_txtExternalBrowserArguments->setText(settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalBrowserArguments)).toString()); m_ui->m_grpCustomExternalBrowser->setChecked(settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalBrowserEnabled)).toBool()); - // Load settings of e-mail. + + // Load settings of e-mail. m_ui->m_cmbExternalEmailPreset->addItem(tr("Mozilla Thunderbird"), QSL("-compose \"subject='%1',body='%2'\"")); m_ui->m_txtExternalEmailExecutable->setText(settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalEmailExecutable)).toString()); m_ui->m_txtExternalEmailArguments->setText(settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalEmailArguments)).toString()); @@ -154,7 +184,8 @@ void SettingsBrowserMail::loadSettings() { m_ui->m_cmbProxyType->addItem(tr("System proxy"), QNetworkProxy::DefaultProxy); m_ui->m_cmbProxyType->addItem(tr("Socks5"), QNetworkProxy::Socks5Proxy); m_ui->m_cmbProxyType->addItem(tr("Http"), QNetworkProxy::HttpProxy); - // Load the settings. + + // Load the settings. QNetworkProxy::ProxyType selected_proxy_type = static_cast(settings()->value(GROUP(Proxy), SETTING(Proxy::Type)).toInt()); m_ui->m_cmbProxyType->setCurrentIndex(m_ui->m_cmbProxyType->findData(selected_proxy_type)); @@ -162,19 +193,24 @@ void SettingsBrowserMail::loadSettings() { m_ui->m_txtProxyUsername->setText(settings()->value(GROUP(Proxy), SETTING(Proxy::Username)).toString()); m_ui->m_txtProxyPassword->setText(TextFactory::decrypt(settings()->value(GROUP(Proxy), SETTING(Proxy::Password)).toString())); m_ui->m_spinProxyPort->setValue(settings()->value(GROUP(Proxy), SETTING(Proxy::Port)).toInt()); + + setExternalTools(settings()->value(GROUP(Browser), SETTING(Browser::ExternalTools)).toStringList()); onEndLoadSettings(); } void SettingsBrowserMail::saveSettings() { onBeginSaveSettings(); + #if !defined(USE_WEBENGINE) settings()->setValue(GROUP(Browser), Browser::OpenLinksInExternalBrowserRightAway, m_ui->m_checkOpenLinksInExternal->isChecked()); #endif - // Save settings of GUI of web browser. + + // Save settings of GUI of web browser. settings()->setValue(GROUP(Browser), Browser::CustomExternalBrowserEnabled, m_ui->m_grpCustomExternalBrowser->isChecked()); settings()->setValue(GROUP(Browser), Browser::CustomExternalBrowserExecutable, m_ui->m_txtExternalBrowserExecutable->text()); settings()->setValue(GROUP(Browser), Browser::CustomExternalBrowserArguments, m_ui->m_txtExternalBrowserArguments->text()); - // Save settings of e-mail. + + // Save settings of e-mail. settings()->setValue(GROUP(Browser), Browser::CustomExternalEmailExecutable, m_ui->m_txtExternalEmailExecutable->text()); settings()->setValue(GROUP(Browser), Browser::CustomExternalEmailArguments, m_ui->m_txtExternalEmailArguments->text()); settings()->setValue(GROUP(Browser), Browser::CustomExternalEmailEnabled, m_ui->m_grpCustomExternalEmail->isChecked()); @@ -183,7 +219,32 @@ void SettingsBrowserMail::saveSettings() { settings()->setValue(GROUP(Proxy), Proxy::Username, m_ui->m_txtProxyUsername->text()); settings()->setValue(GROUP(Proxy), Proxy::Password, TextFactory::encrypt(m_ui->m_txtProxyPassword->text())); settings()->setValue(GROUP(Proxy), Proxy::Port, m_ui->m_spinProxyPort->value()); - // Reload settings for all network access managers. + + settings()->setValue(GROUP(Browser), Browser::ExternalTools, externalTools()); + + // Reload settings for all network access managers. SilentNetworkAccessManager::instance()->loadSettings(); - onEndSaveSettings(); + onEndSaveSettings(); +} + +void SettingsBrowserMail::addExternalTool() { + QString executable_file = QFileDialog::getOpenFileName(this, + tr("Select external tool"), + qApp->homeFolder(), + //: File filter for external tool selection dialog. +#if defined(Q_OS_LINUX) + tr("Executables (*)")); +#else + tr("Executables (*.*)")); +#endif + + if (!executable_file.isEmpty()) { + m_ui->m_listTools->addItem(QDir::toNativeSeparators(executable_file)); + } +} + +void SettingsBrowserMail::deleteSelectedExternalTool() { + if (m_ui->m_listTools->currentRow() >= 0) { + m_ui->m_listTools->takeItem(m_ui->m_listTools->currentRow()); + } } diff --git a/src/gui/settings/settingsbrowsermail.h b/src/gui/settings/settingsbrowsermail.h index d23b23147..562a9b792 100755 --- a/src/gui/settings/settingsbrowsermail.h +++ b/src/gui/settings/settingsbrowsermail.h @@ -38,6 +38,8 @@ class SettingsBrowserMail : public SettingsPanel { void saveSettings(); private slots: + void addExternalTool(); + void deleteSelectedExternalTool(); void changeDefaultBrowserArguments(int index); void selectBrowserExecutable(); void changeDefaultEmailArguments(int index); @@ -46,6 +48,9 @@ class SettingsBrowserMail : public SettingsPanel { void onProxyTypeChanged(int index); private: + QStringList externalTools() const; + void setExternalTools(const QStringList& list); + Ui::SettingsBrowserMail* m_ui; }; diff --git a/src/gui/settings/settingsbrowsermail.ui b/src/gui/settings/settingsbrowsermail.ui index 17eec220a..ab01cfb13 100755 --- a/src/gui/settings/settingsbrowsermail.ui +++ b/src/gui/settings/settingsbrowsermail.ui @@ -7,7 +7,7 @@ 0 0 658 - 200 + 330 @@ -229,6 +229,53 @@ + + + External tools + + + + + + On this page, you can setup a list of external tools which can open URLs of selected messages. + + + + + + + Qt::Horizontal + + + + Add external tool + + + + + false + + + Delete selected external tool + + + + + + + + false + + + true + + + true + + + + + Proxy diff --git a/src/miscellaneous/settings.cpp b/src/miscellaneous/settings.cpp index 404a0a0ce..1a3b7b374 100755 --- a/src/miscellaneous/settings.cpp +++ b/src/miscellaneous/settings.cpp @@ -286,6 +286,8 @@ DVALUE(QString) Browser::CustomExternalEmailExecutableDef = QString(); DKEY Browser::CustomExternalEmailArguments = "external_email_arguments"; DVALUE(char*) Browser::CustomExternalEmailArgumentsDef = ""; +DKEY Browser::ExternalTools = "external_tools"; +DVALUE(QStringList) Browser::ExternalToolsDef = QStringList(); // Categories. DKEY CategoriesExpandStates::ID = "categories_expand_states"; diff --git a/src/miscellaneous/settings.h b/src/miscellaneous/settings.h index fb429f21e..e2a980c45 100755 --- a/src/miscellaneous/settings.h +++ b/src/miscellaneous/settings.h @@ -316,6 +316,9 @@ namespace Browser { KEY CustomExternalEmailExecutable; VALUE(QString) CustomExternalEmailExecutableDef; + KEY ExternalTools; + VALUE(QStringList) ExternalToolsDef; + KEY CustomExternalEmailArguments; VALUE(char*) CustomExternalEmailArgumentsDef; } diff --git a/src/network-web/downloadmanager.cpp b/src/network-web/downloadmanager.cpp index 7d2c143eb..1f1618f81 100755 --- a/src/network-web/downloadmanager.cpp +++ b/src/network-web/downloadmanager.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -47,7 +46,8 @@ DownloadItem::DownloadItem(QNetworkReply* reply, QWidget* parent) : QWidget(pare m_ui->setupUi(this); m_ui->m_btnTryAgain->hide(); m_requestFileName = qApp->settings()->value(GROUP(Downloads), SETTING(Downloads::AlwaysPromptForFilename)).toBool(); - connect(m_ui->m_btnStopDownload, &QToolButton::clicked, this, &DownloadItem::stop); + + connect(m_ui->m_btnStopDownload, &QToolButton::clicked, this, &DownloadItem::stop); connect(m_ui->m_btnOpenFile, &QToolButton::clicked, this, &DownloadItem::openFile); connect(m_ui->m_btnTryAgain, &QToolButton::clicked, this, &DownloadItem::tryAgain); connect(m_ui->m_btnOpenFolder, &QToolButton::clicked, this, &DownloadItem::openFolder); @@ -69,16 +69,19 @@ void DownloadItem::init() { m_ui->m_btnOpenFolder->setEnabled(false); m_url = m_reply->url(); m_reply->setParent(this); - connect(m_reply, &QNetworkReply::readyRead, this, &DownloadItem::downloadReadyRead); + + connect(m_reply, &QNetworkReply::readyRead, this, &DownloadItem::downloadReadyRead); connect(m_reply, static_cast(&QNetworkReply::error), this, &DownloadItem::error); connect(m_reply, &QNetworkReply::downloadProgress, this, &DownloadItem::downloadProgress); connect(m_reply, &QNetworkReply::metaDataChanged, this, &DownloadItem::metaDataChanged); connect(m_reply, &QNetworkReply::finished, this, &DownloadItem::finished); - // Reset info. + + // Reset info. m_ui->m_lblInfoDownload->clear(); m_ui->m_progressDownload->setValue(0); getFileName(); - // Start timer for the download estimation. + + // Start timer for the download estimation. m_downloadTime.start(); if (m_reply->error() != QNetworkReply::NoError) { @@ -119,7 +122,8 @@ void DownloadItem::getFileName() { } m_output.setFileName(chosen_filename); - // Check file path for saving. + + // Check file path for saving. const QDir save_dir = QFileInfo(m_output.fileName()).dir(); if (!save_dir.exists() && !save_dir.mkpath(save_dir.absolutePath())) { @@ -275,7 +279,8 @@ void DownloadItem::downloadReadyRead() { void DownloadItem::error(QNetworkReply::NetworkError code) { Q_UNUSED(code) - m_ui->m_lblInfoDownload->setText(tr("Error: %1").arg(m_reply->errorString())); + + m_ui->m_lblInfoDownload->setText(tr("Error: %1").arg(m_reply->errorString())); m_ui->m_btnTryAgain->setEnabled(true); m_ui->m_btnTryAgain->setVisible(true); emit downloadFinished(); @@ -511,7 +516,8 @@ void DownloadManager::addItem(DownloadItem* item) { connect(item, &DownloadItem::statusChanged, this, static_cast(&DownloadManager::updateRow)); connect(item, &DownloadItem::progress, this, &DownloadManager::itemProgress); connect(item, &DownloadItem::downloadFinished, this, &DownloadManager::itemFinished); - const int row = m_downloads.count(); + + const int row = m_downloads.count(); m_model->beginInsertRows(QModelIndex(), row, row); m_downloads.append(item); m_model->endInsertRows(); @@ -519,7 +525,8 @@ void DownloadManager::addItem(DownloadItem* item) { QIcon icon = style()->standardIcon(QStyle::SP_FileIcon); item->m_ui->m_lblFileIcon->setPixmap(icon.pixmap(DOWNLOADER_ICON_SIZE, DOWNLOADER_ICON_SIZE)); m_ui->m_viewDownloads->setRowHeight(row, item->sizeHint().height()); - // Just in case of download finishes before it is actually added. + + // Just in case of download finishes before it is actually added. updateRow(item); } @@ -572,7 +579,8 @@ void DownloadManager::updateRow(DownloadItem* item) { item->m_ui->m_lblFileIcon->setPixmap(icon.pixmap(DOWNLOADER_ICON_SIZE, DOWNLOADER_ICON_SIZE)); int old_height = m_ui->m_viewDownloads->rowHeight(row); m_ui->m_viewDownloads->setRowHeight(row, qMax(old_height, item->minimumSizeHint().height())); - // Remove the item if: + + // Remove the item if: // a) It is not downloading and private browsing is enabled. // OR // b) Item is already downloaded and it should be remove from downloader list.