From 208284e80d0a9bf8749efd9f0a9f36c750b3ae5d Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Fri, 21 Jul 2017 06:53:23 +0200 Subject: [PATCH] Reformated using astyle, added some astyle scripts. --- .astylerc | 26 + astyle-format-all.sh | 64 + src/core/feeddownloader.cpp | 348 +- src/core/feeddownloader.h | 208 +- src/core/feedsmodel.cpp | 1098 +++--- src/core/feedsmodel.h | 364 +- src/core/feedsproxymodel.cpp | 542 +-- src/core/feedsproxymodel.h | 140 +- src/core/message.cpp | 224 +- src/core/message.h | 170 +- src/core/messagesmodel.cpp | 763 +++-- src/core/messagesmodel.h | 124 +- src/core/messagesmodelcache.cpp | 78 +- src/core/messagesmodelcache.h | 110 +- src/core/messagesmodelsqllayer.cpp | 210 +- src/core/messagesmodelsqllayer.h | 112 +- src/core/messagesproxymodel.cpp | 265 +- src/core/messagesproxymodel.h | 38 +- src/definitions/definitions.h | 516 +-- src/dynamic-shortcuts/dynamicshortcuts.cpp | 26 +- src/dynamic-shortcuts/dynamicshortcuts.h | 20 +- .../dynamicshortcutswidget.cpp | 130 +- .../dynamicshortcutswidget.h | 52 +- src/dynamic-shortcuts/shortcutbutton.cpp | 135 +- src/dynamic-shortcuts/shortcutbutton.h | 20 +- src/dynamic-shortcuts/shortcutcatcher.cpp | 173 +- src/dynamic-shortcuts/shortcutcatcher.h | 58 +- src/exceptions/applicationexception.cpp | 58 +- src/exceptions/applicationexception.h | 70 +- src/exceptions/ioexception.cpp | 50 +- src/exceptions/ioexception.h | 60 +- src/gui/baselineedit.cpp | 82 +- src/gui/baselineedit.h | 86 +- src/gui/basetoolbar.cpp | 100 +- src/gui/basetoolbar.h | 120 +- src/gui/clickablelabel.cpp | 76 +- src/gui/clickablelabel.h | 44 +- src/gui/colorlabel.cpp | 85 +- src/gui/colorlabel.h | 82 +- src/gui/comboboxwithstatus.cpp | 76 +- src/gui/comboboxwithstatus.h | 78 +- src/gui/dialogs/formabout.cpp | 244 +- src/gui/dialogs/formabout.h | 86 +- src/gui/dialogs/formaddaccount.cpp | 75 +- src/gui/dialogs/formaddaccount.h | 26 +- .../dialogs/formbackupdatabasesettings.cpp | 101 +- src/gui/dialogs/formbackupdatabasesettings.h | 26 +- src/gui/dialogs/formdatabasecleanup.cpp | 266 +- src/gui/dialogs/formdatabasecleanup.h | 120 +- src/gui/dialogs/formmain.cpp | 1583 +++++---- src/gui/dialogs/formmain.h | 250 +- .../dialogs/formrestoredatabasesettings.cpp | 275 +- src/gui/dialogs/formrestoredatabasesettings.h | 102 +- src/gui/dialogs/formsettings.cpp | 285 +- src/gui/dialogs/formsettings.h | 104 +- src/gui/dialogs/formupdate.cpp | 518 +-- src/gui/dialogs/formupdate.h | 132 +- src/gui/discoverfeedsbutton.cpp | 186 +- src/gui/discoverfeedsbutton.h | 90 +- src/gui/edittableview.cpp | 61 +- src/gui/edittableview.h | 16 +- src/gui/feedmessageviewer.cpp | 326 +- src/gui/feedmessageviewer.h | 102 +- src/gui/feedstoolbar.cpp | 207 +- src/gui/feedstoolbar.h | 86 +- src/gui/feedsview.cpp | 1121 +++--- src/gui/feedsview.h | 292 +- src/gui/guiutilities.cpp | 75 +- src/gui/guiutilities.h | 64 +- src/gui/labelwithstatus.cpp | 90 +- src/gui/labelwithstatus.h | 84 +- src/gui/lineeditwithstatus.cpp | 81 +- src/gui/lineeditwithstatus.h | 80 +- src/gui/locationlineedit.cpp | 105 +- src/gui/locationlineedit.h | 88 +- src/gui/messagebox.cpp | 217 +- src/gui/messagebox.h | 104 +- src/gui/messagepreviewer.cpp | 453 ++- src/gui/messagepreviewer.h | 156 +- src/gui/messagessearchlineedit.cpp | 50 +- src/gui/messagessearchlineedit.h | 70 +- src/gui/messagestoolbar.cpp | 338 +- src/gui/messagestoolbar.h | 164 +- src/gui/messagesview.cpp | 1078 +++--- src/gui/messagesview.h | 250 +- src/gui/messagetextbrowser.cpp | 102 +- src/gui/messagetextbrowser.h | 80 +- src/gui/newspaperpreviewer.cpp | 129 +- src/gui/newspaperpreviewer.h | 116 +- src/gui/plaintoolbutton.cpp | 61 +- src/gui/plaintoolbutton.h | 34 +- src/gui/settings/settingsbrowsermail.cpp | 387 +-- src/gui/settings/settingsbrowsermail.h | 104 +- src/gui/settings/settingsdatabase.cpp | 422 ++- src/gui/settings/settingsdatabase.h | 104 +- src/gui/settings/settingsdownloads.cpp | 144 +- src/gui/settings/settingsdownloads.h | 94 +- src/gui/settings/settingsfeedsmessages.cpp | 289 +- src/gui/settings/settingsfeedsmessages.h | 98 +- src/gui/settings/settingsgeneral.cpp | 168 +- src/gui/settings/settingsgeneral.h | 88 +- src/gui/settings/settingsgui.cpp | 528 ++- src/gui/settings/settingsgui.h | 96 +- src/gui/settings/settingslocalization.cpp | 176 +- src/gui/settings/settingslocalization.h | 88 +- src/gui/settings/settingspanel.cpp | 147 +- src/gui/settings/settingspanel.h | 138 +- src/gui/settings/settingsshortcuts.cpp | 95 +- src/gui/settings/settingsshortcuts.h | 88 +- src/gui/squeezelabel.cpp | 20 +- src/gui/squeezelabel.h | 14 +- src/gui/statusbar.cpp | 552 ++- src/gui/statusbar.h | 152 +- src/gui/styleditemdelegatewithoutfocus.cpp | 72 +- src/gui/styleditemdelegatewithoutfocus.h | 74 +- src/gui/systemtrayicon.cpp | 368 +- src/gui/systemtrayicon.h | 184 +- src/gui/tabbar.cpp | 191 +- src/gui/tabbar.h | 54 +- src/gui/tabcontent.cpp | 2 +- src/gui/tabcontent.h | 38 +- src/gui/tabwidget.cpp | 423 ++- src/gui/tabwidget.h | 148 +- src/gui/timespinbox.cpp | 175 +- src/gui/timespinbox.h | 74 +- src/gui/toolbareditor.cpp | 551 ++- src/gui/toolbareditor.h | 162 +- src/gui/treeviewcolumnsmenu.cpp | 104 +- src/gui/treeviewcolumnsmenu.h | 78 +- src/gui/treewidget.cpp | 242 +- src/gui/treewidget.h | 66 +- src/gui/webbrowser.cpp | 584 ++-- src/gui/webbrowser.h | 244 +- src/gui/webviewer.cpp | 210 +- src/gui/webviewer.h | 52 +- src/gui/widgetwithstatus.cpp | 153 +- src/gui/widgetwithstatus.h | 130 +- src/main.cpp | 305 +- src/miscellaneous/application.cpp | 918 +++-- src/miscellaneous/application.h | 368 +- src/miscellaneous/autosaver.cpp | 64 +- src/miscellaneous/autosaver.h | 24 +- src/miscellaneous/databasecleaner.cpp | 200 +- src/miscellaneous/databasecleaner.h | 116 +- src/miscellaneous/databasefactory.cpp | 1671 +++++---- src/miscellaneous/databasefactory.h | 362 +- src/miscellaneous/databasequeries.cpp | 3050 ++++++++--------- src/miscellaneous/databasequeries.h | 260 +- src/miscellaneous/debugging.cpp | 159 +- src/miscellaneous/debugging.h | 74 +- src/miscellaneous/feedreader.cpp | 405 ++- src/miscellaneous/feedreader.h | 112 +- src/miscellaneous/iconfactory.cpp | 179 +- src/miscellaneous/iconfactory.h | 60 +- src/miscellaneous/iofactory.cpp | 257 +- src/miscellaneous/iofactory.h | 114 +- src/miscellaneous/localization.cpp | 189 +- src/miscellaneous/localization.h | 142 +- src/miscellaneous/mutex.cpp | 148 +- src/miscellaneous/mutex.h | 120 +- src/miscellaneous/serviceoperator.cpp | 50 +- src/miscellaneous/serviceoperator.h | 72 +- src/miscellaneous/settings.cpp | 771 +++-- src/miscellaneous/settings.h | 758 ++-- src/miscellaneous/settingsproperties.h | 44 +- src/miscellaneous/simplecrypt/simplecrypt.cpp | 495 +-- src/miscellaneous/simplecrypt/simplecrypt.h | 466 +-- src/miscellaneous/simpleregexp.cpp | 75 +- src/miscellaneous/simpleregexp.h | 22 +- src/miscellaneous/skinfactory.cpp | 359 +- src/miscellaneous/skinfactory.h | 166 +- src/miscellaneous/systemfactory.cpp | 378 +- src/miscellaneous/systemfactory.h | 92 +- src/miscellaneous/textfactory.cpp | 176 +- src/miscellaneous/textfactory.h | 50 +- .../adblock/adblockaddsubscriptiondialog.cpp | 91 +- .../adblock/adblockaddsubscriptiondialog.h | 42 +- src/network-web/adblock/adblockdialog.cpp | 162 +- src/network-web/adblock/adblockdialog.h | 50 +- src/network-web/adblock/adblockicon.cpp | 265 +- src/network-web/adblock/adblockicon.h | 40 +- src/network-web/adblock/adblockmanager.cpp | 474 ++- src/network-web/adblock/adblockmanager.h | 81 +- src/network-web/adblock/adblockmatcher.cpp | 322 +- src/network-web/adblock/adblockmatcher.h | 46 +- src/network-web/adblock/adblockrule.cpp | 1008 +++--- src/network-web/adblock/adblockrule.h | 190 +- src/network-web/adblock/adblocksearchtree.cpp | 144 +- src/network-web/adblock/adblocksearchtree.h | 34 +- .../adblock/adblocksubscription.cpp | 458 ++- src/network-web/adblock/adblocksubscription.h | 98 +- src/network-web/adblock/adblocktreewidget.cpp | 323 +- src/network-web/adblock/adblocktreewidget.h | 44 +- .../adblock/adblockurlinterceptor.cpp | 12 +- .../adblock/adblockurlinterceptor.h | 12 +- src/network-web/basenetworkaccessmanager.cpp | 162 +- src/network-web/basenetworkaccessmanager.h | 94 +- src/network-web/downloader.cpp | 441 ++- src/network-web/downloader.h | 202 +- src/network-web/downloadmanager.cpp | 1084 +++--- src/network-web/downloadmanager.h | 214 +- src/network-web/googlesuggest.cpp | 402 ++- src/network-web/googlesuggest.h | 164 +- src/network-web/networkfactory.cpp | 258 +- src/network-web/networkfactory.h | 38 +- src/network-web/networkurlinterceptor.cpp | 38 +- src/network-web/networkurlinterceptor.h | 20 +- .../silentnetworkaccessmanager.cpp | 47 +- src/network-web/silentnetworkaccessmanager.h | 24 +- src/network-web/urlinterceptor.h | 8 +- src/network-web/webfactory.cpp | 318 +- src/network-web/webfactory.h | 144 +- src/network-web/webpage.cpp | 148 +- src/network-web/webpage.h | 98 +- src/qtsingleapplication/qtlocalpeer.cpp | 248 +- src/qtsingleapplication/qtlocalpeer.h | 42 +- src/qtsingleapplication/qtlockedfile.cpp | 44 +- src/qtsingleapplication/qtlockedfile.h | 43 +- src/qtsingleapplication/qtlockedfile_unix.cpp | 126 +- src/qtsingleapplication/qtlockedfile_win.cpp | 315 +- .../qtsingleapplication.cpp | 105 +- src/qtsingleapplication/qtsingleapplication.h | 55 +- .../qtsinglecoreapplication.cpp | 33 +- .../qtsinglecoreapplication.h | 27 +- src/services/abstract/accountcheckmodel.cpp | 391 +-- src/services/abstract/accountcheckmodel.h | 60 +- src/services/abstract/cacheforserviceroot.cpp | 184 +- src/services/abstract/cacheforserviceroot.h | 92 +- src/services/abstract/category.cpp | 131 +- src/services/abstract/category.h | 68 +- src/services/abstract/feed.cpp | 493 ++- src/services/abstract/feed.h | 220 +- src/services/abstract/gui/formfeeddetails.cpp | 837 +++-- src/services/abstract/gui/formfeeddetails.h | 194 +- src/services/abstract/label.cpp | 44 +- src/services/abstract/label.h | 58 +- src/services/abstract/labelsrootitem.cpp | 55 +- src/services/abstract/labelsrootitem.h | 58 +- src/services/abstract/recyclebin.cpp | 155 +- src/services/abstract/recyclebin.h | 58 +- src/services/abstract/rootitem.cpp | 973 +++--- src/services/abstract/rootitem.h | 478 +-- src/services/abstract/serviceentrypoint.cpp | 44 +- src/services/abstract/serviceentrypoint.h | 162 +- src/services/abstract/serviceroot.cpp | 1032 +++--- src/services/abstract/serviceroot.h | 434 +-- src/services/owncloud/definitions.h | 54 +- .../owncloud/gui/formeditowncloudaccount.cpp | 390 ++- .../owncloud/gui/formeditowncloudaccount.h | 118 +- .../owncloud/gui/formowncloudfeeddetails.cpp | 198 +- .../owncloud/gui/formowncloudfeeddetails.h | 74 +- .../network/owncloudnetworkfactory.cpp | 1177 ++++--- .../owncloud/network/owncloudnetworkfactory.h | 314 +- src/services/owncloud/owncloudcategory.cpp | 94 +- src/services/owncloud/owncloudcategory.h | 80 +- src/services/owncloud/owncloudfeed.cpp | 242 +- src/services/owncloud/owncloudfeed.h | 102 +- src/services/owncloud/owncloudrecyclebin.cpp | 80 +- src/services/owncloud/owncloudrecyclebin.h | 74 +- .../owncloud/owncloudserviceentrypoint.cpp | 145 +- .../owncloud/owncloudserviceentrypoint.h | 80 +- src/services/owncloud/owncloudserviceroot.cpp | 606 ++-- src/services/owncloud/owncloudserviceroot.h | 158 +- src/services/standard/atomparser.cpp | 259 +- src/services/standard/atomparser.h | 88 +- src/services/standard/feedparser.cpp | 199 +- src/services/standard/feedparser.h | 90 +- .../gui/formstandardcategorydetails.cpp | 348 +- .../gui/formstandardcategorydetails.h | 76 +- .../standard/gui/formstandardfeeddetails.cpp | 181 +- .../standard/gui/formstandardfeeddetails.h | 74 +- .../standard/gui/formstandardimportexport.cpp | 422 ++- .../standard/gui/formstandardimportexport.h | 56 +- src/services/standard/rdfparser.cpp | 199 +- src/services/standard/rdfparser.h | 68 +- src/services/standard/rssparser.cpp | 245 +- src/services/standard/rssparser.h | 76 +- src/services/standard/standardcategory.cpp | 391 +-- src/services/standard/standardcategory.h | 144 +- src/services/standard/standardfeed.cpp | 982 +++--- src/services/standard/standardfeed.h | 318 +- .../standardfeedsimportexportmodel.cpp | 496 ++- .../standard/standardfeedsimportexportmodel.h | 54 +- .../standard/standardserviceentrypoint.cpp | 164 +- .../standard/standardserviceentrypoint.h | 82 +- src/services/standard/standardserviceroot.cpp | 720 ++-- src/services/standard/standardserviceroot.h | 186 +- src/services/tt-rss/definitions.h | 128 +- .../tt-rss/gui/formeditttrssaccount.cpp | 538 ++- .../tt-rss/gui/formeditttrssaccount.h | 124 +- .../tt-rss/gui/formttrssfeeddetails.cpp | 189 +- .../tt-rss/gui/formttrssfeeddetails.h | 76 +- .../tt-rss/network/ttrssnetworkfactory.cpp | 1291 ++++--- .../tt-rss/network/ttrssnetworkfactory.h | 374 +- src/services/tt-rss/ttrsscategory.cpp | 43 +- src/services/tt-rss/ttrsscategory.h | 16 +- src/services/tt-rss/ttrssfeed.cpp | 210 +- src/services/tt-rss/ttrssfeed.h | 34 +- src/services/tt-rss/ttrssrecyclebin.cpp | 10 +- src/services/tt-rss/ttrssrecyclebin.h | 12 +- .../tt-rss/ttrssserviceentrypoint.cpp | 155 +- src/services/tt-rss/ttrssserviceentrypoint.h | 84 +- src/services/tt-rss/ttrssserviceroot.cpp | 669 ++-- src/services/tt-rss/ttrssserviceroot.h | 160 +- 304 files changed, 34084 insertions(+), 34464 deletions(-) create mode 100755 .astylerc create mode 100755 astyle-format-all.sh mode change 100644 => 100755 src/gui/timespinbox.cpp mode change 100644 => 100755 src/miscellaneous/simplecrypt/simplecrypt.cpp mode change 100644 => 100755 src/miscellaneous/simplecrypt/simplecrypt.h mode change 100644 => 100755 src/qtsingleapplication/qtlocalpeer.h mode change 100644 => 100755 src/qtsingleapplication/qtlockedfile.cpp mode change 100644 => 100755 src/qtsingleapplication/qtlockedfile.h mode change 100644 => 100755 src/qtsingleapplication/qtlockedfile_unix.cpp mode change 100644 => 100755 src/qtsingleapplication/qtlockedfile_win.cpp mode change 100644 => 100755 src/qtsingleapplication/qtsingleapplication.h mode change 100644 => 100755 src/qtsingleapplication/qtsinglecoreapplication.h mode change 100644 => 100755 src/services/abstract/accountcheckmodel.h mode change 100644 => 100755 src/services/abstract/gui/formfeeddetails.h mode change 100644 => 100755 src/services/standard/atomparser.cpp mode change 100644 => 100755 src/services/standard/atomparser.h mode change 100644 => 100755 src/services/standard/feedparser.cpp mode change 100644 => 100755 src/services/standard/feedparser.h mode change 100644 => 100755 src/services/standard/rdfparser.cpp mode change 100644 => 100755 src/services/standard/rdfparser.h mode change 100644 => 100755 src/services/standard/rssparser.cpp mode change 100644 => 100755 src/services/standard/rssparser.h mode change 100644 => 100755 src/services/tt-rss/gui/formttrssfeeddetails.h diff --git a/.astylerc b/.astylerc new file mode 100755 index 000000000..ebc219449 --- /dev/null +++ b/.astylerc @@ -0,0 +1,26 @@ +--style=java +--indent=spaces=2 +--indent-classes +--indent-namespaces +--indent-switches +--indent-labels +--indent-preproc-define +--indent-col1-comments +--max-continuation-indent=100 +--break-blocks=all +--unpad-paren +--delete-empty-lines +--pad-oper +--pad-comma +--pad-header +--align-pointer=type +--align-reference=type +--break-closing-braces +--break-one-line-headers +--add-braces +--convert-tabs +--close-templates +--max-code-length=200 +--lineend=linux +-t -p +-M60Ucv \ No newline at end of file diff --git a/astyle-format-all.sh b/astyle-format-all.sh new file mode 100755 index 000000000..628b0a731 --- /dev/null +++ b/astyle-format-all.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# +# This file is part of RSS Guard. +# +# Copyright (C) 2011-2017 by Martin Rotter +# Copyright (C) 2010-2014 by David Rosca +# +# RSS Guard is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RSS Guard is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with RSS Guard. If not, see . + +function usage { + echo "Usage: $0 \"root-directory\"..." + exit 1 +} + +if [ $# -eq 0 ]; then + usage +fi + +ASTYLE_CMD="astyle" +ASTYLE_RC=".astylerc" + +# Check all args. +for dir in "$@"; do + if [ ! -d "${dir}" ]; then + echo "\"${dir}\" is not a directory..." + usage + fi +done + +# Run the thing. +for dir in "$@"; do + pushd "${dir}" + + if [ ! -r "$ASTYLE_RC" ]; then + echo "No $ASTYLE_RC in pwd \"$PWD\"..." + continue + fi + + for f in $(find . \ + -name '*.c' \ + -o -name '*.cc' \ + -o -name '*.cpp' \ + -o -name '*.h' \ + -o -name '*.hh' \ + -o -name '*.hpp'); do + "${ASTYLE_CMD}" --options="$ASTYLE_RC" "${f}" + done + + # Remove backup files. + find . -name "*.orig" | xargs --no-run-if-empty rm -v + + popd +done diff --git a/src/core/feeddownloader.cpp b/src/core/feeddownloader.cpp index 4a77aafbc..da2220de8 100755 --- a/src/core/feeddownloader.cpp +++ b/src/core/feeddownloader.cpp @@ -1,178 +1,170 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "core/feeddownloader.h" - -#include "services/abstract/feed.h" -#include "definitions/definitions.h" - -#include -#include -#include -#include -#include - - -FeedDownloader::FeedDownloader(QObject *parent) - : QObject(parent), m_feeds(QList()), m_mutex(new QMutex()), m_threadPool(new QThreadPool(this)), - m_results(FeedDownloadResults()), m_feedsUpdated(0), - m_feedsUpdating(0), m_feedsOriginalCount(0) { - qRegisterMetaType("FeedDownloadResults"); - m_threadPool->setMaxThreadCount(FEED_DOWNLOADER_MAX_THREADS); -} - -FeedDownloader::~FeedDownloader() { - m_mutex->tryLock(); - m_mutex->unlock(); - delete m_mutex; - - qDebug("Destroying FeedDownloader instance."); -} - -bool FeedDownloader::isUpdateRunning() const { - return !m_feeds.isEmpty() || m_feedsUpdating > 0; -} - -void FeedDownloader::updateAvailableFeeds() { - while (!m_feeds.isEmpty()) { - connect(m_feeds.first(), &Feed::messagesObtained, this, &FeedDownloader::oneFeedUpdateFinished, - (Qt::ConnectionType) (Qt::UniqueConnection | Qt::AutoConnection)); - if (m_threadPool->tryStart(m_feeds.first())) { - m_feeds.removeFirst(); - m_feedsUpdating++; - } - else { - // We want to start update of some feeds but all working threads are occupied. - break; - } - } -} - -void FeedDownloader::updateFeeds(const QList &feeds) { - QMutexLocker locker(m_mutex); - - if (feeds.isEmpty()) { - qDebug("No feeds to update in worker thread, aborting update."); - finalizeUpdate(); - } - else { - qDebug().nospace() << "Starting feed updates from worker in thread: \'" << QThread::currentThreadId() << "\'."; - - m_feeds = feeds; - m_feedsOriginalCount = m_feeds.size(); - m_results.clear(); - m_feedsUpdated = m_feedsUpdating = 0; - - // Job starts now. - emit updateStarted(); - updateAvailableFeeds(); - } -} - -void FeedDownloader::stopRunningUpdate() { - m_threadPool->clear(); - m_feeds.clear(); -} - -void FeedDownloader::oneFeedUpdateFinished(const QList &messages, bool error_during_obtaining) { - QMutexLocker locker(m_mutex); - - m_feedsUpdated++; - m_feedsUpdating--; - - Feed *feed = qobject_cast(sender()); - - disconnect(feed, &Feed::messagesObtained, this, &FeedDownloader::oneFeedUpdateFinished); - - // Now, we check if there are any feeds we would like to update too. - updateAvailableFeeds(); - - // Now make sure, that messages are actually stored to SQL in a locked state. - qDebug().nospace() << "Saving messages of feed " - << feed->id() << " in thread: \'" - << QThread::currentThreadId() << "\'."; - - int updated_messages = feed->updateMessages(messages, error_during_obtaining); - - /* - QMetaObject::invokeMethod(feed, "updateMessages", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(int, updated_messages), - Q_ARG(QList, messages)); - */ - - if (updated_messages > 0) { - m_results.appendUpdatedFeed(QPair(feed->title(), updated_messages)); - } - - qDebug("Made progress in feed updates, total feeds count %d/%d (id of feed is %d).", m_feedsUpdated, m_feedsOriginalCount, feed->id()); - emit updateProgress(feed, m_feedsUpdated, m_feedsOriginalCount); - - if (m_feeds.isEmpty() && m_feedsUpdating <= 0) { - finalizeUpdate(); - } -} - -void FeedDownloader::finalizeUpdate() { - qDebug().nospace() << "Finished feed updates in thread: \'" << QThread::currentThreadId() << "\'."; - - m_results.sort(); - - // Update of feeds has finished. - // NOTE: This means that now "update lock" can be unlocked - // and feeds can be added/edited/deleted and application - // can eventually quit. - emit updateFinished(m_results); -} - -FeedDownloadResults::FeedDownloadResults() : m_updatedFeeds(QList >()) { -} - -QString FeedDownloadResults::overview(int how_many_feeds) const { - QStringList result; - - for (int i = 0, number_items_output = qMin(how_many_feeds, m_updatedFeeds.size()); i < number_items_output; i++) { - result.append(m_updatedFeeds.at(i).first + QSL(": ") + QString::number(m_updatedFeeds.at(i).second)); - } - - QString res_str = result.join(QSL("\n")); - - if (m_updatedFeeds.size() > how_many_feeds) { - res_str += QObject::tr("\n\n+ %n other feeds.", 0, m_updatedFeeds.size() - how_many_feeds); - } - - return res_str; -} - -void FeedDownloadResults::appendUpdatedFeed(const QPair &feed) { - m_updatedFeeds.append(feed); -} - -void FeedDownloadResults::sort() { - qSort(m_updatedFeeds.begin(), m_updatedFeeds.end(), FeedDownloadResults::lessThan); -} - -bool FeedDownloadResults::lessThan(const QPair &lhs, const QPair &rhs) { - return lhs.second > rhs.second; -} - -void FeedDownloadResults::clear() { - m_updatedFeeds.clear(); -} - -QList > FeedDownloadResults::updatedFeeds() const { - return m_updatedFeeds; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "core/feeddownloader.h" + +#include "services/abstract/feed.h" +#include "definitions/definitions.h" + +#include +#include +#include +#include +#include + + +FeedDownloader::FeedDownloader(QObject* parent) + : QObject(parent), m_feeds(QList()), m_mutex(new QMutex()), m_threadPool(new QThreadPool(this)), + m_results(FeedDownloadResults()), m_feedsUpdated(0), + m_feedsUpdating(0), m_feedsOriginalCount(0) { + qRegisterMetaType("FeedDownloadResults"); + m_threadPool->setMaxThreadCount(FEED_DOWNLOADER_MAX_THREADS); +} + +FeedDownloader::~FeedDownloader() { + m_mutex->tryLock(); + m_mutex->unlock(); + delete m_mutex; + qDebug("Destroying FeedDownloader instance."); +} + +bool FeedDownloader::isUpdateRunning() const { + return !m_feeds.isEmpty() || m_feedsUpdating > 0; +} + +void FeedDownloader::updateAvailableFeeds() { + while (!m_feeds.isEmpty()) { + connect(m_feeds.first(), &Feed::messagesObtained, this, &FeedDownloader::oneFeedUpdateFinished, + (Qt::ConnectionType)(Qt::UniqueConnection | Qt::AutoConnection)); + + if (m_threadPool->tryStart(m_feeds.first())) { + m_feeds.removeFirst(); + m_feedsUpdating++; + } + + else { + // We want to start update of some feeds but all working threads are occupied. + break; + } + } +} + +void FeedDownloader::updateFeeds(const QList& feeds) { + QMutexLocker locker(m_mutex); + + if (feeds.isEmpty()) { + qDebug("No feeds to update in worker thread, aborting update."); + finalizeUpdate(); + } + + else { + qDebug().nospace() << "Starting feed updates from worker in thread: \'" << QThread::currentThreadId() << "\'."; + m_feeds = feeds; + m_feedsOriginalCount = m_feeds.size(); + m_results.clear(); + m_feedsUpdated = m_feedsUpdating = 0; + // Job starts now. + emit updateStarted(); + updateAvailableFeeds(); + } +} + +void FeedDownloader::stopRunningUpdate() { + m_threadPool->clear(); + m_feeds.clear(); +} + +void FeedDownloader::oneFeedUpdateFinished(const QList& messages, bool error_during_obtaining) { + QMutexLocker locker(m_mutex); + m_feedsUpdated++; + m_feedsUpdating--; + Feed* feed = qobject_cast(sender()); + disconnect(feed, &Feed::messagesObtained, this, &FeedDownloader::oneFeedUpdateFinished); + // Now, we check if there are any feeds we would like to update too. + updateAvailableFeeds(); + // Now make sure, that messages are actually stored to SQL in a locked state. + qDebug().nospace() << "Saving messages of feed " + << feed->id() << " in thread: \'" + << QThread::currentThreadId() << "\'."; + int updated_messages = feed->updateMessages(messages, error_during_obtaining); + + /* + QMetaObject::invokeMethod(feed, "updateMessages", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(int, updated_messages), + Q_ARG(QList, messages)); + */ + + if (updated_messages > 0) { + m_results.appendUpdatedFeed(QPair(feed->title(), updated_messages)); + } + + qDebug("Made progress in feed updates, total feeds count %d/%d (id of feed is %d).", m_feedsUpdated, m_feedsOriginalCount, feed->id()); + emit updateProgress(feed, m_feedsUpdated, m_feedsOriginalCount); + + if (m_feeds.isEmpty() && m_feedsUpdating <= 0) { + finalizeUpdate(); + } +} + +void FeedDownloader::finalizeUpdate() { + qDebug().nospace() << "Finished feed updates in thread: \'" << QThread::currentThreadId() << "\'."; + m_results.sort(); + // Update of feeds has finished. + // NOTE: This means that now "update lock" can be unlocked + // and feeds can be added/edited/deleted and application + // can eventually quit. + emit updateFinished(m_results); +} + +FeedDownloadResults::FeedDownloadResults() : m_updatedFeeds(QList>()) { +} + +QString FeedDownloadResults::overview(int how_many_feeds) const { + QStringList result; + + for (int i = 0, number_items_output = qMin(how_many_feeds, m_updatedFeeds.size()); i < number_items_output; i++) { + result.append(m_updatedFeeds.at(i).first + QSL(": ") + QString::number(m_updatedFeeds.at(i).second)); + } + + QString res_str = result.join(QSL("\n")); + + if (m_updatedFeeds.size() > how_many_feeds) { + res_str += QObject::tr("\n\n+ %n other feeds.", 0, m_updatedFeeds.size() - how_many_feeds); + } + + return res_str; +} + +void FeedDownloadResults::appendUpdatedFeed(const QPair& feed) { + m_updatedFeeds.append(feed); +} + +void FeedDownloadResults::sort() { + qSort(m_updatedFeeds.begin(), m_updatedFeeds.end(), FeedDownloadResults::lessThan); +} + +bool FeedDownloadResults::lessThan(const QPair& lhs, const QPair& rhs) { + return lhs.second > rhs.second; +} + +void FeedDownloadResults::clear() { + m_updatedFeeds.clear(); +} + +QList> FeedDownloadResults::updatedFeeds() const { + return m_updatedFeeds; +} diff --git a/src/core/feeddownloader.h b/src/core/feeddownloader.h index 9e821aec7..ddb1e64ee 100755 --- a/src/core/feeddownloader.h +++ b/src/core/feeddownloader.h @@ -1,104 +1,104 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FEEDDOWNLOADER_H -#define FEEDDOWNLOADER_H - -#include - -#include - -#include "core/message.h" - - -class Feed; -class QThreadPool; -class QMutex; - -// Represents results of batch feed updates. -class FeedDownloadResults { - public: - explicit FeedDownloadResults(); - - QList > updatedFeeds() const; - QString overview(int how_many_feeds) const; - - void appendUpdatedFeed(const QPair &feed); - void sort(); - void clear(); - - static bool lessThan(const QPair &lhs, const QPair &rhs); - - private: - // QString represents title if the feed, int represents count of newly downloaded messages. - QList > m_updatedFeeds; -}; - -// This class offers means to "update" feeds and "special" categories. -// NOTE: This class is used within separate thread. -class FeedDownloader : public QObject { - Q_OBJECT - - public: - // Constructors and destructors. - explicit FeedDownloader(QObject *parent = 0); - virtual ~FeedDownloader(); - - bool isUpdateRunning() const; - - public slots: - // Performs update of all feeds from the "feeds" parameter. - // New messages are downloaded for each feed and they - // are stored persistently in the database. - // Appropriate signals are emitted. - void updateFeeds(const QList &feeds); - - // Stops running update. - void stopRunningUpdate(); - - private slots: - void oneFeedUpdateFinished(const QList &messages, bool error_during_obtaining); - - signals: - // Emitted if feed updates started. - void updateStarted(); - - // Emitted if all items from update queue are - // processed. - void updateFinished(FeedDownloadResults updated_feeds); - - // Emitted if any item is processed. - // "Current" number indicates count of processed feeds - // and "total" number indicates total number of feeds - // which were in the initial queue. - void updateProgress(const Feed *feed, int current, int total); - - private: - void updateAvailableFeeds(); - void finalizeUpdate(); - - QList m_feeds; - QMutex *m_mutex; - QThreadPool *m_threadPool; - FeedDownloadResults m_results; - - int m_feedsUpdated; - int m_feedsUpdating; - int m_feedsOriginalCount; -}; - -#endif // FEEDDOWNLOADER_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FEEDDOWNLOADER_H +#define FEEDDOWNLOADER_H + +#include + +#include + +#include "core/message.h" + + +class Feed; +class QThreadPool; +class QMutex; + +// Represents results of batch feed updates. +class FeedDownloadResults { + public: + explicit FeedDownloadResults(); + + QList> updatedFeeds() const; + QString overview(int how_many_feeds) const; + + void appendUpdatedFeed(const QPair& feed); + void sort(); + void clear(); + + static bool lessThan(const QPair& lhs, const QPair& rhs); + + private: + // QString represents title if the feed, int represents count of newly downloaded messages. + QList> m_updatedFeeds; +}; + +// This class offers means to "update" feeds and "special" categories. +// NOTE: This class is used within separate thread. +class FeedDownloader : public QObject { + Q_OBJECT + + public: + // Constructors and destructors. + explicit FeedDownloader(QObject* parent = 0); + virtual ~FeedDownloader(); + + bool isUpdateRunning() const; + + public slots: + // Performs update of all feeds from the "feeds" parameter. + // New messages are downloaded for each feed and they + // are stored persistently in the database. + // Appropriate signals are emitted. + void updateFeeds(const QList& feeds); + + // Stops running update. + void stopRunningUpdate(); + + private slots: + void oneFeedUpdateFinished(const QList& messages, bool error_during_obtaining); + + signals: + // Emitted if feed updates started. + void updateStarted(); + + // Emitted if all items from update queue are + // processed. + void updateFinished(FeedDownloadResults updated_feeds); + + // Emitted if any item is processed. + // "Current" number indicates count of processed feeds + // and "total" number indicates total number of feeds + // which were in the initial queue. + void updateProgress(const Feed* feed, int current, int total); + + private: + void updateAvailableFeeds(); + void finalizeUpdate(); + + QList m_feeds; + QMutex* m_mutex; + QThreadPool* m_threadPool; + FeedDownloadResults m_results; + + int m_feedsUpdated; + int m_feedsUpdating; + int m_feedsOriginalCount; +}; + +#endif // FEEDDOWNLOADER_H diff --git a/src/core/feedsmodel.cpp b/src/core/feedsmodel.cpp index 2b408c96d..8131fdb9a 100755 --- a/src/core/feedsmodel.cpp +++ b/src/core/feedsmodel.cpp @@ -1,554 +1,544 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "core/feedsmodel.h" - -#include "definitions/definitions.h" -#include "services/abstract/feed.h" -#include "services/abstract/category.h" -#include "services/abstract/serviceroot.h" -#include "services/abstract/recyclebin.h" -#include "services/abstract/serviceentrypoint.h" -#include "services/standard/standardserviceroot.h" -#include "miscellaneous/textfactory.h" -#include "miscellaneous/databasefactory.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/feedreader.h" - -#include -#include -#include -#include -#include - -#include - - -FeedsModel::FeedsModel(QObject *parent) : QAbstractItemModel(parent) { - setObjectName(QSL("FeedsModel")); - - // Create root item. - m_rootItem = new RootItem(); - - //: Name of root item of feed list which can be seen in feed add/edit dialog. - m_rootItem->setTitle(tr("Root")); - m_rootItem->setIcon(qApp->icons()->fromTheme(QSL("folder"))); - - // Setup icons. - m_countsIcon = qApp->icons()->fromTheme(QSL("mail-mark-unread")); - - //: Title text in the feed list header. - m_headerData << tr("Title"); - - m_tooltipData << /*: Feed list header "titles" column tooltip.*/ tr("Titles of feeds/categories.") << - /*: Feed list header "counts" column tooltip.*/ tr("Counts of unread/all mesages."); -} - -FeedsModel::~FeedsModel() { - qDebug("Destroying FeedsModel instance."); - - // Delete all model items. - delete m_rootItem; -} - -QMimeData *FeedsModel::mimeData(const QModelIndexList &indexes) const { - QMimeData *mime_data = new QMimeData(); - QByteArray encoded_data; - QDataStream stream(&encoded_data, QIODevice::WriteOnly); - - foreach (const QModelIndex &index, indexes) { - if (index.column() != 0) { - continue; - } - - RootItem *item_for_index = itemForIndex(index); - - if (item_for_index->kind() != RootItemKind::Root) { - stream << (quintptr) item_for_index; - } - } - - mime_data->setData(QSL(MIME_TYPE_ITEM_POINTER), encoded_data); - return mime_data; -} - -QStringList FeedsModel::mimeTypes() const { - return QStringList() << QSL(MIME_TYPE_ITEM_POINTER); -} - -bool FeedsModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { - Q_UNUSED(row) - Q_UNUSED(column) - - if (action == Qt::IgnoreAction) { - return true; - } - else if (action != Qt::MoveAction) { - return false; - } - - QByteArray dragged_items_data = data->data(QSL(MIME_TYPE_ITEM_POINTER)); - - if (dragged_items_data.isEmpty()) { - return false; - } - else { - QDataStream stream(&dragged_items_data, QIODevice::ReadOnly); - - while (!stream.atEnd()) { - quintptr pointer_to_item; - stream >> pointer_to_item; - - // We have item we want to drag, we also determine the target item. - RootItem *dragged_item = (RootItem*) pointer_to_item; - RootItem *target_item = itemForIndex(parent); - ServiceRoot *dragged_item_root = dragged_item->getParentServiceRoot(); - ServiceRoot *target_item_root = target_item->getParentServiceRoot(); - - if (dragged_item == target_item || dragged_item->parent() == target_item) { - qDebug("Dragged item is equal to target item or its parent is equal to target item. Cancelling drag-drop action."); - return false; - } - - if (dragged_item_root != target_item_root) { - // Transferring of items between different accounts is not possible. - qApp->showGuiMessage(tr("Cannot perform drag & drop operation"), - tr("You can't transfer dragged item into different account, this is not supported."), - QSystemTrayIcon::Warning, - qApp->mainFormWidget(), - true); - - qDebug("Dragged item cannot be dragged into different account. Cancelling drag-drop action."); - return false; - } - - if (dragged_item->performDragDropChange(target_item)) { - // Drag & drop is supported by the dragged item and was - // completed on data level and in item hierarchy. - emit requireItemValidationAfterDragDrop(indexForItem(dragged_item)); - } - } - - return true; - } - - return false; -} - -Qt::DropActions FeedsModel::supportedDropActions() const { - return Qt::MoveAction; -} - -Qt::ItemFlags FeedsModel::flags(const QModelIndex &index) const { - Qt::ItemFlags base_flags = QAbstractItemModel::flags(index); - const RootItem *item_for_index = itemForIndex(index); - Qt::ItemFlags additional_flags = item_for_index->additionalFlags(); - - return base_flags | additional_flags; -} - -QVariant FeedsModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation != Qt::Horizontal) { - return QVariant(); - } - - switch (role) { - case Qt::DisplayRole: - if (section == FDS_MODEL_TITLE_INDEX) { - return m_headerData.at(FDS_MODEL_TITLE_INDEX); - } - else { - return QVariant(); - } - - case Qt::ToolTipRole: - return m_tooltipData.at(section); - - case Qt::DecorationRole: - if (section == FDS_MODEL_COUNTS_INDEX) { - return m_countsIcon; - } - else { - return QVariant(); - } - - default: - return QVariant(); - } -} - -QModelIndex FeedsModel::index(int row, int column, const QModelIndex &parent) const { - if (!hasIndex(row, column, parent)) { - return QModelIndex(); - } - - RootItem *parent_item = itemForIndex(parent); - RootItem *child_item = parent_item->child(row); - - if (child_item) { - return createIndex(row, column, child_item); - } - else { - return QModelIndex(); - } -} - -QModelIndex FeedsModel::parent(const QModelIndex &child) const { - if (!child.isValid()) { - return QModelIndex(); - } - - RootItem *child_item = itemForIndex(child); - RootItem *parent_item = child_item->parent(); - - if (parent_item == m_rootItem) { - return QModelIndex(); - } - else { - return createIndex(parent_item->row(), 0, parent_item); - } -} - -int FeedsModel::rowCount(const QModelIndex &parent) const { - if (parent.column() > 0) { - return 0; - } - else { - return itemForIndex(parent)->childCount(); - } -} - -int FeedsModel::countOfAllMessages() const { - return m_rootItem->countOfAllMessages(); -} - -int FeedsModel::countOfUnreadMessages() const { - return m_rootItem->countOfUnreadMessages(); -} - -void FeedsModel::reloadCountsOfWholeModel() { - m_rootItem->updateCounts(true); - reloadWholeLayout(); - notifyWithCounts(); -} - -void FeedsModel::removeItem(const QModelIndex &index) { - if (index.isValid()) { - RootItem *deleting_item = itemForIndex(index); - QModelIndex parent_index = index.parent(); - RootItem *parent_item = deleting_item->parent(); - - beginRemoveRows(parent_index, index.row(), index.row()); - parent_item->removeChild(deleting_item); - endRemoveRows(); - - deleting_item->deleteLater(); - notifyWithCounts(); - } -} - -void FeedsModel::removeItem(RootItem *deleting_item) { - if (deleting_item != nullptr) { - QModelIndex index = indexForItem(deleting_item); - QModelIndex parent_index = index.parent(); - RootItem *parent_item = deleting_item->parent(); - - beginRemoveRows(parent_index, index.row(), index.row()); - parent_item->removeChild(deleting_item); - endRemoveRows(); - - deleting_item->deleteLater(); - notifyWithCounts(); - } -} - -void FeedsModel::reassignNodeToNewParent(RootItem *original_node, RootItem *new_parent) { - RootItem *original_parent = original_node->parent(); - - if (original_parent != new_parent) { - if (original_parent != nullptr) { - int original_index_of_item = original_parent->childItems().indexOf(original_node); - - if (original_index_of_item >= 0) { - // Remove the original item from the model... - beginRemoveRows(indexForItem(original_parent), original_index_of_item, original_index_of_item); - original_parent->removeChild(original_node); - endRemoveRows(); - } - } - - int new_index_of_item = new_parent->childCount(); - - // ... and insert it under the new parent. - beginInsertRows(indexForItem(new_parent), new_index_of_item, new_index_of_item); - new_parent->appendChild(original_node); - endInsertRows(); - } -} - -QList FeedsModel::serviceRoots() const { - QList roots; - - foreach (RootItem *root, m_rootItem->childItems()) { - if (root->kind() == RootItemKind::ServiceRoot) { - roots.append(root->toServiceRoot()); - } - } - - return roots; -} - -bool FeedsModel::containsServiceRootFromEntryPoint(const ServiceEntryPoint *point) const { - foreach (const ServiceRoot *root, serviceRoots()) { - if (root->code() == point->code()) { - return true; - } - } - - return false; -} - -StandardServiceRoot *FeedsModel::standardServiceRoot() const { - foreach (ServiceRoot *root, serviceRoots()) { - StandardServiceRoot *std_service_root; - - if ((std_service_root = dynamic_cast(root)) != nullptr) { - return std_service_root; - } - } - - return nullptr; -} - -QList FeedsModel::feedsForScheduledUpdate(bool auto_update_now) { - QList feeds_for_update; - - foreach (Feed *feed, m_rootItem->getSubTreeFeeds()) { - switch (feed->autoUpdateType()) { - case Feed::DontAutoUpdate: - // Do not auto-update this feed ever. - continue; - - case Feed::DefaultAutoUpdate: - if (auto_update_now) { - feeds_for_update.append(feed); - } - - break; - - case Feed::SpecificAutoUpdate: - default: - int remaining_interval = feed->autoUpdateRemainingInterval(); - - if (--remaining_interval <= 0) { - // Interval of this feed passed, include this feed in the output list - // and reset the interval. - feeds_for_update.append(feed); - feed->setAutoUpdateRemainingInterval(feed->autoUpdateInitialInterval()); - } - else { - // Interval did not pass, set new decremented interval and do NOT - // include this feed in the output list. - feed->setAutoUpdateRemainingInterval(remaining_interval); - } - - break; - } - } - - return feeds_for_update; -} - -QList FeedsModel::messagesForItem(RootItem *item) const { - return item->undeletedMessages(); -} - -int FeedsModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent) - - return FEEDS_VIEW_COLUMN_COUNT; -} - -RootItem *FeedsModel::itemForIndex(const QModelIndex &index) const { - if (index.isValid() && index.model() == this) { - return static_cast(index.internalPointer()); - } - else { - return m_rootItem; - } -} - -QModelIndex FeedsModel::indexForItem(const RootItem *item) const { - if (item == nullptr || item->kind() == RootItemKind::Root) { - // Root item lies on invalid index. - return QModelIndex(); - } - - QStack chain; - - while (item->kind() != RootItemKind::Root) { - chain.push(item); - item = item->parent(); - } - - // Now, we have complete chain list: parent --- ..... --- parent --- leaf (item). - QModelIndex target_index = indexForItem(m_rootItem); - - // We go through the stack and create our target index. - while (!chain.isEmpty()) { - const RootItem *parent_item = chain.pop(); - target_index = index(parent_item->parent()->childItems().indexOf(const_cast(parent_item)), 0, target_index); - } - - return target_index; -} - -bool FeedsModel::hasAnyFeedNewMessages() const { - foreach (const Feed *feed, m_rootItem->getSubTreeFeeds()) { - if (feed->status() == Feed::NewMessages) { - return true; - } - } - - return false; -} - -RootItem *FeedsModel::rootItem() const { - return m_rootItem; -} - -void FeedsModel::reloadChangedLayout(QModelIndexList list) { - while (!list.isEmpty()) { - QModelIndex indx = list.takeFirst(); - - if (indx.isValid()) { - QModelIndex indx_parent = indx.parent(); - - // Underlying data are changed. - emit dataChanged(index(indx.row(), 0, indx_parent), index(indx.row(), FDS_MODEL_COUNTS_INDEX, indx_parent)); - - list.append(indx_parent); - } - } -} - -void FeedsModel::reloadChangedItem(RootItem *item) { - QModelIndex index_item = indexForItem(item); - reloadChangedLayout(QModelIndexList() << index_item); -} - -void FeedsModel::notifyWithCounts() { - emit messageCountsChanged(countOfUnreadMessages(), hasAnyFeedNewMessages()); -} - -void FeedsModel::onItemDataChanged(const QList &items) { - if (items.size() > RELOAD_MODEL_BORDER_NUM) { - qDebug("There is request to reload feed model for more than %d items, reloading model fully.", RELOAD_MODEL_BORDER_NUM); - reloadWholeLayout(); - } - else { - qDebug("There is request to reload feed model, reloading the %d items individually.", items.size()); - - foreach (RootItem *item, items) { - reloadChangedItem(item); - } - } - - notifyWithCounts(); -} - -void FeedsModel::reloadWholeLayout() { - emit layoutAboutToBeChanged(); - emit layoutChanged(); -} - -bool FeedsModel::addServiceAccount(ServiceRoot *root, bool freshly_activated) { - int new_row_index = m_rootItem->childCount(); - - beginInsertRows(indexForItem(m_rootItem), new_row_index, new_row_index); - m_rootItem->appendChild(root); - endInsertRows(); - - // Connect. - connect(root, &ServiceRoot::itemRemovalRequested, this, static_cast(&FeedsModel::removeItem)); - connect(root, &ServiceRoot::itemReassignmentRequested, this, &FeedsModel::reassignNodeToNewParent); - connect(root, &ServiceRoot::dataChanged, this, &FeedsModel::onItemDataChanged); - connect(root, &ServiceRoot::reloadMessageListRequested, this, &FeedsModel::reloadMessageListRequested); - connect(root, &ServiceRoot::itemExpandRequested, this, &FeedsModel::itemExpandRequested); - connect(root, &ServiceRoot::itemExpandStateSaveRequested, this, &FeedsModel::itemExpandStateSaveRequested); - - root->start(freshly_activated); - return true; -} - -bool FeedsModel::restoreAllBins() { - bool result = true; - - foreach (ServiceRoot *root, serviceRoots()) { - RecycleBin *bin_of_root = root->recycleBin(); - - if (bin_of_root != nullptr) { - result &= bin_of_root->restore(); - } - } - - return result; -} - -bool FeedsModel::emptyAllBins() { - bool result = true; - - foreach (ServiceRoot *root, serviceRoots()) { - RecycleBin *bin_of_root = root->recycleBin(); - - if (bin_of_root != nullptr) { - result &= bin_of_root->empty(); - } - } - - return result; -} - -void FeedsModel::loadActivatedServiceAccounts() { - // Iterate all globally available feed "service plugins". - foreach (const ServiceEntryPoint *entry_point, qApp->feedReader()->feedServices()) { - // Load all stored root nodes from the entry point and add those to the model. - QList roots = entry_point->initializeSubtree(); - - foreach (ServiceRoot *root, roots) { - addServiceAccount(root, false); - } - } -} - -void FeedsModel::stopServiceAccounts() { - foreach (ServiceRoot *account, serviceRoots()) { - account->stop(); - } -} - -QList FeedsModel::feedsForIndex(const QModelIndex &index) const { - return itemForIndex(index)->getSubTreeFeeds(); -} - -bool FeedsModel::markItemRead(RootItem *item, RootItem::ReadStatus read) { - return item->markAsReadUnread(read); -} - -bool FeedsModel::markItemCleared(RootItem *item, bool clean_read_only) { - return item->cleanMessages(clean_read_only); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "core/feedsmodel.h" + +#include "definitions/definitions.h" +#include "services/abstract/feed.h" +#include "services/abstract/category.h" +#include "services/abstract/serviceroot.h" +#include "services/abstract/recyclebin.h" +#include "services/abstract/serviceentrypoint.h" +#include "services/standard/standardserviceroot.h" +#include "miscellaneous/textfactory.h" +#include "miscellaneous/databasefactory.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/feedreader.h" + +#include +#include +#include +#include +#include + +#include + + +FeedsModel::FeedsModel(QObject* parent) : QAbstractItemModel(parent) { + setObjectName(QSL("FeedsModel")); + // Create root item. + m_rootItem = new RootItem(); + //: Name of root item of feed list which can be seen in feed add/edit dialog. + m_rootItem->setTitle(tr("Root")); + m_rootItem->setIcon(qApp->icons()->fromTheme(QSL("folder"))); + // Setup icons. + m_countsIcon = qApp->icons()->fromTheme(QSL("mail-mark-unread")); + //: Title text in the feed list header. + m_headerData << tr("Title"); + m_tooltipData << /*: Feed list header "titles" column tooltip.*/ tr("Titles of feeds/categories.") << + /*: Feed list header "counts" column tooltip.*/ tr("Counts of unread/all mesages."); +} + +FeedsModel::~FeedsModel() { + qDebug("Destroying FeedsModel instance."); + // Delete all model items. + delete m_rootItem; +} + +QMimeData* FeedsModel::mimeData(const QModelIndexList& indexes) const { + QMimeData* mime_data = new QMimeData(); + QByteArray encoded_data; + QDataStream stream(&encoded_data, QIODevice::WriteOnly); + + foreach (const QModelIndex& index, indexes) { + if (index.column() != 0) { + continue; + } + + RootItem* item_for_index = itemForIndex(index); + + if (item_for_index->kind() != RootItemKind::Root) { + stream << (quintptr) item_for_index; + } + } + + mime_data->setData(QSL(MIME_TYPE_ITEM_POINTER), encoded_data); + return mime_data; +} + +QStringList FeedsModel::mimeTypes() const { + return QStringList() << QSL(MIME_TYPE_ITEM_POINTER); +} + +bool FeedsModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) { + Q_UNUSED(row) + Q_UNUSED(column) + + if (action == Qt::IgnoreAction) { + return true; + } + + else if (action != Qt::MoveAction) { + return false; + } + + QByteArray dragged_items_data = data->data(QSL(MIME_TYPE_ITEM_POINTER)); + + if (dragged_items_data.isEmpty()) { + return false; + } + + else { + QDataStream stream(&dragged_items_data, QIODevice::ReadOnly); + + while (!stream.atEnd()) { + quintptr pointer_to_item; + stream >> pointer_to_item; + // We have item we want to drag, we also determine the target item. + RootItem* dragged_item = (RootItem*) pointer_to_item; + RootItem* target_item = itemForIndex(parent); + ServiceRoot* dragged_item_root = dragged_item->getParentServiceRoot(); + ServiceRoot* target_item_root = target_item->getParentServiceRoot(); + + if (dragged_item == target_item || dragged_item->parent() == target_item) { + qDebug("Dragged item is equal to target item or its parent is equal to target item. Cancelling drag-drop action."); + return false; + } + + if (dragged_item_root != target_item_root) { + // Transferring of items between different accounts is not possible. + qApp->showGuiMessage(tr("Cannot perform drag & drop operation"), + tr("You can't transfer dragged item into different account, this is not supported."), + QSystemTrayIcon::Warning, + qApp->mainFormWidget(), + true); + qDebug("Dragged item cannot be dragged into different account. Cancelling drag-drop action."); + return false; + } + + if (dragged_item->performDragDropChange(target_item)) { + // Drag & drop is supported by the dragged item and was + // completed on data level and in item hierarchy. + emit requireItemValidationAfterDragDrop(indexForItem(dragged_item)); + } + } + + return true; + } + + return false; +} + +Qt::DropActions FeedsModel::supportedDropActions() const { + return Qt::MoveAction; +} + +Qt::ItemFlags FeedsModel::flags(const QModelIndex& index) const { + Qt::ItemFlags base_flags = QAbstractItemModel::flags(index); + const RootItem* item_for_index = itemForIndex(index); + Qt::ItemFlags additional_flags = item_for_index->additionalFlags(); + return base_flags | additional_flags; +} + +QVariant FeedsModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (orientation != Qt::Horizontal) { + return QVariant(); + } + + switch (role) { + case Qt::DisplayRole: + if (section == FDS_MODEL_TITLE_INDEX) { + return m_headerData.at(FDS_MODEL_TITLE_INDEX); + } + + else { + return QVariant(); + } + + case Qt::ToolTipRole: + return m_tooltipData.at(section); + + case Qt::DecorationRole: + if (section == FDS_MODEL_COUNTS_INDEX) { + return m_countsIcon; + } + + else { + return QVariant(); + } + + default: + return QVariant(); + } +} + +QModelIndex FeedsModel::index(int row, int column, const QModelIndex& parent) const { + if (!hasIndex(row, column, parent)) { + return QModelIndex(); + } + + RootItem* parent_item = itemForIndex(parent); + RootItem* child_item = parent_item->child(row); + + if (child_item) { + return createIndex(row, column, child_item); + } + + else { + return QModelIndex(); + } +} + +QModelIndex FeedsModel::parent(const QModelIndex& child) const { + if (!child.isValid()) { + return QModelIndex(); + } + + RootItem* child_item = itemForIndex(child); + RootItem* parent_item = child_item->parent(); + + if (parent_item == m_rootItem) { + return QModelIndex(); + } + + else { + return createIndex(parent_item->row(), 0, parent_item); + } +} + +int FeedsModel::rowCount(const QModelIndex& parent) const { + if (parent.column() > 0) { + return 0; + } + + else { + return itemForIndex(parent)->childCount(); + } +} + +int FeedsModel::countOfAllMessages() const { + return m_rootItem->countOfAllMessages(); +} + +int FeedsModel::countOfUnreadMessages() const { + return m_rootItem->countOfUnreadMessages(); +} + +void FeedsModel::reloadCountsOfWholeModel() { + m_rootItem->updateCounts(true); + reloadWholeLayout(); + notifyWithCounts(); +} + +void FeedsModel::removeItem(const QModelIndex& index) { + if (index.isValid()) { + RootItem* deleting_item = itemForIndex(index); + QModelIndex parent_index = index.parent(); + RootItem* parent_item = deleting_item->parent(); + beginRemoveRows(parent_index, index.row(), index.row()); + parent_item->removeChild(deleting_item); + endRemoveRows(); + deleting_item->deleteLater(); + notifyWithCounts(); + } +} + +void FeedsModel::removeItem(RootItem* deleting_item) { + if (deleting_item != nullptr) { + QModelIndex index = indexForItem(deleting_item); + QModelIndex parent_index = index.parent(); + RootItem* parent_item = deleting_item->parent(); + beginRemoveRows(parent_index, index.row(), index.row()); + parent_item->removeChild(deleting_item); + endRemoveRows(); + deleting_item->deleteLater(); + notifyWithCounts(); + } +} + +void FeedsModel::reassignNodeToNewParent(RootItem* original_node, RootItem* new_parent) { + RootItem* original_parent = original_node->parent(); + + if (original_parent != new_parent) { + if (original_parent != nullptr) { + int original_index_of_item = original_parent->childItems().indexOf(original_node); + + if (original_index_of_item >= 0) { + // Remove the original item from the model... + beginRemoveRows(indexForItem(original_parent), original_index_of_item, original_index_of_item); + original_parent->removeChild(original_node); + endRemoveRows(); + } + } + + int new_index_of_item = new_parent->childCount(); + // ... and insert it under the new parent. + beginInsertRows(indexForItem(new_parent), new_index_of_item, new_index_of_item); + new_parent->appendChild(original_node); + endInsertRows(); + } +} + +QList FeedsModel::serviceRoots() const { + QList roots; + + foreach (RootItem* root, m_rootItem->childItems()) { + if (root->kind() == RootItemKind::ServiceRoot) { + roots.append(root->toServiceRoot()); + } + } + + return roots; +} + +bool FeedsModel::containsServiceRootFromEntryPoint(const ServiceEntryPoint* point) const { + foreach (const ServiceRoot* root, serviceRoots()) { + if (root->code() == point->code()) { + return true; + } + } + + return false; +} + +StandardServiceRoot* FeedsModel::standardServiceRoot() const { + foreach (ServiceRoot* root, serviceRoots()) { + StandardServiceRoot* std_service_root; + + if ((std_service_root = dynamic_cast(root)) != nullptr) { + return std_service_root; + } + } + + return nullptr; +} + +QList FeedsModel::feedsForScheduledUpdate(bool auto_update_now) { + QList feeds_for_update; + + foreach (Feed* feed, m_rootItem->getSubTreeFeeds()) { + switch (feed->autoUpdateType()) { + case Feed::DontAutoUpdate: + // Do not auto-update this feed ever. + continue; + + case Feed::DefaultAutoUpdate: + if (auto_update_now) { + feeds_for_update.append(feed); + } + + break; + + case Feed::SpecificAutoUpdate: + default: + int remaining_interval = feed->autoUpdateRemainingInterval(); + + if (--remaining_interval <= 0) { + // Interval of this feed passed, include this feed in the output list + // and reset the interval. + feeds_for_update.append(feed); + feed->setAutoUpdateRemainingInterval(feed->autoUpdateInitialInterval()); + } + + else { + // Interval did not pass, set new decremented interval and do NOT + // include this feed in the output list. + feed->setAutoUpdateRemainingInterval(remaining_interval); + } + + break; + } + } + + return feeds_for_update; +} + +QList FeedsModel::messagesForItem(RootItem* item) const { + return item->undeletedMessages(); +} + +int FeedsModel::columnCount(const QModelIndex& parent) const { + Q_UNUSED(parent) + return FEEDS_VIEW_COLUMN_COUNT; +} + +RootItem* FeedsModel::itemForIndex(const QModelIndex& index) const { + if (index.isValid() && index.model() == this) { + return static_cast(index.internalPointer()); + } + + else { + return m_rootItem; + } +} + +QModelIndex FeedsModel::indexForItem(const RootItem* item) const { + if (item == nullptr || item->kind() == RootItemKind::Root) { + // Root item lies on invalid index. + return QModelIndex(); + } + + QStack chain; + + while (item->kind() != RootItemKind::Root) { + chain.push(item); + item = item->parent(); + } + + // Now, we have complete chain list: parent --- ..... --- parent --- leaf (item). + QModelIndex target_index = indexForItem(m_rootItem); + + // We go through the stack and create our target index. + while (!chain.isEmpty()) { + const RootItem* parent_item = chain.pop(); + target_index = index(parent_item->parent()->childItems().indexOf(const_cast(parent_item)), 0, target_index); + } + + return target_index; +} + +bool FeedsModel::hasAnyFeedNewMessages() const { + foreach (const Feed* feed, m_rootItem->getSubTreeFeeds()) { + if (feed->status() == Feed::NewMessages) { + return true; + } + } + + return false; +} + +RootItem* FeedsModel::rootItem() const { + return m_rootItem; +} + +void FeedsModel::reloadChangedLayout(QModelIndexList list) { + while (!list.isEmpty()) { + QModelIndex indx = list.takeFirst(); + + if (indx.isValid()) { + QModelIndex indx_parent = indx.parent(); + // Underlying data are changed. + emit dataChanged(index(indx.row(), 0, indx_parent), index(indx.row(), FDS_MODEL_COUNTS_INDEX, indx_parent)); + list.append(indx_parent); + } + } +} + +void FeedsModel::reloadChangedItem(RootItem* item) { + QModelIndex index_item = indexForItem(item); + reloadChangedLayout(QModelIndexList() << index_item); +} + +void FeedsModel::notifyWithCounts() { + emit messageCountsChanged(countOfUnreadMessages(), hasAnyFeedNewMessages()); +} + +void FeedsModel::onItemDataChanged(const QList& items) { + if (items.size() > RELOAD_MODEL_BORDER_NUM) { + qDebug("There is request to reload feed model for more than %d items, reloading model fully.", RELOAD_MODEL_BORDER_NUM); + reloadWholeLayout(); + } + + else { + qDebug("There is request to reload feed model, reloading the %d items individually.", items.size()); + + foreach (RootItem* item, items) { + reloadChangedItem(item); + } + } + + notifyWithCounts(); +} + +void FeedsModel::reloadWholeLayout() { + emit layoutAboutToBeChanged(); + emit layoutChanged(); +} + +bool FeedsModel::addServiceAccount(ServiceRoot* root, bool freshly_activated) { + int new_row_index = m_rootItem->childCount(); + beginInsertRows(indexForItem(m_rootItem), new_row_index, new_row_index); + m_rootItem->appendChild(root); + endInsertRows(); + // Connect. + connect(root, &ServiceRoot::itemRemovalRequested, this, static_cast(&FeedsModel::removeItem)); + connect(root, &ServiceRoot::itemReassignmentRequested, this, &FeedsModel::reassignNodeToNewParent); + connect(root, &ServiceRoot::dataChanged, this, &FeedsModel::onItemDataChanged); + connect(root, &ServiceRoot::reloadMessageListRequested, this, &FeedsModel::reloadMessageListRequested); + connect(root, &ServiceRoot::itemExpandRequested, this, &FeedsModel::itemExpandRequested); + connect(root, &ServiceRoot::itemExpandStateSaveRequested, this, &FeedsModel::itemExpandStateSaveRequested); + root->start(freshly_activated); + return true; +} + +bool FeedsModel::restoreAllBins() { + bool result = true; + + foreach (ServiceRoot* root, serviceRoots()) { + RecycleBin* bin_of_root = root->recycleBin(); + + if (bin_of_root != nullptr) { + result &= bin_of_root->restore(); + } + } + + return result; +} + +bool FeedsModel::emptyAllBins() { + bool result = true; + + foreach (ServiceRoot* root, serviceRoots()) { + RecycleBin* bin_of_root = root->recycleBin(); + + if (bin_of_root != nullptr) { + result &= bin_of_root->empty(); + } + } + + return result; +} + +void FeedsModel::loadActivatedServiceAccounts() { + // Iterate all globally available feed "service plugins". + foreach (const ServiceEntryPoint* entry_point, qApp->feedReader()->feedServices()) { + // Load all stored root nodes from the entry point and add those to the model. + QList roots = entry_point->initializeSubtree(); + + foreach (ServiceRoot* root, roots) { + addServiceAccount(root, false); + } + } +} + +void FeedsModel::stopServiceAccounts() { + foreach (ServiceRoot* account, serviceRoots()) { + account->stop(); + } +} + +QList FeedsModel::feedsForIndex(const QModelIndex& index) const { + return itemForIndex(index)->getSubTreeFeeds(); +} + +bool FeedsModel::markItemRead(RootItem* item, RootItem::ReadStatus read) { + return item->markAsReadUnread(read); +} + +bool FeedsModel::markItemCleared(RootItem* item, bool clean_read_only) { + return item->cleanMessages(clean_read_only); +} diff --git a/src/core/feedsmodel.h b/src/core/feedsmodel.h index 3033ed2a4..335643b1d 100755 --- a/src/core/feedsmodel.h +++ b/src/core/feedsmodel.h @@ -1,182 +1,182 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FEEDSMODEL_H -#define FEEDSMODEL_H - -#include - -#include "core/message.h" -#include "services/abstract/rootitem.h" - -class Category; -class Feed; -class ServiceRoot; -class ServiceEntryPoint; -class StandardServiceRoot; - -class FeedsModel : public QAbstractItemModel { - Q_OBJECT - - public: - // Constructors and destructors. - explicit FeedsModel(QObject *parent = 0); - virtual ~FeedsModel(); - - // Model implementation. - inline QVariant data(const QModelIndex &index, int role) const { - // Return data according to item. - return itemForIndex(index)->data(index.column(), role); - } - - // Drag & drop. - QMimeData *mimeData(const QModelIndexList &indexes) const; - QStringList mimeTypes() const; - bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); - Qt::DropActions supportedDropActions() const; - Qt::ItemFlags flags(const QModelIndex &index) const; - - // Other subclassed methods. - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QModelIndex index(int row, int column, const QModelIndex &parent) const; - QModelIndex parent(const QModelIndex &child) const; - int columnCount(const QModelIndex &parent) const; - int rowCount(const QModelIndex &parent) const; - - // Returns counts of ALL/UNREAD (non-deleted) messages for the model. - int countOfAllMessages() const; - int countOfUnreadMessages() const; - - // Returns all activated service roots. - // NOTE: Service root nodes are lying directly UNDER - // the model root item. - QList serviceRoots() const; - - // Determines if there is any account activated from given entry point. - bool containsServiceRootFromEntryPoint(const ServiceEntryPoint *point) const; - - // Direct and the only global accessor to standard service root. - StandardServiceRoot *standardServiceRoot() const; - - // Returns the list of feeds which should be updated - // according to auto-update schedule. - // Variable "auto_update_now" is true, when global timeout - // for scheduled auto-update was met and global auto-update strategy is enabled - // so feeds with "default" auto-update strategy should be updated. - // - // This method might change some properties of some feeds. - QList feedsForScheduledUpdate(bool auto_update_now); - - // Returns (undeleted) messages for given feeds. - // This is usually used for displaying whole feeds - // in "newspaper" mode. - QList messagesForItem(RootItem *item) const; - - // Returns ALL RECURSIVE CHILD feeds contained within single index. - QList feedsForIndex(const QModelIndex &index) const; - - // Returns feed/category which lies at the specified index or - // root item if index is invalid. - RootItem *itemForIndex(const QModelIndex &index) const; - - // Returns source QModelIndex on which lies given item. - // NOTE: This goes through all available indexes and - // checks their bound items manually, there is no - // other way to to this. - QModelIndex indexForItem(const RootItem *item) const; - - // Determines if any feed has any new messages. - bool hasAnyFeedNewMessages() const; - - // Access to root item. - RootItem *rootItem() const; - - public slots: - // Loads feed/categories from the database. - void loadActivatedServiceAccounts(); - - // Stops all accounts before exit. - void stopServiceAccounts(); - - // Reloads counts of all feeds/categories/whatever in the model. - void reloadCountsOfWholeModel(); - - // Checks if new parent node is different from one used by original node. - // If it is, then it reassigns original_node to new parent. - void reassignNodeToNewParent(RootItem *original_node, RootItem *new_parent); - - // Adds given service root account. - bool addServiceAccount(ServiceRoot *root, bool freshly_activated); - - // Removes item with given index. - // NOTE: Also deletes item from memory. - void removeItem(const QModelIndex &index); - void removeItem(RootItem *deleting_item); - - // Recycle bins operations. - bool restoreAllBins(); - bool emptyAllBins(); - - // Feeds operations. - bool markItemRead(RootItem *item, RootItem::ReadStatus read); - bool markItemCleared(RootItem *item, bool clean_read_only); - - // Signals that properties (probably counts) - // of ALL items have changed. - void reloadWholeLayout(); - - // Signals that SOME data of this model need - // to be reloaded by ALL attached views. - // NOTE: This reloads all parent valid indexes too. - void reloadChangedLayout(QModelIndexList list); - - // Invalidates data under index for the item. - void reloadChangedItem(RootItem *item); - - // Notifies other components about messages - // counts. - void notifyWithCounts(); - - private slots: - void onItemDataChanged(const QList &items); - - signals: - // Emitted if counts of messages are changed. - void messageCountsChanged(int unread_messages, bool any_feed_has_unread_messages); - - // Emitted if any item requested that any view should expand it. - void itemExpandRequested(QList items, bool expand); - - // Emitted if any item requested that its expand states should be explicitly saved. - // NOTE: Normally expand states are saved when application quits. - void itemExpandStateSaveRequested(RootItem *subtree_root); - - // Emitted when there is a need of reloading of displayed messages. - void reloadMessageListRequested(bool mark_selected_messages_read); - - // There was some drag/drop operation, notify view about this. - // NOTE: View will probably expand dropped index. - void requireItemValidationAfterDragDrop(const QModelIndex &source_index); - - private: - RootItem *m_rootItem; - QList m_headerData; - QList m_tooltipData; - QIcon m_countsIcon; -}; - -#endif // FEEDSMODEL_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FEEDSMODEL_H +#define FEEDSMODEL_H + +#include + +#include "core/message.h" +#include "services/abstract/rootitem.h" + +class Category; +class Feed; +class ServiceRoot; +class ServiceEntryPoint; +class StandardServiceRoot; + +class FeedsModel : public QAbstractItemModel { + Q_OBJECT + + public: + // Constructors and destructors. + explicit FeedsModel(QObject* parent = 0); + virtual ~FeedsModel(); + + // Model implementation. + inline QVariant data(const QModelIndex& index, int role) const { + // Return data according to item. + return itemForIndex(index)->data(index.column(), role); + } + + // Drag & drop. + QMimeData* mimeData(const QModelIndexList& indexes) const; + QStringList mimeTypes() const; + bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); + Qt::DropActions supportedDropActions() const; + Qt::ItemFlags flags(const QModelIndex& index) const; + + // Other subclassed methods. + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QModelIndex index(int row, int column, const QModelIndex& parent) const; + QModelIndex parent(const QModelIndex& child) const; + int columnCount(const QModelIndex& parent) const; + int rowCount(const QModelIndex& parent) const; + + // Returns counts of ALL/UNREAD (non-deleted) messages for the model. + int countOfAllMessages() const; + int countOfUnreadMessages() const; + + // Returns all activated service roots. + // NOTE: Service root nodes are lying directly UNDER + // the model root item. + QList serviceRoots() const; + + // Determines if there is any account activated from given entry point. + bool containsServiceRootFromEntryPoint(const ServiceEntryPoint* point) const; + + // Direct and the only global accessor to standard service root. + StandardServiceRoot* standardServiceRoot() const; + + // Returns the list of feeds which should be updated + // according to auto-update schedule. + // Variable "auto_update_now" is true, when global timeout + // for scheduled auto-update was met and global auto-update strategy is enabled + // so feeds with "default" auto-update strategy should be updated. + // + // This method might change some properties of some feeds. + QList feedsForScheduledUpdate(bool auto_update_now); + + // Returns (undeleted) messages for given feeds. + // This is usually used for displaying whole feeds + // in "newspaper" mode. + QList messagesForItem(RootItem* item) const; + + // Returns ALL RECURSIVE CHILD feeds contained within single index. + QList feedsForIndex(const QModelIndex& index) const; + + // Returns feed/category which lies at the specified index or + // root item if index is invalid. + RootItem* itemForIndex(const QModelIndex& index) const; + + // Returns source QModelIndex on which lies given item. + // NOTE: This goes through all available indexes and + // checks their bound items manually, there is no + // other way to to this. + QModelIndex indexForItem(const RootItem* item) const; + + // Determines if any feed has any new messages. + bool hasAnyFeedNewMessages() const; + + // Access to root item. + RootItem* rootItem() const; + + public slots: + // Loads feed/categories from the database. + void loadActivatedServiceAccounts(); + + // Stops all accounts before exit. + void stopServiceAccounts(); + + // Reloads counts of all feeds/categories/whatever in the model. + void reloadCountsOfWholeModel(); + + // Checks if new parent node is different from one used by original node. + // If it is, then it reassigns original_node to new parent. + void reassignNodeToNewParent(RootItem* original_node, RootItem* new_parent); + + // Adds given service root account. + bool addServiceAccount(ServiceRoot* root, bool freshly_activated); + + // Removes item with given index. + // NOTE: Also deletes item from memory. + void removeItem(const QModelIndex& index); + void removeItem(RootItem* deleting_item); + + // Recycle bins operations. + bool restoreAllBins(); + bool emptyAllBins(); + + // Feeds operations. + bool markItemRead(RootItem* item, RootItem::ReadStatus read); + bool markItemCleared(RootItem* item, bool clean_read_only); + + // Signals that properties (probably counts) + // of ALL items have changed. + void reloadWholeLayout(); + + // Signals that SOME data of this model need + // to be reloaded by ALL attached views. + // NOTE: This reloads all parent valid indexes too. + void reloadChangedLayout(QModelIndexList list); + + // Invalidates data under index for the item. + void reloadChangedItem(RootItem* item); + + // Notifies other components about messages + // counts. + void notifyWithCounts(); + + private slots: + void onItemDataChanged(const QList& items); + + signals: + // Emitted if counts of messages are changed. + void messageCountsChanged(int unread_messages, bool any_feed_has_unread_messages); + + // Emitted if any item requested that any view should expand it. + void itemExpandRequested(QList items, bool expand); + + // Emitted if any item requested that its expand states should be explicitly saved. + // NOTE: Normally expand states are saved when application quits. + void itemExpandStateSaveRequested(RootItem* subtree_root); + + // Emitted when there is a need of reloading of displayed messages. + void reloadMessageListRequested(bool mark_selected_messages_read); + + // There was some drag/drop operation, notify view about this. + // NOTE: View will probably expand dropped index. + void requireItemValidationAfterDragDrop(const QModelIndex& source_index); + + private: + RootItem* m_rootItem; + QList m_headerData; + QList m_tooltipData; + QIcon m_countsIcon; +}; + +#endif // FEEDSMODEL_H diff --git a/src/core/feedsproxymodel.cpp b/src/core/feedsproxymodel.cpp index 7e77f982b..16de9e1c6 100755 --- a/src/core/feedsproxymodel.cpp +++ b/src/core/feedsproxymodel.cpp @@ -1,264 +1,278 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "core/feedsproxymodel.h" - -#include "definitions/definitions.h" -#include "miscellaneous/application.h" -#include "core/feedsmodel.h" -#include "services/abstract/rootitem.h" - -#include - - -FeedsProxyModel::FeedsProxyModel(FeedsModel *source_model, QObject *parent) - : QSortFilterProxyModel(parent), m_sourceModel(source_model), m_selectedItem(nullptr), - m_showUnreadOnly(false), m_hiddenIndices(QList >()) { - setObjectName(QSL("FeedsProxyModel")); - setSortRole(Qt::EditRole); - setSortCaseSensitivity(Qt::CaseInsensitive); - setFilterCaseSensitivity(Qt::CaseInsensitive); - setFilterKeyColumn(-1); - setFilterRole(Qt::EditRole); - setDynamicSortFilter(true); - setSourceModel(m_sourceModel); -} - -FeedsProxyModel::~FeedsProxyModel() { - qDebug("Destroying FeedsProxyModel instance"); -} - -QModelIndexList FeedsProxyModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const { - QModelIndexList result; - const uint match_type = flags & 0x0F; - const Qt::CaseSensitivity cs = Qt::CaseInsensitive; - const bool recurse = flags & Qt::MatchRecursive; - const bool wrap = flags & Qt::MatchWrap; - const bool all_hits = (hits == -1); - QString entered_text; - const QModelIndex p = parent(start); - int from = start.row(); - int to = rowCount(p); - - for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i) { - for (int r = from; (r < to) && (all_hits || result.count() < hits); ++r) { - QModelIndex idx = index(r, start.column(), p); - - if (!idx.isValid()) { - continue; - } - - QModelIndex mapped_idx = mapToSource(idx); - QVariant item_value = m_sourceModel->itemForIndex(mapped_idx)->title(); - - // QVariant based matching. - if (match_type == Qt::MatchExactly) { - if (value == item_value) { - result.append(idx); - } - } - // QString based matching. - else { - if (entered_text.isEmpty()) { - entered_text = value.toString(); - } - - QString item_text = item_value.toString(); - - switch (match_type) { - case Qt::MatchRegExp: - if (QRegExp(entered_text, cs).exactMatch(item_text)) { - result.append(idx); - } - break; - - case Qt::MatchWildcard: - if (QRegExp(entered_text, cs, QRegExp::Wildcard).exactMatch(item_text)) { - result.append(idx); - } - break; - - case Qt::MatchStartsWith: - if (item_text.startsWith(entered_text, cs)) { - result.append(idx); - } - break; - - case Qt::MatchEndsWith: - if (item_text.endsWith(entered_text, cs)) { - result.append(idx); - } - break; - - case Qt::MatchFixedString: - if (item_text.compare(entered_text, cs) == 0) { - result.append(idx); - } - break; - - case Qt::MatchContains: - default: - if (item_text.contains(entered_text, cs)) { - result.append(idx); - } - break; - } - } - - if (recurse && hasChildren(idx)) { - result += match(index(0, idx.column(), idx), role, (entered_text.isEmpty() ? value : entered_text), (all_hits ? -1 : hits - result.count()), flags); - } - } - - from = 0; - to = start.row(); - } - - return result; -} - -bool FeedsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { - if (left.isValid() && right.isValid()) { - // Make necessary castings. - const RootItem *left_item = m_sourceModel->itemForIndex(left); - const RootItem *right_item = m_sourceModel->itemForIndex(right); - - // NOTE: Here we want to accomplish that ALL - // categories are queued one after another and all - // feeds are queued one after another too. - // Moreover, sort everything alphabetically or - // by item counts, depending on the sort column. - - if (left_item->kind() == right_item->kind()) { - // Both items are feeds or both items are categories. - if (left.column() == FDS_MODEL_COUNTS_INDEX) { - // User wants to sort according to counts. - return left_item->countOfUnreadMessages() < right_item->countOfUnreadMessages(); - } - else { - // In other cases, sort by title. - return QString::localeAwareCompare(left_item->title(), right_item->title()) < 0; - } - } - else if (left_item->kind() == RootItemKind::Bin) { - // Left item is recycle bin. Make sure it is "biggest" item if we have selected ascending order. - return sortOrder() == Qt::DescendingOrder; - } - else if (right_item->kind() == RootItemKind::Bin) { - // Right item is recycle bin. Make sure it is "smallest" item if we have selected descending order. - return sortOrder() == Qt::AscendingOrder; - } - else if (left_item->kind() == RootItemKind::Feed) { - // Left item is feed, right item is category. - return false; - } - else { - // Left item is category, right item is feed. - // NOTE: Category is in fact "more" than feed but we consider it to be "less" because it should be "placed" - // above the "smalles" feed when ascending sort is used. - // NOTE: We need to keep recycle bin in first position. - return true; - } - } - else { - return false; - } -} - -bool FeedsProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { - bool should_show = filterAcceptsRowInternal(source_row, source_parent); - - if (should_show && m_hiddenIndices.contains(QPair(source_row, source_parent))) { - const_cast(this)->m_hiddenIndices.removeAll(QPair(source_row, source_parent)); - - // Load status. - emit expandAfterFilterIn(m_sourceModel->index(source_row, 0, source_parent)); - } - - if (!should_show) { - const_cast(this)->m_hiddenIndices.append(QPair(source_row, source_parent)); - } - - return should_show; -} - -bool FeedsProxyModel::filterAcceptsRowInternal(int source_row, const QModelIndex &source_parent) const { - if (!m_showUnreadOnly) { - return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); - } - - const QModelIndex idx = m_sourceModel->index(source_row, 0, source_parent); - - if (!idx.isValid()) { - return false; - } - - const RootItem *item = m_sourceModel->itemForIndex(idx); - - if (item->kind() == RootItemKind::Bin || item->kind() == RootItemKind::ServiceRoot) { - // Recycle bin is always displayed. - return true; - } - else if (item->isParentOf(m_selectedItem)/* || item->isChildOf(m_selectedItem)*/ || m_selectedItem == item) { - // Currently selected item and all its parents and children must be displayed. - return true; - } - else { - // NOTE: If item has < 0 of unread message it may mean, that the count - // of unread messages is not (yet) known, display that item too. - return item->countOfUnreadMessages() != 0; - } -} - -const RootItem *FeedsProxyModel::selectedItem() const { - return m_selectedItem; -} - -void FeedsProxyModel::setSelectedItem(const RootItem *selected_item) { - m_selectedItem = selected_item; -} - -bool FeedsProxyModel::showUnreadOnly() const { - return m_showUnreadOnly; -} - -void FeedsProxyModel::invalidateReadFeedsFilter(bool set_new_value, bool show_unread_only) { - if (set_new_value) { - setShowUnreadOnly(show_unread_only); - } - - QTimer::singleShot(0, this, &FeedsProxyModel::invalidateFilter); -} - -void FeedsProxyModel::setShowUnreadOnly(bool show_unread_only) { - m_showUnreadOnly = show_unread_only; - qApp->settings()->setValue(GROUP(Feeds), Feeds::ShowOnlyUnreadFeeds, show_unread_only); -} - -QModelIndexList FeedsProxyModel::mapListToSource(const QModelIndexList &indexes) const { - QModelIndexList source_indexes; - - foreach (const QModelIndex &index, indexes) { - source_indexes << mapToSource(index); - } - - return source_indexes; -} - -void FeedsProxyModel::invalidateFilter() { - QSortFilterProxyModel::invalidateFilter(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "core/feedsproxymodel.h" + +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "core/feedsmodel.h" +#include "services/abstract/rootitem.h" + +#include + + +FeedsProxyModel::FeedsProxyModel(FeedsModel* source_model, QObject* parent) + : QSortFilterProxyModel(parent), m_sourceModel(source_model), m_selectedItem(nullptr), + m_showUnreadOnly(false), m_hiddenIndices(QList>()) { + setObjectName(QSL("FeedsProxyModel")); + setSortRole(Qt::EditRole); + setSortCaseSensitivity(Qt::CaseInsensitive); + setFilterCaseSensitivity(Qt::CaseInsensitive); + setFilterKeyColumn(-1); + setFilterRole(Qt::EditRole); + setDynamicSortFilter(true); + setSourceModel(m_sourceModel); +} + +FeedsProxyModel::~FeedsProxyModel() { + qDebug("Destroying FeedsProxyModel instance"); +} + +QModelIndexList FeedsProxyModel::match(const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags) const { + QModelIndexList result; + const uint match_type = flags & 0x0F; + const Qt::CaseSensitivity cs = Qt::CaseInsensitive; + const bool recurse = flags & Qt::MatchRecursive; + const bool wrap = flags & Qt::MatchWrap; + const bool all_hits = (hits == -1); + QString entered_text; + const QModelIndex p = parent(start); + int from = start.row(); + int to = rowCount(p); + + for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i) { + for (int r = from; (r < to) && (all_hits || result.count() < hits); ++r) { + QModelIndex idx = index(r, start.column(), p); + + if (!idx.isValid()) { + continue; + } + + QModelIndex mapped_idx = mapToSource(idx); + QVariant item_value = m_sourceModel->itemForIndex(mapped_idx)->title(); + + // QVariant based matching. + if (match_type == Qt::MatchExactly) { + if (value == item_value) { + result.append(idx); + } + } + + // QString based matching. + else { + if (entered_text.isEmpty()) { + entered_text = value.toString(); + } + + QString item_text = item_value.toString(); + + switch (match_type) { + case Qt::MatchRegExp: + if (QRegExp(entered_text, cs).exactMatch(item_text)) { + result.append(idx); + } + + break; + + case Qt::MatchWildcard: + if (QRegExp(entered_text, cs, QRegExp::Wildcard).exactMatch(item_text)) { + result.append(idx); + } + + break; + + case Qt::MatchStartsWith: + if (item_text.startsWith(entered_text, cs)) { + result.append(idx); + } + + break; + + case Qt::MatchEndsWith: + if (item_text.endsWith(entered_text, cs)) { + result.append(idx); + } + + break; + + case Qt::MatchFixedString: + if (item_text.compare(entered_text, cs) == 0) { + result.append(idx); + } + + break; + + case Qt::MatchContains: + default: + if (item_text.contains(entered_text, cs)) { + result.append(idx); + } + + break; + } + } + + if (recurse && hasChildren(idx)) { + result += match(index(0, idx.column(), idx), role, (entered_text.isEmpty() ? value : entered_text), (all_hits ? -1 : hits - result.count()), flags); + } + } + + from = 0; + to = start.row(); + } + + return result; +} + +bool FeedsProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { + if (left.isValid() && right.isValid()) { + // Make necessary castings. + const RootItem* left_item = m_sourceModel->itemForIndex(left); + const RootItem* right_item = m_sourceModel->itemForIndex(right); + + // NOTE: Here we want to accomplish that ALL + // categories are queued one after another and all + // feeds are queued one after another too. + // Moreover, sort everything alphabetically or + // by item counts, depending on the sort column. + + if (left_item->kind() == right_item->kind()) { + // Both items are feeds or both items are categories. + if (left.column() == FDS_MODEL_COUNTS_INDEX) { + // User wants to sort according to counts. + return left_item->countOfUnreadMessages() < right_item->countOfUnreadMessages(); + } + + else { + // In other cases, sort by title. + return QString::localeAwareCompare(left_item->title(), right_item->title()) < 0; + } + } + + else if (left_item->kind() == RootItemKind::Bin) { + // Left item is recycle bin. Make sure it is "biggest" item if we have selected ascending order. + return sortOrder() == Qt::DescendingOrder; + } + + else if (right_item->kind() == RootItemKind::Bin) { + // Right item is recycle bin. Make sure it is "smallest" item if we have selected descending order. + return sortOrder() == Qt::AscendingOrder; + } + + else if (left_item->kind() == RootItemKind::Feed) { + // Left item is feed, right item is category. + return false; + } + + else { + // Left item is category, right item is feed. + // NOTE: Category is in fact "more" than feed but we consider it to be "less" because it should be "placed" + // above the "smalles" feed when ascending sort is used. + // NOTE: We need to keep recycle bin in first position. + return true; + } + } + + else { + return false; + } +} + +bool FeedsProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { + bool should_show = filterAcceptsRowInternal(source_row, source_parent); + + if (should_show && m_hiddenIndices.contains(QPair(source_row, source_parent))) { + const_cast(this)->m_hiddenIndices.removeAll(QPair(source_row, source_parent)); + // Load status. + emit expandAfterFilterIn(m_sourceModel->index(source_row, 0, source_parent)); + } + + if (!should_show) { + const_cast(this)->m_hiddenIndices.append(QPair(source_row, source_parent)); + } + + return should_show; +} + +bool FeedsProxyModel::filterAcceptsRowInternal(int source_row, const QModelIndex& source_parent) const { + if (!m_showUnreadOnly) { + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); + } + + const QModelIndex idx = m_sourceModel->index(source_row, 0, source_parent); + + if (!idx.isValid()) { + return false; + } + + const RootItem* item = m_sourceModel->itemForIndex(idx); + + if (item->kind() == RootItemKind::Bin || item->kind() == RootItemKind::ServiceRoot) { + // Recycle bin is always displayed. + return true; + } + + else if (item->isParentOf(m_selectedItem)/* || item->isChildOf(m_selectedItem)*/ || m_selectedItem == item) { + // Currently selected item and all its parents and children must be displayed. + return true; + } + + else { + // NOTE: If item has < 0 of unread message it may mean, that the count + // of unread messages is not (yet) known, display that item too. + return item->countOfUnreadMessages() != 0; + } +} + +const RootItem* FeedsProxyModel::selectedItem() const { + return m_selectedItem; +} + +void FeedsProxyModel::setSelectedItem(const RootItem* selected_item) { + m_selectedItem = selected_item; +} + +bool FeedsProxyModel::showUnreadOnly() const { + return m_showUnreadOnly; +} + +void FeedsProxyModel::invalidateReadFeedsFilter(bool set_new_value, bool show_unread_only) { + if (set_new_value) { + setShowUnreadOnly(show_unread_only); + } + + QTimer::singleShot(0, this, &FeedsProxyModel::invalidateFilter); +} + +void FeedsProxyModel::setShowUnreadOnly(bool show_unread_only) { + m_showUnreadOnly = show_unread_only; + qApp->settings()->setValue(GROUP(Feeds), Feeds::ShowOnlyUnreadFeeds, show_unread_only); +} + +QModelIndexList FeedsProxyModel::mapListToSource(const QModelIndexList& indexes) const { + QModelIndexList source_indexes; + + foreach (const QModelIndex& index, indexes) { + source_indexes << mapToSource(index); + } + + return source_indexes; +} + +void FeedsProxyModel::invalidateFilter() { + QSortFilterProxyModel::invalidateFilter(); +} diff --git a/src/core/feedsproxymodel.h b/src/core/feedsproxymodel.h index 7015f96f5..cce824e4d 100755 --- a/src/core/feedsproxymodel.h +++ b/src/core/feedsproxymodel.h @@ -1,70 +1,70 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FEEDSPROXYMODEL_H -#define FEEDSPROXYMODEL_H - -#include - - -class FeedsModel; -class RootItem; - -class FeedsProxyModel : public QSortFilterProxyModel { - Q_OBJECT - - public: - // Constructors and destructors. - explicit FeedsProxyModel(FeedsModel *source_model, QObject *parent = 0); - virtual ~FeedsProxyModel(); - - // Returns index list of items which "match" given value. - // Used for finding items according to entered title text. - QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const; - - // Maps list of indexes. - QModelIndexList mapListToSource(const QModelIndexList &indexes) const; - - bool showUnreadOnly() const; - void setShowUnreadOnly(bool show_unread_only); - - const RootItem *selectedItem() const; - void setSelectedItem(const RootItem *selected_item); - - public slots: - void invalidateReadFeedsFilter(bool set_new_value = false, bool show_unread_only = false); - - private slots: - void invalidateFilter(); - - signals: - void expandAfterFilterIn(QModelIndex idx) const; - - private: - // Compares two rows of data. - bool lessThan(const QModelIndex &left, const QModelIndex &right) const; - bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; - bool filterAcceptsRowInternal(int source_row, const QModelIndex &source_parent) const; - - // Source model pointer. - FeedsModel *m_sourceModel; - const RootItem *m_selectedItem; - bool m_showUnreadOnly; - QList > m_hiddenIndices; -}; - -#endif // FEEDSPROXYMODEL_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FEEDSPROXYMODEL_H +#define FEEDSPROXYMODEL_H + +#include + + +class FeedsModel; +class RootItem; + +class FeedsProxyModel : public QSortFilterProxyModel { + Q_OBJECT + + public: + // Constructors and destructors. + explicit FeedsProxyModel(FeedsModel* source_model, QObject* parent = 0); + virtual ~FeedsProxyModel(); + + // Returns index list of items which "match" given value. + // Used for finding items according to entered title text. + QModelIndexList match(const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags) const; + + // Maps list of indexes. + QModelIndexList mapListToSource(const QModelIndexList& indexes) const; + + bool showUnreadOnly() const; + void setShowUnreadOnly(bool show_unread_only); + + const RootItem* selectedItem() const; + void setSelectedItem(const RootItem* selected_item); + + public slots: + void invalidateReadFeedsFilter(bool set_new_value = false, bool show_unread_only = false); + + private slots: + void invalidateFilter(); + + signals: + void expandAfterFilterIn(QModelIndex idx) const; + + private: + // Compares two rows of data. + bool lessThan(const QModelIndex& left, const QModelIndex& right) const; + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const; + bool filterAcceptsRowInternal(int source_row, const QModelIndex& source_parent) const; + + // Source model pointer. + FeedsModel* m_sourceModel; + const RootItem* m_selectedItem; + bool m_showUnreadOnly; + QList> m_hiddenIndices; +}; + +#endif // FEEDSPROXYMODEL_H diff --git a/src/core/message.cpp b/src/core/message.cpp index f8bd6788e..fcbd679e8 100755 --- a/src/core/message.cpp +++ b/src/core/message.cpp @@ -1,112 +1,112 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "core/message.h" - -#include "miscellaneous/textfactory.h" - -#include - - -Enclosure::Enclosure(const QString &url, const QString &mime) : m_url(url), m_mimeType(mime) { -} - -QList Enclosures::decodeEnclosuresFromString(const QString &enclosures_data) { - QList enclosures; - - foreach (const QString &single_enclosure, enclosures_data.split(ENCLOSURES_OUTER_SEPARATOR, QString::SkipEmptyParts)) { - Enclosure enclosure; - - if (single_enclosure.contains(ECNLOSURES_INNER_SEPARATOR)) { - QStringList mime_url = single_enclosure.split(ECNLOSURES_INNER_SEPARATOR); - - enclosure.m_mimeType = QByteArray::fromBase64(mime_url.at(0).toLocal8Bit()); - enclosure.m_url = QByteArray::fromBase64(mime_url.at(1).toLocal8Bit()); - } - else { - enclosure.m_url = QByteArray::fromBase64(single_enclosure.toLocal8Bit()); - } - - enclosures.append(enclosure); - } - - return enclosures; -} - -QString Enclosures::encodeEnclosuresToString(const QList &enclosures) { - QStringList enclosures_str; - - foreach (const Enclosure &enclosure, enclosures) { - if (enclosure.m_mimeType.isEmpty()) { - enclosures_str.append(enclosure.m_url.toLocal8Bit().toBase64()); - } - else { - enclosures_str.append(QString(enclosure.m_mimeType.toLocal8Bit().toBase64()) + - ECNLOSURES_INNER_SEPARATOR + - enclosure.m_url.toLocal8Bit().toBase64()); - } - } - - return enclosures_str.join(QString(ENCLOSURES_OUTER_SEPARATOR)); -} - -Message::Message() { - m_title = m_url = m_author = m_contents = m_feedId = m_customId = m_customHash = QSL(""); - m_enclosures = QList(); - m_accountId = m_id = 0; - m_isRead = m_isImportant = false; -} - -Message Message::fromSqlRecord(const QSqlRecord &record, bool *result) { - if (record.count() != MSG_DB_CUSTOM_HASH_INDEX + 1) { - if (result != nullptr) { - *result = false; - return Message(); - } - } - - Message message; - - message.m_id = record.value(MSG_DB_ID_INDEX).toInt(); - message.m_isRead = record.value(MSG_DB_READ_INDEX).toBool(); - message.m_isImportant = record.value(MSG_DB_IMPORTANT_INDEX).toBool(); - message.m_feedId = record.value(MSG_DB_FEED_CUSTOM_ID_INDEX).toString(); - message.m_title = record.value(MSG_DB_TITLE_INDEX).toString(); - message.m_url = record.value(MSG_DB_URL_INDEX).toString(); - message.m_author = record.value(MSG_DB_AUTHOR_INDEX).toString(); - message.m_created = TextFactory::parseDateTime(record.value(MSG_DB_DCREATED_INDEX).value()); - message.m_contents = record.value(MSG_DB_CONTENTS_INDEX).toString(); - message.m_enclosures = Enclosures::decodeEnclosuresFromString(record.value(MSG_DB_ENCLOSURES_INDEX).toString()); - message.m_accountId = record.value(MSG_DB_ACCOUNT_ID_INDEX).toInt(); - message.m_customId = record.value(MSG_DB_CUSTOM_ID_INDEX).toString(); - message.m_customHash = record.value(MSG_DB_CUSTOM_HASH_INDEX).toString(); - - if (result != nullptr) { - *result = true; - } - - return message; -} - -uint qHash(Message key, uint seed) { - Q_UNUSED(seed) - return (key.m_accountId * 10000) + key.m_id; -} - -uint qHash(const Message &key) { - return (key.m_accountId * 10000) + key.m_id; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "core/message.h" + +#include "miscellaneous/textfactory.h" + +#include + + +Enclosure::Enclosure(const QString& url, const QString& mime) : m_url(url), m_mimeType(mime) { +} + +QList Enclosures::decodeEnclosuresFromString(const QString& enclosures_data) { + QList enclosures; + + foreach (const QString& single_enclosure, enclosures_data.split(ENCLOSURES_OUTER_SEPARATOR, QString::SkipEmptyParts)) { + Enclosure enclosure; + + if (single_enclosure.contains(ECNLOSURES_INNER_SEPARATOR)) { + QStringList mime_url = single_enclosure.split(ECNLOSURES_INNER_SEPARATOR); + enclosure.m_mimeType = QByteArray::fromBase64(mime_url.at(0).toLocal8Bit()); + enclosure.m_url = QByteArray::fromBase64(mime_url.at(1).toLocal8Bit()); + } + + else { + enclosure.m_url = QByteArray::fromBase64(single_enclosure.toLocal8Bit()); + } + + enclosures.append(enclosure); + } + + return enclosures; +} + +QString Enclosures::encodeEnclosuresToString(const QList& enclosures) { + QStringList enclosures_str; + + foreach (const Enclosure& enclosure, enclosures) { + if (enclosure.m_mimeType.isEmpty()) { + enclosures_str.append(enclosure.m_url.toLocal8Bit().toBase64()); + } + + else { + enclosures_str.append(QString(enclosure.m_mimeType.toLocal8Bit().toBase64()) + + ECNLOSURES_INNER_SEPARATOR + + enclosure.m_url.toLocal8Bit().toBase64()); + } + } + + return enclosures_str.join(QString(ENCLOSURES_OUTER_SEPARATOR)); +} + +Message::Message() { + m_title = m_url = m_author = m_contents = m_feedId = m_customId = m_customHash = QSL(""); + m_enclosures = QList(); + m_accountId = m_id = 0; + m_isRead = m_isImportant = false; +} + +Message Message::fromSqlRecord(const QSqlRecord& record, bool* result) { + if (record.count() != MSG_DB_CUSTOM_HASH_INDEX + 1) { + if (result != nullptr) { + *result = false; + return Message(); + } + } + + Message message; + message.m_id = record.value(MSG_DB_ID_INDEX).toInt(); + message.m_isRead = record.value(MSG_DB_READ_INDEX).toBool(); + message.m_isImportant = record.value(MSG_DB_IMPORTANT_INDEX).toBool(); + message.m_feedId = record.value(MSG_DB_FEED_CUSTOM_ID_INDEX).toString(); + message.m_title = record.value(MSG_DB_TITLE_INDEX).toString(); + message.m_url = record.value(MSG_DB_URL_INDEX).toString(); + message.m_author = record.value(MSG_DB_AUTHOR_INDEX).toString(); + message.m_created = TextFactory::parseDateTime(record.value(MSG_DB_DCREATED_INDEX).value()); + message.m_contents = record.value(MSG_DB_CONTENTS_INDEX).toString(); + message.m_enclosures = Enclosures::decodeEnclosuresFromString(record.value(MSG_DB_ENCLOSURES_INDEX).toString()); + message.m_accountId = record.value(MSG_DB_ACCOUNT_ID_INDEX).toInt(); + message.m_customId = record.value(MSG_DB_CUSTOM_ID_INDEX).toString(); + message.m_customHash = record.value(MSG_DB_CUSTOM_HASH_INDEX).toString(); + + if (result != nullptr) { + *result = true; + } + + return message; +} + +uint qHash(Message key, uint seed) { + Q_UNUSED(seed) + return (key.m_accountId * 10000) + key.m_id; +} + +uint qHash(const Message& key) { + return (key.m_accountId * 10000) + key.m_id; +} diff --git a/src/core/message.h b/src/core/message.h index 8fc81c4d6..459b77e5b 100755 --- a/src/core/message.h +++ b/src/core/message.h @@ -1,85 +1,85 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef MESSAGE_H -#define MESSAGE_H - -#include "definitions/definitions.h" - -#include -#include -#include - - -// Represents single enclosure. -struct Enclosure { - public: - explicit Enclosure(const QString &url = QString(), const QString &mime = QString()); - - QString m_url; - QString m_mimeType; -}; - -// Represents single enclosure. -class Enclosures { - public: - static QList decodeEnclosuresFromString(const QString &enclosures_data); - static QString encodeEnclosuresToString(const QList &enclosures); -}; - -// Represents single message. -class Message { - public: - explicit Message(); - - // Creates Message from given record, which contains - // row from query SELECT * FROM Messages WHERE ....; - static Message fromSqlRecord(const QSqlRecord &record, bool *result = nullptr); - - QString m_title; - QString m_url; - QString m_author; - QString m_contents; - QDateTime m_created; - QString m_feedId; - int m_accountId; - int m_id; - QString m_customId; - QString m_customHash; - - bool m_isRead; - bool m_isImportant; - - QList m_enclosures; - - // Is true if "created" date was obtained directly - // from the feed, otherwise is false - bool m_createdFromFeed; - - friend inline bool operator==(const Message &lhs, const Message &rhs) { - return lhs.m_accountId == rhs.m_accountId && lhs.m_id == rhs.m_id; - } - - friend inline bool operator!=(const Message &lhs, const Message &rhs) { - return !(lhs == rhs); - } -}; - -uint qHash(Message key, uint seed); -uint qHash(const Message &key); - -#endif // MESSAGE_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef MESSAGE_H +#define MESSAGE_H + +#include "definitions/definitions.h" + +#include +#include +#include + + +// Represents single enclosure. +struct Enclosure { + public: + explicit Enclosure(const QString& url = QString(), const QString& mime = QString()); + + QString m_url; + QString m_mimeType; +}; + +// Represents single enclosure. +class Enclosures { + public: + static QList decodeEnclosuresFromString(const QString& enclosures_data); + static QString encodeEnclosuresToString(const QList& enclosures); +}; + +// Represents single message. +class Message { + public: + explicit Message(); + + // Creates Message from given record, which contains + // row from query SELECT * FROM Messages WHERE ....; + static Message fromSqlRecord(const QSqlRecord& record, bool* result = nullptr); + + QString m_title; + QString m_url; + QString m_author; + QString m_contents; + QDateTime m_created; + QString m_feedId; + int m_accountId; + int m_id; + QString m_customId; + QString m_customHash; + + bool m_isRead; + bool m_isImportant; + + QList m_enclosures; + + // Is true if "created" date was obtained directly + // from the feed, otherwise is false + bool m_createdFromFeed; + + friend inline bool operator==(const Message& lhs, const Message& rhs) { + return lhs.m_accountId == rhs.m_accountId && lhs.m_id == rhs.m_id; + } + + friend inline bool operator!=(const Message& lhs, const Message& rhs) { + return !(lhs == rhs); + } +}; + +uint qHash(Message key, uint seed); +uint qHash(const Message& key); + +#endif // MESSAGE_H diff --git a/src/core/messagesmodel.cpp b/src/core/messagesmodel.cpp index 374e58dac..13ae977f8 100755 --- a/src/core/messagesmodel.cpp +++ b/src/core/messagesmodel.cpp @@ -30,524 +30,525 @@ #include -MessagesModel::MessagesModel(QObject *parent) - : QSqlQueryModel(parent), MessagesModelSqlLayer(), - m_cache(new MessagesModelCache(this)), m_messageHighlighter(NoHighlighting), m_customDateFormat(QString()) { - setupFonts(); - setupIcons(); - setupHeaderData(); - updateDateFormat(); - - loadMessages(nullptr); +MessagesModel::MessagesModel(QObject* parent) + : QSqlQueryModel(parent), MessagesModelSqlLayer(), + m_cache(new MessagesModelCache(this)), m_messageHighlighter(NoHighlighting), m_customDateFormat(QString()) { + setupFonts(); + setupIcons(); + setupHeaderData(); + updateDateFormat(); + loadMessages(nullptr); } MessagesModel::~MessagesModel() { - qDebug("Destroying MessagesModel instance."); + qDebug("Destroying MessagesModel instance."); } void MessagesModel::setupIcons() { - m_favoriteIcon = qApp->icons()->fromTheme(QSL("mail-mark-important")); - m_readIcon = qApp->icons()->fromTheme(QSL("mail-mark-read")); - m_unreadIcon = qApp->icons()->fromTheme(QSL("mail-mark-unread")); + m_favoriteIcon = qApp->icons()->fromTheme(QSL("mail-mark-important")); + m_readIcon = qApp->icons()->fromTheme(QSL("mail-mark-read")); + m_unreadIcon = qApp->icons()->fromTheme(QSL("mail-mark-unread")); } void MessagesModel::repopulate() { - m_cache->clear(); - setQuery(selectStatement(), m_db); + m_cache->clear(); + setQuery(selectStatement(), m_db); - while (canFetchMore()) { - fetchMore(); - } + while (canFetchMore()) { + fetchMore(); + } } -bool MessagesModel::setData(const QModelIndex &index, const QVariant &value, int role) { - Q_UNUSED(role) - - m_cache->setData(index, value, record(index.row())); - return true; +bool MessagesModel::setData(const QModelIndex& index, const QVariant& value, int role) { + Q_UNUSED(role) + m_cache->setData(index, value, record(index.row())); + return true; } void MessagesModel::setupFonts() { - m_normalFont = Application::font("MessagesView"); - m_boldFont = m_normalFont; - m_boldFont.setBold(true); - - m_normalStrikedFont = m_normalFont; - m_boldStrikedFont = m_boldFont; - m_normalStrikedFont.setStrikeOut(true); - m_boldStrikedFont.setStrikeOut(true); + m_normalFont = Application::font("MessagesView"); + m_boldFont = m_normalFont; + m_boldFont.setBold(true); + m_normalStrikedFont = m_normalFont; + m_boldStrikedFont = m_boldFont; + m_normalStrikedFont.setStrikeOut(true); + m_boldStrikedFont.setStrikeOut(true); } -void MessagesModel::loadMessages(RootItem *item) { - m_selectedItem = item; +void MessagesModel::loadMessages(RootItem* item) { + m_selectedItem = item; - if (item == nullptr) { - setFilter(QSL(DEFAULT_SQL_MESSAGES_FILTER)); - } - else { - if (!item->getParentServiceRoot()->loadMessagesForItem(item, this)) { - setFilter(QSL("true != true")); - qWarning("Loading of messages from item '%s' failed.", qPrintable(item->title())); - qApp->showGuiMessage(tr("Loading of messages from item '%1' failed.").arg(item->title()), - tr("Loading of messages failed, maybe messages could not be downloaded."), - QSystemTrayIcon::Critical, - qApp->mainFormWidget(), - true); - } - } + if (item == nullptr) { + setFilter(QSL(DEFAULT_SQL_MESSAGES_FILTER)); + } - repopulate(); + else { + if (!item->getParentServiceRoot()->loadMessagesForItem(item, this)) { + setFilter(QSL("true != true")); + qWarning("Loading of messages from item '%s' failed.", qPrintable(item->title())); + qApp->showGuiMessage(tr("Loading of messages from item '%1' failed.").arg(item->title()), + tr("Loading of messages failed, maybe messages could not be downloaded."), + QSystemTrayIcon::Critical, + qApp->mainFormWidget(), + true); + } + } + + repopulate(); } bool MessagesModel::setMessageImportantById(int id, RootItem::Importance important) { - for (int i = 0; i < rowCount(); i++) { - int found_id = data(i, MSG_DB_ID_INDEX, Qt::EditRole).toInt(); + for (int i = 0; i < rowCount(); i++) { + int found_id = data(i, MSG_DB_ID_INDEX, Qt::EditRole).toInt(); - if (found_id == id) { - bool set = setData(index(i, MSG_DB_IMPORTANT_INDEX), important); + if (found_id == id) { + bool set = setData(index(i, MSG_DB_IMPORTANT_INDEX), important); - if (set) { - emit dataChanged(index(i, 0), index(i, MSG_DB_CUSTOM_HASH_INDEX)); - } + if (set) { + emit dataChanged(index(i, 0), index(i, MSG_DB_CUSTOM_HASH_INDEX)); + } - return set; - } - } + return set; + } + } - return false; + return false; } void MessagesModel::highlightMessages(MessagesModel::MessageHighlighter highlight) { - m_messageHighlighter = highlight; - emit layoutAboutToBeChanged(); - emit layoutChanged(); + m_messageHighlighter = highlight; + emit layoutAboutToBeChanged(); + emit layoutChanged(); } int MessagesModel::messageId(int row_index) const { - return data(row_index, MSG_DB_ID_INDEX, Qt::EditRole).toInt(); + return data(row_index, MSG_DB_ID_INDEX, Qt::EditRole).toInt(); } RootItem::Importance MessagesModel::messageImportance(int row_index) const { - return (RootItem::Importance) data(row_index, MSG_DB_IMPORTANT_INDEX, Qt::EditRole).toInt(); + return (RootItem::Importance) data(row_index, MSG_DB_IMPORTANT_INDEX, Qt::EditRole).toInt(); } -RootItem *MessagesModel::loadedItem() const { - return m_selectedItem; +RootItem* MessagesModel::loadedItem() const { + return m_selectedItem; } void MessagesModel::updateDateFormat() { - if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::UseCustomDate)).toBool()) { - m_customDateFormat = qApp->settings()->value(GROUP(Messages), SETTING(Messages::CustomDateFormat)).toString(); - } - else { - m_customDateFormat = QString(); - } + if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::UseCustomDate)).toBool()) { + m_customDateFormat = qApp->settings()->value(GROUP(Messages), SETTING(Messages::CustomDateFormat)).toString(); + } + + else { + m_customDateFormat = QString(); + } } void MessagesModel::reloadWholeLayout() { - emit layoutAboutToBeChanged(); - emit layoutChanged(); + emit layoutAboutToBeChanged(); + emit layoutChanged(); } -Message MessagesModel::messageAt(int row_index) const { - return Message::fromSqlRecord(m_cache->containsData(row_index) ? m_cache->record(row_index) : record(row_index)); +Message MessagesModel::messageAt(int row_index) const { + return Message::fromSqlRecord(m_cache->containsData(row_index) ? m_cache->record(row_index) : record(row_index)); } void MessagesModel::setupHeaderData() { - m_headerData << /*: Tooltip for ID of message.*/ tr("Id") << - /*: Tooltip for "read" column in msg list.*/ tr("Read") << - /*: Tooltip for "deleted" column in msg list.*/ tr("Deleted") << - /*: Tooltip for "important" column in msg list.*/ tr("Important") << - /*: Tooltip for name of feed for message.*/ tr("Feed") << - /*: Tooltip for title of message.*/ tr("Title") << - /*: Tooltip for url of message.*/ tr("Url") << - /*: Tooltip for author of message.*/ tr("Author") << - /*: Tooltip for creation date of message.*/ tr("Created on") << - /*: Tooltip for contents of message.*/ tr("Contents") << - /*: Tooltip for "pdeleted" column in msg list.*/ tr("Permanently deleted") << - /*: Tooltip for attachments of message.*/ tr("Attachments") << - /*: Tooltip for account ID of message.*/ tr("Account ID") << - /*: Tooltip for custom ID of message.*/ tr("Custom ID") << - /*: Tooltip for custom hash string of message.*/ tr("Custom hash") << - /*: Tooltip for custom ID of feed of message.*/ tr("Feed ID");; - - m_tooltipData << tr("Id of the message.") << tr("Is message read?") << - tr("Is message deleted?") << tr("Is message important?") << - tr("Id of feed which this message belongs to.") << - tr("Title of the message.") << tr("Url of the message.") << - tr("Author of the message.") << tr("Creation date of the message.") << - tr("Contents of the message.") << tr("Is message permanently deleted from recycle bin?") << - tr("List of attachments.") << tr("Account ID of the message.") << tr("Custom ID of the message") << - tr("Custom hash of the message.") << tr("Custom ID of feed of the message."); + m_headerData << /*: Tooltip for ID of message.*/ tr("Id") << + /*: Tooltip for "read" column in msg list.*/ tr("Read") << + /*: Tooltip for "deleted" column in msg list.*/ tr("Deleted") << + /*: Tooltip for "important" column in msg list.*/ tr("Important") << + /*: Tooltip for name of feed for message.*/ tr("Feed") << + /*: Tooltip for title of message.*/ tr("Title") << + /*: Tooltip for url of message.*/ tr("Url") << + /*: Tooltip for author of message.*/ tr("Author") << + /*: Tooltip for creation date of message.*/ tr("Created on") << + /*: Tooltip for contents of message.*/ tr("Contents") << + /*: Tooltip for "pdeleted" column in msg list.*/ tr("Permanently deleted") << + /*: Tooltip for attachments of message.*/ tr("Attachments") << + /*: Tooltip for account ID of message.*/ tr("Account ID") << + /*: Tooltip for custom ID of message.*/ tr("Custom ID") << + /*: Tooltip for custom hash string of message.*/ tr("Custom hash") << + /*: Tooltip for custom ID of feed of message.*/ tr("Feed ID");; + m_tooltipData << tr("Id of the message.") << tr("Is message read?") << + tr("Is message deleted?") << tr("Is message important?") << + tr("Id of feed which this message belongs to.") << + tr("Title of the message.") << tr("Url of the message.") << + tr("Author of the message.") << tr("Creation date of the message.") << + tr("Contents of the message.") << tr("Is message permanently deleted from recycle bin?") << + tr("List of attachments.") << tr("Account ID of the message.") << tr("Custom ID of the message") << + tr("Custom hash of the message.") << tr("Custom ID of feed of the message."); } -Qt::ItemFlags MessagesModel::flags(const QModelIndex &index) const { - Q_UNUSED(index) - - return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemNeverHasChildren; +Qt::ItemFlags MessagesModel::flags(const QModelIndex& index) const { + Q_UNUSED(index) + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemNeverHasChildren; } QVariant MessagesModel::data(int row, int column, int role) const { - return data(index(row, column), role); + return data(index(row, column), role); } -QVariant MessagesModel::data(const QModelIndex &idx, int role) const { - // This message is not in cache, return real data from live query. - switch (role) { - // Human readable data for viewing. - case Qt::DisplayRole: { - int index_column = idx.column(); +QVariant MessagesModel::data(const QModelIndex& idx, int role) const { + // This message is not in cache, return real data from live query. + switch (role) { + // Human readable data for viewing. + case Qt::DisplayRole: { + int index_column = idx.column(); - if (index_column == MSG_DB_DCREATED_INDEX) { - QDateTime dt = TextFactory::parseDateTime(QSqlQueryModel::data(idx, role).value()).toLocalTime(); + if (index_column == MSG_DB_DCREATED_INDEX) { + QDateTime dt = TextFactory::parseDateTime(QSqlQueryModel::data(idx, role).value()).toLocalTime(); - if (m_customDateFormat.isEmpty()) { - return dt.toString(Qt::DefaultLocaleShortDate); - } - else { - return dt.toString(m_customDateFormat); - } - } - else if (index_column == MSG_DB_AUTHOR_INDEX) { - const QString author_name = QSqlQueryModel::data(idx, role).toString(); + if (m_customDateFormat.isEmpty()) { + return dt.toString(Qt::DefaultLocaleShortDate); + } - return author_name.isEmpty() ? QSL("-") : author_name; - } - else if (index_column != MSG_DB_IMPORTANT_INDEX && index_column != MSG_DB_READ_INDEX) { - return QSqlQueryModel::data(idx, role); - } - else { - return QVariant(); - } - } + else { + return dt.toString(m_customDateFormat); + } + } - case Qt::EditRole: - return m_cache->containsData(idx.row()) ? m_cache->data(idx) : QSqlQueryModel::data(idx, role); + else if (index_column == MSG_DB_AUTHOR_INDEX) { + const QString author_name = QSqlQueryModel::data(idx, role).toString(); + return author_name.isEmpty() ? QSL("-") : author_name; + } - case Qt::FontRole: { - QModelIndex idx_read = index(idx.row(), MSG_DB_READ_INDEX); - QVariant data_read = data(idx_read, Qt::EditRole); + else if (index_column != MSG_DB_IMPORTANT_INDEX && index_column != MSG_DB_READ_INDEX) { + return QSqlQueryModel::data(idx, role); + } - const bool is_bin = qobject_cast(loadedItem()) != nullptr; - bool is_deleted; + else { + return QVariant(); + } + } - if (is_bin) { - QModelIndex idx_del = index(idx.row(), MSG_DB_PDELETED_INDEX); - is_deleted = data(idx_del, Qt::EditRole).toBool(); - } - else { - QModelIndex idx_del = index(idx.row(), MSG_DB_DELETED_INDEX); - is_deleted = data(idx_del, Qt::EditRole).toBool(); - } + case Qt::EditRole: + return m_cache->containsData(idx.row()) ? m_cache->data(idx) : QSqlQueryModel::data(idx, role); - const bool striked = is_deleted; + case Qt::FontRole: { + QModelIndex idx_read = index(idx.row(), MSG_DB_READ_INDEX); + QVariant data_read = data(idx_read, Qt::EditRole); + const bool is_bin = qobject_cast(loadedItem()) != nullptr; + bool is_deleted; - if (data_read.toBool()) { - return striked ? m_normalStrikedFont : m_normalFont; - } - else { - return striked ? m_boldStrikedFont : m_boldFont; - } - } + if (is_bin) { + QModelIndex idx_del = index(idx.row(), MSG_DB_PDELETED_INDEX); + is_deleted = data(idx_del, Qt::EditRole).toBool(); + } - case Qt::ForegroundRole: - switch (m_messageHighlighter) { - case HighlightImportant: { - QModelIndex idx_important = index(idx.row(), MSG_DB_IMPORTANT_INDEX); - QVariant dta = m_cache->containsData(idx_important.row()) ? m_cache->data(idx_important) : QSqlQueryModel::data(idx_important); + else { + QModelIndex idx_del = index(idx.row(), MSG_DB_DELETED_INDEX); + is_deleted = data(idx_del, Qt::EditRole).toBool(); + } - return dta.toInt() == 1 ? QColor(Qt::blue) : QVariant(); - } + const bool striked = is_deleted; - case HighlightUnread: { - QModelIndex idx_read = index(idx.row(), MSG_DB_READ_INDEX); - QVariant dta = m_cache->containsData(idx_read.row()) ? m_cache->data(idx_read) : QSqlQueryModel::data(idx_read); + if (data_read.toBool()) { + return striked ? m_normalStrikedFont : m_normalFont; + } - return dta.toInt() == 0 ? QColor(Qt::blue) : QVariant(); - } + else { + return striked ? m_boldStrikedFont : m_boldFont; + } + } - case NoHighlighting: - default: - return QVariant(); - } + case Qt::ForegroundRole: + switch (m_messageHighlighter) { + case HighlightImportant: { + QModelIndex idx_important = index(idx.row(), MSG_DB_IMPORTANT_INDEX); + QVariant dta = m_cache->containsData(idx_important.row()) ? m_cache->data(idx_important) : QSqlQueryModel::data(idx_important); + return dta.toInt() == 1 ? QColor(Qt::blue) : QVariant(); + } - case Qt::DecorationRole: { - const int index_column = idx.column(); + case HighlightUnread: { + QModelIndex idx_read = index(idx.row(), MSG_DB_READ_INDEX); + QVariant dta = m_cache->containsData(idx_read.row()) ? m_cache->data(idx_read) : QSqlQueryModel::data(idx_read); + return dta.toInt() == 0 ? QColor(Qt::blue) : QVariant(); + } - if (index_column == MSG_DB_READ_INDEX) { - QModelIndex idx_read = index(idx.row(), MSG_DB_READ_INDEX); - QVariant dta = m_cache->containsData(idx_read.row()) ? m_cache->data(idx_read) : QSqlQueryModel::data(idx_read); + case NoHighlighting: + default: + return QVariant(); + } - return dta.toInt() == 1 ? m_readIcon : m_unreadIcon; - } - else if (index_column == MSG_DB_IMPORTANT_INDEX) { - QModelIndex idx_important = index(idx.row(), MSG_DB_IMPORTANT_INDEX); - QVariant dta = m_cache->containsData(idx_important.row()) ? m_cache->data(idx_important) : QSqlQueryModel::data(idx_important); + case Qt::DecorationRole: { + const int index_column = idx.column(); - return dta.toInt() == 1 ? m_favoriteIcon : QVariant(); - } - else { - return QVariant(); - } - } + if (index_column == MSG_DB_READ_INDEX) { + QModelIndex idx_read = index(idx.row(), MSG_DB_READ_INDEX); + QVariant dta = m_cache->containsData(idx_read.row()) ? m_cache->data(idx_read) : QSqlQueryModel::data(idx_read); + return dta.toInt() == 1 ? m_readIcon : m_unreadIcon; + } - default: - return QVariant(); - } + else if (index_column == MSG_DB_IMPORTANT_INDEX) { + QModelIndex idx_important = index(idx.row(), MSG_DB_IMPORTANT_INDEX); + QVariant dta = m_cache->containsData(idx_important.row()) ? m_cache->data(idx_important) : QSqlQueryModel::data(idx_important); + return dta.toInt() == 1 ? m_favoriteIcon : QVariant(); + } + + else { + return QVariant(); + } + } + + default: + return QVariant(); + } } bool MessagesModel::setMessageRead(int row_index, RootItem::ReadStatus read) { - if (data(row_index, MSG_DB_READ_INDEX, Qt::EditRole).toInt() == read) { - // Read status is the same is the one currently set. - // In that case, no extra work is needed. - return true; - } + if (data(row_index, MSG_DB_READ_INDEX, Qt::EditRole).toInt() == read) { + // Read status is the same is the one currently set. + // In that case, no extra work is needed. + return true; + } - Message message = messageAt(row_index); + Message message = messageAt(row_index); - if (!m_selectedItem->getParentServiceRoot()->onBeforeSetMessagesRead(m_selectedItem, QList() << message, read)) { - // Cannot change read status of the item. Abort. - return false; - } + if (!m_selectedItem->getParentServiceRoot()->onBeforeSetMessagesRead(m_selectedItem, QList() << message, read)) { + // Cannot change read status of the item. Abort. + return false; + } - // Rewrite "visible" data in the model. - bool working_change = setData(index(row_index, MSG_DB_READ_INDEX), read); + // Rewrite "visible" data in the model. + bool working_change = setData(index(row_index, MSG_DB_READ_INDEX), read); - if (!working_change) { - // If rewriting in the model failed, then cancel all actions. - qDebug("Setting of new data to the model failed for message read change."); - return false; - } + if (!working_change) { + // If rewriting in the model failed, then cancel all actions. + qDebug("Setting of new data to the model failed for message read change."); + return false; + } - if (DatabaseQueries::markMessagesReadUnread(m_db, QStringList() << QString::number(message.m_id), read)) { - return m_selectedItem->getParentServiceRoot()->onAfterSetMessagesRead(m_selectedItem, QList() << message, read); - } - else { - return false; - } + if (DatabaseQueries::markMessagesReadUnread(m_db, QStringList() << QString::number(message.m_id), read)) { + return m_selectedItem->getParentServiceRoot()->onAfterSetMessagesRead(m_selectedItem, QList() << message, read); + } + + else { + return false; + } } bool MessagesModel::setMessageReadById(int id, RootItem::ReadStatus read) { - for (int i = 0; i < rowCount(); i++) { - int found_id = data(i, MSG_DB_ID_INDEX, Qt::EditRole).toInt(); + for (int i = 0; i < rowCount(); i++) { + int found_id = data(i, MSG_DB_ID_INDEX, Qt::EditRole).toInt(); - if (found_id == id) { - bool set = setData(index(i, MSG_DB_READ_INDEX), read); + if (found_id == id) { + bool set = setData(index(i, MSG_DB_READ_INDEX), read); - if (set) { - emit dataChanged(index(i, 0), index(i, MSG_DB_CUSTOM_HASH_INDEX)); - } + if (set) { + emit dataChanged(index(i, 0), index(i, MSG_DB_CUSTOM_HASH_INDEX)); + } - return set; - } - } + return set; + } + } - return false; + return false; } bool MessagesModel::switchMessageImportance(int row_index) { - const QModelIndex target_index = index(row_index, MSG_DB_IMPORTANT_INDEX); - const RootItem::Importance current_importance = (RootItem::Importance) data(target_index, Qt::EditRole).toInt(); - const RootItem::Importance next_importance = current_importance == RootItem::Important ? - RootItem::NotImportant : RootItem::Important; - const Message message = messageAt(row_index); - const QPair pair(message, next_importance); + const QModelIndex target_index = index(row_index, MSG_DB_IMPORTANT_INDEX); + const RootItem::Importance current_importance = (RootItem::Importance) data(target_index, Qt::EditRole).toInt(); + const RootItem::Importance next_importance = current_importance == RootItem::Important ? + RootItem::NotImportant : RootItem::Important; + const Message message = messageAt(row_index); + const QPair pair(message, next_importance); - if (!m_selectedItem->getParentServiceRoot()->onBeforeSwitchMessageImportance(m_selectedItem, - QList >() << pair)) { - return false; - } + if (!m_selectedItem->getParentServiceRoot()->onBeforeSwitchMessageImportance(m_selectedItem, + QList>() << pair)) { + return false; + } - // Rewrite "visible" data in the model. - const bool working_change = setData(target_index, next_importance); + // Rewrite "visible" data in the model. + const bool working_change = setData(target_index, next_importance); - if (!working_change) { - // If rewriting in the model failed, then cancel all actions. - qDebug("Setting of new data to the model failed for message importance change."); - return false; - } + if (!working_change) { + // If rewriting in the model failed, then cancel all actions. + qDebug("Setting of new data to the model failed for message importance change."); + return false; + } - // Commit changes. - if (DatabaseQueries::markMessageImportant(m_db, message.m_id, next_importance)) { - emit dataChanged(index(row_index, 0), index(row_index, MSG_DB_FEED_CUSTOM_ID_INDEX), QVector() << Qt::FontRole); + // Commit changes. + if (DatabaseQueries::markMessageImportant(m_db, message.m_id, next_importance)) { + emit dataChanged(index(row_index, 0), index(row_index, MSG_DB_FEED_CUSTOM_ID_INDEX), QVector() << Qt::FontRole); + return m_selectedItem->getParentServiceRoot()->onAfterSwitchMessageImportance(m_selectedItem, + QList>() << pair); + } - return m_selectedItem->getParentServiceRoot()->onAfterSwitchMessageImportance(m_selectedItem, - QList >() << pair); - } - else { - return false; - } + else { + return false; + } } -bool MessagesModel::switchBatchMessageImportance(const QModelIndexList &messages) { - QStringList message_ids; - QList > message_states; +bool MessagesModel::switchBatchMessageImportance(const QModelIndexList& messages) { + QStringList message_ids; + QList> message_states; - // Obtain IDs of all desired messages. - foreach (const QModelIndex &message, messages) { - const Message msg = messageAt(message.row()); - RootItem::Importance message_importance = messageImportance((message.row())); + // Obtain IDs of all desired messages. + foreach (const QModelIndex& message, messages) { + const Message msg = messageAt(message.row()); + RootItem::Importance message_importance = messageImportance((message.row())); + message_states.append(QPair(msg, message_importance == RootItem::Important ? + RootItem::NotImportant : + RootItem::Important)); + message_ids.append(QString::number(msg.m_id)); + QModelIndex idx_msg_imp = index(message.row(), MSG_DB_IMPORTANT_INDEX); + setData(idx_msg_imp, message_importance == RootItem::Important ? + (int) RootItem::NotImportant : + (int) RootItem::Important); + } - message_states.append(QPair(msg, message_importance == RootItem::Important ? - RootItem::NotImportant : - RootItem::Important)); - message_ids.append(QString::number(msg.m_id)); + reloadWholeLayout(); - QModelIndex idx_msg_imp = index(message.row(), MSG_DB_IMPORTANT_INDEX); - setData(idx_msg_imp, message_importance == RootItem::Important ? - (int) RootItem::NotImportant : - (int) RootItem::Important); - } + if (!m_selectedItem->getParentServiceRoot()->onBeforeSwitchMessageImportance(m_selectedItem, message_states)) { + return false; + } - reloadWholeLayout(); + if (DatabaseQueries::switchMessagesImportance(m_db, message_ids)) { + return m_selectedItem->getParentServiceRoot()->onAfterSwitchMessageImportance(m_selectedItem, message_states); + } - if (!m_selectedItem->getParentServiceRoot()->onBeforeSwitchMessageImportance(m_selectedItem, message_states)) { - return false; - } - - if (DatabaseQueries::switchMessagesImportance(m_db, message_ids)) { - return m_selectedItem->getParentServiceRoot()->onAfterSwitchMessageImportance(m_selectedItem, message_states); - } - else { - return false; - } + else { + return false; + } } -bool MessagesModel::setBatchMessagesDeleted(const QModelIndexList &messages) { - QStringList message_ids; - QList msgs; +bool MessagesModel::setBatchMessagesDeleted(const QModelIndexList& messages) { + QStringList message_ids; + QList msgs; - // Obtain IDs of all desired messages. - foreach (const QModelIndex &message, messages) { - const Message msg = messageAt(message.row()); + // Obtain IDs of all desired messages. + foreach (const QModelIndex& message, messages) { + const Message msg = messageAt(message.row()); + msgs.append(msg); + message_ids.append(QString::number(msg.m_id)); - msgs.append(msg); - message_ids.append(QString::number(msg.m_id)); + if (qobject_cast(m_selectedItem) != nullptr) { + setData(index(message.row(), MSG_DB_PDELETED_INDEX), 1); + } - if (qobject_cast(m_selectedItem) != nullptr) { - setData(index(message.row(), MSG_DB_PDELETED_INDEX), 1); - } - else { - setData(index(message.row(), MSG_DB_DELETED_INDEX), 1); - } - } + else { + setData(index(message.row(), MSG_DB_DELETED_INDEX), 1); + } + } - reloadWholeLayout(); + reloadWholeLayout(); - if (!m_selectedItem->getParentServiceRoot()->onBeforeMessagesDelete(m_selectedItem, msgs)) { - return false; - } + if (!m_selectedItem->getParentServiceRoot()->onBeforeMessagesDelete(m_selectedItem, msgs)) { + return false; + } - bool deleted; + bool deleted; - if (m_selectedItem->kind() != RootItemKind::Bin) { - deleted = DatabaseQueries::deleteOrRestoreMessagesToFromBin(m_db, message_ids, true); - } - else { - deleted = DatabaseQueries::permanentlyDeleteMessages(m_db, message_ids); - } + if (m_selectedItem->kind() != RootItemKind::Bin) { + deleted = DatabaseQueries::deleteOrRestoreMessagesToFromBin(m_db, message_ids, true); + } - if (deleted) { - return m_selectedItem->getParentServiceRoot()->onAfterMessagesDelete(m_selectedItem, msgs); - } - else { - return false; - } + else { + deleted = DatabaseQueries::permanentlyDeleteMessages(m_db, message_ids); + } + + if (deleted) { + return m_selectedItem->getParentServiceRoot()->onAfterMessagesDelete(m_selectedItem, msgs); + } + + else { + return false; + } } -bool MessagesModel::setBatchMessagesRead(const QModelIndexList &messages, RootItem::ReadStatus read) { - QStringList message_ids; - QList msgs; +bool MessagesModel::setBatchMessagesRead(const QModelIndexList& messages, RootItem::ReadStatus read) { + QStringList message_ids; + QList msgs; - // Obtain IDs of all desired messages. - foreach (const QModelIndex &message, messages) { - Message msg = messageAt(message.row()); + // Obtain IDs of all desired messages. + foreach (const QModelIndex& message, messages) { + Message msg = messageAt(message.row()); + msgs.append(msg); + message_ids.append(QString::number(msg.m_id)); + setData(index(message.row(), MSG_DB_READ_INDEX), (int) read); + } - msgs.append(msg); - message_ids.append(QString::number(msg.m_id)); + reloadWholeLayout(); - setData(index(message.row(), MSG_DB_READ_INDEX), (int) read); - } + if (!m_selectedItem->getParentServiceRoot()->onBeforeSetMessagesRead(m_selectedItem, msgs, read)) { + return false; + } - reloadWholeLayout(); + if (DatabaseQueries::markMessagesReadUnread(m_db, message_ids, read)) { + return m_selectedItem->getParentServiceRoot()->onAfterSetMessagesRead(m_selectedItem, msgs, read); + } - if (!m_selectedItem->getParentServiceRoot()->onBeforeSetMessagesRead(m_selectedItem, msgs, read)) { - return false; - } - - if (DatabaseQueries::markMessagesReadUnread(m_db, message_ids, read)) { - return m_selectedItem->getParentServiceRoot()->onAfterSetMessagesRead(m_selectedItem, msgs, read); - } - else { - return false; - } + else { + return false; + } } -bool MessagesModel::setBatchMessagesRestored(const QModelIndexList &messages) { - QStringList message_ids; - QList msgs; +bool MessagesModel::setBatchMessagesRestored(const QModelIndexList& messages) { + QStringList message_ids; + QList msgs; - // Obtain IDs of all desired messages. - foreach (const QModelIndex &message, messages) { - const Message msg = messageAt(message.row()); + // Obtain IDs of all desired messages. + foreach (const QModelIndex& message, messages) { + const Message msg = messageAt(message.row()); + msgs.append(msg); + message_ids.append(QString::number(msg.m_id)); + setData(index(message.row(), MSG_DB_PDELETED_INDEX), 0); + setData(index(message.row(), MSG_DB_DELETED_INDEX), 0); + } - msgs.append(msg); - message_ids.append(QString::number(msg.m_id)); + reloadWholeLayout(); - setData(index(message.row(), MSG_DB_PDELETED_INDEX), 0); - setData(index(message.row(), MSG_DB_DELETED_INDEX), 0); - } + if (!m_selectedItem->getParentServiceRoot()->onBeforeMessagesRestoredFromBin(m_selectedItem, msgs)) { + return false; + } - reloadWholeLayout(); + if (DatabaseQueries::deleteOrRestoreMessagesToFromBin(m_db, message_ids, false)) { + return m_selectedItem->getParentServiceRoot()->onAfterMessagesRestoredFromBin(m_selectedItem, msgs); + } - if (!m_selectedItem->getParentServiceRoot()->onBeforeMessagesRestoredFromBin(m_selectedItem, msgs)) { - return false; - } - - if (DatabaseQueries::deleteOrRestoreMessagesToFromBin(m_db, message_ids, false)) { - return m_selectedItem->getParentServiceRoot()->onAfterMessagesRestoredFromBin(m_selectedItem, msgs); - } - else { - return false; - } + else { + return false; + } } QVariant MessagesModel::headerData(int section, Qt::Orientation orientation, int role) const { - Q_UNUSED(orientation) + Q_UNUSED(orientation) - switch (role) { - case Qt::DisplayRole: - // Display textual headers for all columns except "read" and - // "important" columns. - if (section != MSG_DB_READ_INDEX && section != MSG_DB_IMPORTANT_INDEX) { - return m_headerData.at(section); - } - else { - return QVariant(); - } + switch (role) { + case Qt::DisplayRole: - case Qt::ToolTipRole: - return m_tooltipData.at(section); + // Display textual headers for all columns except "read" and + // "important" columns. + if (section != MSG_DB_READ_INDEX && section != MSG_DB_IMPORTANT_INDEX) { + return m_headerData.at(section); + } - case Qt::EditRole: - return m_headerData.at(section); + else { + return QVariant(); + } - // Display icons for "read" and "important" columns. - case Qt::DecorationRole: { - switch (section) { - case MSG_DB_READ_INDEX: - return m_readIcon; + case Qt::ToolTipRole: + return m_tooltipData.at(section); - case MSG_DB_IMPORTANT_INDEX: - return m_favoriteIcon; + case Qt::EditRole: + return m_headerData.at(section); - default: - return QVariant(); - } - } + // Display icons for "read" and "important" columns. + case Qt::DecorationRole: { + switch (section) { + case MSG_DB_READ_INDEX: + return m_readIcon; - default: - return QVariant(); - } + case MSG_DB_IMPORTANT_INDEX: + return m_favoriteIcon; + + default: + return QVariant(); + } + } + + default: + return QVariant(); + } } diff --git a/src/core/messagesmodel.h b/src/core/messagesmodel.h index ec6d63383..dacd390cd 100755 --- a/src/core/messagesmodel.h +++ b/src/core/messagesmodel.h @@ -32,84 +32,84 @@ class MessagesModelCache; class MessagesModel : public QSqlQueryModel, public MessagesModelSqlLayer { - Q_OBJECT + Q_OBJECT - public: - // Enum which describes basic filtering schemes - // for messages. - enum MessageHighlighter { - NoHighlighting = 100, - HighlightUnread = 101, - HighlightImportant = 102 - }; + public: + // Enum which describes basic filtering schemes + // for messages. + enum MessageHighlighter { + NoHighlighting = 100, + HighlightUnread = 101, + HighlightImportant = 102 + }; - // Constructors and destructors. - explicit MessagesModel(QObject *parent = 0); - virtual ~MessagesModel(); + // Constructors and destructors. + explicit MessagesModel(QObject* parent = 0); + virtual ~MessagesModel(); - // Fetches ALL available data to the model. - // NOTE: This activates the SQL query and populates the model with new data. - void repopulate(); + // Fetches ALL available data to the model. + // NOTE: This activates the SQL query and populates the model with new data. + void repopulate(); - // Model implementation. - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - QVariant data(int row, int column, int role = Qt::DisplayRole) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - Qt::ItemFlags flags(const QModelIndex &index) const; + // Model implementation. + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + QVariant data(int row, int column, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + Qt::ItemFlags flags(const QModelIndex& index) const; - // Returns message at given index. - Message messageAt(int row_index) const; - int messageId(int row_index) const; - RootItem::Importance messageImportance(int row_index) const; + // Returns message at given index. + Message messageAt(int row_index) const; + int messageId(int row_index) const; + RootItem::Importance messageImportance(int row_index) const; - RootItem *loadedItem() const; - void updateDateFormat(); - void reloadWholeLayout(); + RootItem* loadedItem() const; + void updateDateFormat(); + void reloadWholeLayout(); - // SINGLE message manipulators. - bool switchMessageImportance(int row_index); - bool setMessageRead(int row_index, RootItem::ReadStatus read); + // SINGLE message manipulators. + bool switchMessageImportance(int row_index); + bool setMessageRead(int row_index, RootItem::ReadStatus read); - // BATCH messages manipulators. - bool switchBatchMessageImportance(const QModelIndexList &messages); - bool setBatchMessagesDeleted(const QModelIndexList &messages); - bool setBatchMessagesRead(const QModelIndexList &messages, RootItem::ReadStatus read); - bool setBatchMessagesRestored(const QModelIndexList &messages); + // BATCH messages manipulators. + bool switchBatchMessageImportance(const QModelIndexList& messages); + bool setBatchMessagesDeleted(const QModelIndexList& messages); + bool setBatchMessagesRead(const QModelIndexList& messages, RootItem::ReadStatus read); + bool setBatchMessagesRestored(const QModelIndexList& messages); - // Highlights messages. - void highlightMessages(MessageHighlighter highlight); + // Highlights messages. + void highlightMessages(MessageHighlighter highlight); - // Loads messages of given feeds. - void loadMessages(RootItem *item); + // Loads messages of given feeds. + void loadMessages(RootItem* item); - public slots: - // NOTE: These methods DO NOT actually change data in the DB, just in the model. - // These are particularly used by msg browser. - bool setMessageImportantById(int id, RootItem::Importance important); - bool setMessageReadById(int id, RootItem::ReadStatus read); + public slots: + // NOTE: These methods DO NOT actually change data in the DB, just in the model. + // These are particularly used by msg browser. + bool setMessageImportantById(int id, RootItem::Importance important); + bool setMessageReadById(int id, RootItem::ReadStatus read); - private: - void setupHeaderData(); - void setupFonts(); - void setupIcons(); + private: + void setupHeaderData(); + void setupFonts(); + void setupIcons(); - MessagesModelCache *m_cache; - MessageHighlighter m_messageHighlighter; + MessagesModelCache* m_cache; + MessageHighlighter m_messageHighlighter; - QString m_customDateFormat; - RootItem *m_selectedItem; - QList m_headerData; - QList m_tooltipData; + QString m_customDateFormat; + RootItem* m_selectedItem; + QList m_headerData; + QList m_tooltipData; - QFont m_normalFont; - QFont m_boldFont; - QFont m_normalStrikedFont; - QFont m_boldStrikedFont; + QFont m_normalFont; + QFont m_boldFont; + QFont m_normalStrikedFont; + QFont m_boldStrikedFont; - QIcon m_favoriteIcon; - QIcon m_readIcon; - QIcon m_unreadIcon; + QIcon m_favoriteIcon; + QIcon m_readIcon; + QIcon m_unreadIcon; }; Q_DECLARE_METATYPE(MessagesModel::MessageHighlighter) diff --git a/src/core/messagesmodelcache.cpp b/src/core/messagesmodelcache.cpp index 6cab72336..98e7135b1 100755 --- a/src/core/messagesmodelcache.cpp +++ b/src/core/messagesmodelcache.cpp @@ -1,39 +1,39 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "core/messagesmodelcache.h" - -#include "miscellaneous/textfactory.h" - - -MessagesModelCache::MessagesModelCache(QObject *parent) : QObject(parent), m_msgCache(QHash()) { -} - -MessagesModelCache::~MessagesModelCache() { -} - -void MessagesModelCache::setData(const QModelIndex &index, const QVariant &value, const QSqlRecord &record) { - if (!m_msgCache.contains(index.row())) { - m_msgCache[index.row()] = record; - } - - m_msgCache[index.row()].setValue(index.column(), value); -} - -QVariant MessagesModelCache::data(const QModelIndex &idx) { - return m_msgCache[idx.row()].value(idx.column()); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "core/messagesmodelcache.h" + +#include "miscellaneous/textfactory.h" + + +MessagesModelCache::MessagesModelCache(QObject* parent) : QObject(parent), m_msgCache(QHash()) { +} + +MessagesModelCache::~MessagesModelCache() { +} + +void MessagesModelCache::setData(const QModelIndex& index, const QVariant& value, const QSqlRecord& record) { + if (!m_msgCache.contains(index.row())) { + m_msgCache[index.row()] = record; + } + + m_msgCache[index.row()].setValue(index.column(), value); +} + +QVariant MessagesModelCache::data(const QModelIndex& idx) { + return m_msgCache[idx.row()].value(idx.column()); +} diff --git a/src/core/messagesmodelcache.h b/src/core/messagesmodelcache.h index 9e4ced221..7b799e8e8 100755 --- a/src/core/messagesmodelcache.h +++ b/src/core/messagesmodelcache.h @@ -1,55 +1,55 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef MESSAGESMODELCACHE_H -#define MESSAGESMODELCACHE_H - -#include - -#include "core/message.h" - -#include -#include - - -class MessagesModelCache : public QObject { - Q_OBJECT - - public: - explicit MessagesModelCache(QObject *parent = nullptr); - virtual ~MessagesModelCache(); - - inline bool containsData(int row_idx) const { - return m_msgCache.contains(row_idx); - } - - inline QSqlRecord record(int row_idx) const { - return m_msgCache.value(row_idx); - } - - inline void clear() { - m_msgCache.clear(); - } - - void setData(const QModelIndex &index, const QVariant &value, const QSqlRecord &record); - QVariant data(const QModelIndex &idx); - - private: - QHash m_msgCache; -}; - -#endif // MESSAGESMODELCACHE_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef MESSAGESMODELCACHE_H +#define MESSAGESMODELCACHE_H + +#include + +#include "core/message.h" + +#include +#include + + +class MessagesModelCache : public QObject { + Q_OBJECT + + public: + explicit MessagesModelCache(QObject* parent = nullptr); + virtual ~MessagesModelCache(); + + inline bool containsData(int row_idx) const { + return m_msgCache.contains(row_idx); + } + + inline QSqlRecord record(int row_idx) const { + return m_msgCache.value(row_idx); + } + + inline void clear() { + m_msgCache.clear(); + } + + void setData(const QModelIndex& index, const QVariant& value, const QSqlRecord& record); + QVariant data(const QModelIndex& idx); + + private: + QHash m_msgCache; +}; + +#endif // MESSAGESMODELCACHE_H diff --git a/src/core/messagesmodelsqllayer.cpp b/src/core/messagesmodelsqllayer.cpp index ba5d0fa51..308c2766c 100755 --- a/src/core/messagesmodelsqllayer.cpp +++ b/src/core/messagesmodelsqllayer.cpp @@ -1,105 +1,105 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "core/messagesmodelsqllayer.h" - -#include "definitions/definitions.h" -#include "miscellaneous/application.h" - - -MessagesModelSqlLayer::MessagesModelSqlLayer() - : m_filter(QSL(DEFAULT_SQL_MESSAGES_FILTER)), m_fieldNames(QMap()), - m_sortColumns(QList()), m_sortOrders(QList()){ - m_db = qApp->database()->connection(QSL("MessagesModel"), DatabaseFactory::FromSettings); - - m_fieldNames[MSG_DB_ID_INDEX] = "Messages.id"; - m_fieldNames[MSG_DB_READ_INDEX] = "Messages.is_read"; - m_fieldNames[MSG_DB_DELETED_INDEX] = "Messages.is_deleted"; - m_fieldNames[MSG_DB_IMPORTANT_INDEX] = "Messages.is_important"; - m_fieldNames[MSG_DB_FEED_TITLE_INDEX] = "Feeds.title"; - m_fieldNames[MSG_DB_TITLE_INDEX] = "Messages.title"; - m_fieldNames[MSG_DB_URL_INDEX] = "Messages.url"; - m_fieldNames[MSG_DB_AUTHOR_INDEX] = "Messages.author"; - m_fieldNames[MSG_DB_DCREATED_INDEX] = "Messages.date_created"; - m_fieldNames[MSG_DB_CONTENTS_INDEX] = "Messages.contents"; - m_fieldNames[MSG_DB_PDELETED_INDEX] = "Messages.is_pdeleted"; - m_fieldNames[MSG_DB_ENCLOSURES_INDEX] = "Messages.enclosures"; - m_fieldNames[MSG_DB_ACCOUNT_ID_INDEX] = "Messages.account_id"; - m_fieldNames[MSG_DB_CUSTOM_ID_INDEX] = "Messages.custom_id"; - m_fieldNames[MSG_DB_CUSTOM_HASH_INDEX] = "Messages.custom_hash"; - m_fieldNames[MSG_DB_FEED_CUSTOM_ID_INDEX] = "Messages.feed"; -} - -void MessagesModelSqlLayer::addSortState(int column, Qt::SortOrder order) { - int existing = m_sortColumns.indexOf(column); - bool is_ctrl_pressed = (QApplication::queryKeyboardModifiers() & Qt::ControlModifier) == Qt::ControlModifier; - - if (existing >= 0) { - m_sortColumns.removeAt(existing); - m_sortOrders.removeAt(existing); - } - - if (m_sortColumns.size() > MAX_MULTICOLUMN_SORT_STATES) { - // We support only limited number of sort states - // due to DB performance. - m_sortColumns.removeAt(0); - m_sortOrders.removeAt(0); - } - - if (is_ctrl_pressed) { - // User is activating the multicolumn sort mode. - m_sortColumns.append(column); - m_sortOrders.append(order); - } - else { - m_sortColumns.prepend(column); - m_sortOrders.prepend(order); - } - - qDebug("Added sort state, select statement is now:\n'%s'", qPrintable(selectStatement())); -} - -void MessagesModelSqlLayer::setFilter(const QString &filter) { - m_filter = filter; -} - -QString MessagesModelSqlLayer::formatFields() const { - return m_fieldNames.values().join(QSL(", ")); -} - -QString MessagesModelSqlLayer::selectStatement() const { - return QL1S("SELECT ") + formatFields() + - QSL(" FROM Messages LEFT JOIN Feeds ON Messages.feed = Feeds.custom_id AND Messages.account_id = Feeds.account_id WHERE ") + - m_filter + orderByClause() + QL1C(';'); -} - -QString MessagesModelSqlLayer::orderByClause() const { - if (m_sortColumns.isEmpty()) { - return QString(); - } - else { - QStringList sorts; - - for (int i = 0; i < m_sortColumns.size(); i++) { - QString field_name(m_fieldNames[m_sortColumns[i]]); - - sorts.append(field_name + (m_sortOrders[i] == Qt::AscendingOrder ? QSL(" ASC") : QSL(" DESC"))); - } - - return QL1S(" ORDER BY ") + sorts.join(QSL(", ")); - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "core/messagesmodelsqllayer.h" + +#include "definitions/definitions.h" +#include "miscellaneous/application.h" + + +MessagesModelSqlLayer::MessagesModelSqlLayer() + : m_filter(QSL(DEFAULT_SQL_MESSAGES_FILTER)), m_fieldNames(QMap()), + m_sortColumns(QList()), m_sortOrders(QList()) { + m_db = qApp->database()->connection(QSL("MessagesModel"), DatabaseFactory::FromSettings); + m_fieldNames[MSG_DB_ID_INDEX] = "Messages.id"; + m_fieldNames[MSG_DB_READ_INDEX] = "Messages.is_read"; + m_fieldNames[MSG_DB_DELETED_INDEX] = "Messages.is_deleted"; + m_fieldNames[MSG_DB_IMPORTANT_INDEX] = "Messages.is_important"; + m_fieldNames[MSG_DB_FEED_TITLE_INDEX] = "Feeds.title"; + m_fieldNames[MSG_DB_TITLE_INDEX] = "Messages.title"; + m_fieldNames[MSG_DB_URL_INDEX] = "Messages.url"; + m_fieldNames[MSG_DB_AUTHOR_INDEX] = "Messages.author"; + m_fieldNames[MSG_DB_DCREATED_INDEX] = "Messages.date_created"; + m_fieldNames[MSG_DB_CONTENTS_INDEX] = "Messages.contents"; + m_fieldNames[MSG_DB_PDELETED_INDEX] = "Messages.is_pdeleted"; + m_fieldNames[MSG_DB_ENCLOSURES_INDEX] = "Messages.enclosures"; + m_fieldNames[MSG_DB_ACCOUNT_ID_INDEX] = "Messages.account_id"; + m_fieldNames[MSG_DB_CUSTOM_ID_INDEX] = "Messages.custom_id"; + m_fieldNames[MSG_DB_CUSTOM_HASH_INDEX] = "Messages.custom_hash"; + m_fieldNames[MSG_DB_FEED_CUSTOM_ID_INDEX] = "Messages.feed"; +} + +void MessagesModelSqlLayer::addSortState(int column, Qt::SortOrder order) { + int existing = m_sortColumns.indexOf(column); + bool is_ctrl_pressed = (QApplication::queryKeyboardModifiers() & Qt::ControlModifier) == Qt::ControlModifier; + + if (existing >= 0) { + m_sortColumns.removeAt(existing); + m_sortOrders.removeAt(existing); + } + + if (m_sortColumns.size() > MAX_MULTICOLUMN_SORT_STATES) { + // We support only limited number of sort states + // due to DB performance. + m_sortColumns.removeAt(0); + m_sortOrders.removeAt(0); + } + + if (is_ctrl_pressed) { + // User is activating the multicolumn sort mode. + m_sortColumns.append(column); + m_sortOrders.append(order); + } + + else { + m_sortColumns.prepend(column); + m_sortOrders.prepend(order); + } + + qDebug("Added sort state, select statement is now:\n'%s'", qPrintable(selectStatement())); +} + +void MessagesModelSqlLayer::setFilter(const QString& filter) { + m_filter = filter; +} + +QString MessagesModelSqlLayer::formatFields() const { + return m_fieldNames.values().join(QSL(", ")); +} + +QString MessagesModelSqlLayer::selectStatement() const { + return QL1S("SELECT ") + formatFields() + + QSL(" FROM Messages LEFT JOIN Feeds ON Messages.feed = Feeds.custom_id AND Messages.account_id = Feeds.account_id WHERE ") + + m_filter + orderByClause() + QL1C(';'); +} + +QString MessagesModelSqlLayer::orderByClause() const { + if (m_sortColumns.isEmpty()) { + return QString(); + } + + else { + QStringList sorts; + + for (int i = 0; i < m_sortColumns.size(); i++) { + QString field_name(m_fieldNames[m_sortColumns[i]]); + sorts.append(field_name + (m_sortOrders[i] == Qt::AscendingOrder ? QSL(" ASC") : QSL(" DESC"))); + } + + return QL1S(" ORDER BY ") + sorts.join(QSL(", ")); + } +} diff --git a/src/core/messagesmodelsqllayer.h b/src/core/messagesmodelsqllayer.h index 1823eac3a..f2a3de13f 100755 --- a/src/core/messagesmodelsqllayer.h +++ b/src/core/messagesmodelsqllayer.h @@ -1,56 +1,56 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - - -#ifndef MESSAGESMODELSQLLAYER_H -#define MESSAGESMODELSQLLAYER_H - -#include - -#include -#include - - -class MessagesModelSqlLayer { - public: - explicit MessagesModelSqlLayer(); - - // Adds this new state to queue of sort states. - void addSortState(int column, Qt::SortOrder order); - - // Sets SQL WHERE clause, without "WHERE" keyword. - void setFilter(const QString &filter); - - protected: - QString orderByClause() const; - QString selectStatement() const; - QString formatFields() const; - - QSqlDatabase m_db; - - private: - QString m_filter; - - // NOTE: These two lists contain data for multicolumn sorting. - // They are always same length. Most important sort column/order - // are located at the start of lists; - QMap m_fieldNames; - QList m_sortColumns; - QList m_sortOrders; -}; - -#endif // MESSAGESMODELSQLLAYER_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + + +#ifndef MESSAGESMODELSQLLAYER_H +#define MESSAGESMODELSQLLAYER_H + +#include + +#include +#include + + +class MessagesModelSqlLayer { + public: + explicit MessagesModelSqlLayer(); + + // Adds this new state to queue of sort states. + void addSortState(int column, Qt::SortOrder order); + + // Sets SQL WHERE clause, without "WHERE" keyword. + void setFilter(const QString& filter); + + protected: + QString orderByClause() const; + QString selectStatement() const; + QString formatFields() const; + + QSqlDatabase m_db; + + private: + QString m_filter; + + // NOTE: These two lists contain data for multicolumn sorting. + // They are always same length. Most important sort column/order + // are located at the start of lists; + QMap m_fieldNames; + QList m_sortColumns; + QList m_sortOrders; +}; + +#endif // MESSAGESMODELSQLLAYER_H diff --git a/src/core/messagesproxymodel.cpp b/src/core/messagesproxymodel.cpp index 5b69fc5cf..1ec36acb3 100755 --- a/src/core/messagesproxymodel.cpp +++ b/src/core/messagesproxymodel.cpp @@ -20,174 +20,181 @@ #include "core/messagesmodel.h" -MessagesProxyModel::MessagesProxyModel(MessagesModel *source_model, QObject *parent) - : QSortFilterProxyModel(parent), m_sourceModel(source_model) { - - setObjectName(QSL("MessagesProxyModel")); - setSortRole(Qt::EditRole); - setSortCaseSensitivity(Qt::CaseInsensitive); - setFilterCaseSensitivity(Qt::CaseInsensitive); - setFilterKeyColumn(-1); - setFilterRole(Qt::EditRole); - setDynamicSortFilter(false); - setSourceModel(m_sourceModel); +MessagesProxyModel::MessagesProxyModel(MessagesModel* source_model, QObject* parent) + : QSortFilterProxyModel(parent), m_sourceModel(source_model) { + setObjectName(QSL("MessagesProxyModel")); + setSortRole(Qt::EditRole); + setSortCaseSensitivity(Qt::CaseInsensitive); + setFilterCaseSensitivity(Qt::CaseInsensitive); + setFilterKeyColumn(-1); + setFilterRole(Qt::EditRole); + setDynamicSortFilter(false); + setSourceModel(m_sourceModel); } MessagesProxyModel::~MessagesProxyModel() { - qDebug("Destroying MessagesProxyModel instance."); + qDebug("Destroying MessagesProxyModel instance."); } QModelIndex MessagesProxyModel::getNextPreviousUnreadItemIndex(int default_row) { - const bool started_from_zero = default_row == 0; - QModelIndex next_index = getNextUnreadItemIndex(default_row, rowCount() - 1); + const bool started_from_zero = default_row == 0; + QModelIndex next_index = getNextUnreadItemIndex(default_row, rowCount() - 1); - // There is no next message, check previous. - if (!next_index.isValid() && !started_from_zero) { - next_index = getNextUnreadItemIndex(0, default_row - 1); - } + // There is no next message, check previous. + if (!next_index.isValid() && !started_from_zero) { + next_index = getNextUnreadItemIndex(0, default_row - 1); + } - return next_index; + return next_index; } QModelIndex MessagesProxyModel::getNextUnreadItemIndex(int default_row, int max_row) const { - while (default_row <= max_row) { - // Get info if the message is read or not. - const QModelIndex proxy_index = index(default_row, MSG_DB_READ_INDEX); - const bool is_read = m_sourceModel->data(mapToSource(proxy_index).row(), - MSG_DB_READ_INDEX, Qt::EditRole).toInt() == 1; + while (default_row <= max_row) { + // Get info if the message is read or not. + const QModelIndex proxy_index = index(default_row, MSG_DB_READ_INDEX); + const bool is_read = m_sourceModel->data(mapToSource(proxy_index).row(), + MSG_DB_READ_INDEX, Qt::EditRole).toInt() == 1; - if (!is_read) { - // We found unread message, mark it. - return proxy_index; - } - else { - default_row++; - } - } + if (!is_read) { + // We found unread message, mark it. + return proxy_index; + } - return QModelIndex(); + else { + default_row++; + } + } + + return QModelIndex(); } -bool MessagesProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { - Q_UNUSED(left) - Q_UNUSED(right) - - // NOTE: Comparisons are done by SQL servers itself, not client-side. - return false; +bool MessagesProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { + Q_UNUSED(left) + Q_UNUSED(right) + // NOTE: Comparisons are done by SQL servers itself, not client-side. + return false; } -QModelIndexList MessagesProxyModel::mapListFromSource(const QModelIndexList &indexes, bool deep) const { - QModelIndexList mapped_indexes; +QModelIndexList MessagesProxyModel::mapListFromSource(const QModelIndexList& indexes, bool deep) const { + QModelIndexList mapped_indexes; - foreach (const QModelIndex &index, indexes) { - if (deep) { - // Construct new source index. - mapped_indexes << mapFromSource(m_sourceModel->index(index.row(), index.column())); - } - else { - mapped_indexes << mapFromSource(index); - } - } + foreach (const QModelIndex& index, indexes) { + if (deep) { + // Construct new source index. + mapped_indexes << mapFromSource(m_sourceModel->index(index.row(), index.column())); + } - return mapped_indexes; + else { + mapped_indexes << mapFromSource(index); + } + } + + return mapped_indexes; } -QModelIndexList MessagesProxyModel::match(const QModelIndex &start, int role, - const QVariant &entered_value, int hits, Qt::MatchFlags flags) const { - QModelIndexList result; - const uint match_type = flags & 0x0F; - const Qt::CaseSensitivity case_sensitivity = Qt::CaseInsensitive; - const bool wrap = flags & Qt::MatchWrap; - const bool all_hits = (hits == -1); - QString entered_text; - int from = start.row(); - int to = rowCount(); +QModelIndexList MessagesProxyModel::match(const QModelIndex& start, int role, + const QVariant& entered_value, int hits, Qt::MatchFlags flags) const { + QModelIndexList result; + const uint match_type = flags & 0x0F; + const Qt::CaseSensitivity case_sensitivity = Qt::CaseInsensitive; + const bool wrap = flags & Qt::MatchWrap; + const bool all_hits = (hits == -1); + QString entered_text; + int from = start.row(); + int to = rowCount(); - for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); i++) { - for (int r = from; (r < to) && (all_hits || result.count() < hits); r++) { - QModelIndex idx = index(r, start.column()); + for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); i++) { + for (int r = from; (r < to) && (all_hits || result.count() < hits); r++) { + QModelIndex idx = index(r, start.column()); - if (!idx.isValid()) { - continue; - } + if (!idx.isValid()) { + continue; + } - QVariant item_value = m_sourceModel->data(mapToSource(idx).row(), MSG_DB_TITLE_INDEX, role); + QVariant item_value = m_sourceModel->data(mapToSource(idx).row(), MSG_DB_TITLE_INDEX, role); - // QVariant based matching. - if (match_type == Qt::MatchExactly) { - if (entered_value == item_value) { - result.append(idx); - } - } - // QString based matching. - else { - if (entered_text.isEmpty()) { - entered_text = entered_value.toString(); - } + // QVariant based matching. + if (match_type == Qt::MatchExactly) { + if (entered_value == item_value) { + result.append(idx); + } + } - QString item_text = item_value.toString(); + // QString based matching. + else { + if (entered_text.isEmpty()) { + entered_text = entered_value.toString(); + } - switch (match_type) { - case Qt::MatchRegExp: - if (QRegExp(entered_text, case_sensitivity).exactMatch(item_text)) { - result.append(idx); - } - break; + QString item_text = item_value.toString(); - case Qt::MatchWildcard: - if (QRegExp(entered_text, case_sensitivity, QRegExp::Wildcard).exactMatch(item_text)) { - result.append(idx); - } - break; + switch (match_type) { + case Qt::MatchRegExp: + if (QRegExp(entered_text, case_sensitivity).exactMatch(item_text)) { + result.append(idx); + } - case Qt::MatchStartsWith: - if (item_text.startsWith(entered_text, case_sensitivity)) { - result.append(idx); - } - break; + break; - case Qt::MatchEndsWith: - if (item_text.endsWith(entered_text, case_sensitivity)) { - result.append(idx); - } - break; + case Qt::MatchWildcard: + if (QRegExp(entered_text, case_sensitivity, QRegExp::Wildcard).exactMatch(item_text)) { + result.append(idx); + } - case Qt::MatchFixedString: - if (item_text.compare(entered_text, case_sensitivity) == 0) { - result.append(idx); - } - break; + break; - case Qt::MatchContains: - default: - if (item_text.contains(entered_text, case_sensitivity)) { - result.append(idx); - } - break; - } - } - } + case Qt::MatchStartsWith: + if (item_text.startsWith(entered_text, case_sensitivity)) { + result.append(idx); + } - // Prepare for the next iteration. - from = 0; - to = start.row(); - } + break; - return result; + case Qt::MatchEndsWith: + if (item_text.endsWith(entered_text, case_sensitivity)) { + result.append(idx); + } + + break; + + case Qt::MatchFixedString: + if (item_text.compare(entered_text, case_sensitivity) == 0) { + result.append(idx); + } + + break; + + case Qt::MatchContains: + default: + if (item_text.contains(entered_text, case_sensitivity)) { + result.append(idx); + } + + break; + } + } + } + + // Prepare for the next iteration. + from = 0; + to = start.row(); + } + + return result; } void MessagesProxyModel::sort(int column, Qt::SortOrder order) { - // NOTE: Ignore here, sort is done elsewhere (server-side). - Q_UNUSED(column) - Q_UNUSED(order) + // NOTE: Ignore here, sort is done elsewhere (server-side). + Q_UNUSED(column) + Q_UNUSED(order) } -QModelIndexList MessagesProxyModel::mapListToSource(const QModelIndexList &indexes) const { - QModelIndexList source_indexes; +QModelIndexList MessagesProxyModel::mapListToSource(const QModelIndexList& indexes) const { + QModelIndexList source_indexes; - foreach (const QModelIndex &index, indexes) { - source_indexes << mapToSource(index); - } + foreach (const QModelIndex& index, indexes) { + source_indexes << mapToSource(index); + } - return source_indexes; + return source_indexes; } diff --git a/src/core/messagesproxymodel.h b/src/core/messagesproxymodel.h index b889cc656..1d221ad7e 100755 --- a/src/core/messagesproxymodel.h +++ b/src/core/messagesproxymodel.h @@ -24,33 +24,33 @@ class MessagesModel; class MessagesProxyModel : public QSortFilterProxyModel { - Q_OBJECT + Q_OBJECT - public: - // Constructors and destructors. - explicit MessagesProxyModel(MessagesModel *source_model, QObject *parent = 0); - virtual ~MessagesProxyModel(); + public: + // Constructors and destructors. + explicit MessagesProxyModel(MessagesModel* source_model, QObject* parent = 0); + virtual ~MessagesProxyModel(); - QModelIndex getNextPreviousUnreadItemIndex(int default_row); + QModelIndex getNextPreviousUnreadItemIndex(int default_row); - // Maps list of indexes. - QModelIndexList mapListToSource(const QModelIndexList &indexes) const; - QModelIndexList mapListFromSource(const QModelIndexList &indexes, bool deep = false) const; + // Maps list of indexes. + QModelIndexList mapListToSource(const QModelIndexList& indexes) const; + QModelIndexList mapListFromSource(const QModelIndexList& indexes, bool deep = false) const; - // Fix for matching indexes with respect to specifics of the message model. - QModelIndexList match(const QModelIndex &start, int role, const QVariant &entered_value, int hits, Qt::MatchFlags flags) const; + // Fix for matching indexes with respect to specifics of the message model. + QModelIndexList match(const QModelIndex& start, int role, const QVariant& entered_value, int hits, Qt::MatchFlags flags) const; - // Performs sort of items. - void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + // Performs sort of items. + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); - private: - QModelIndex getNextUnreadItemIndex(int default_row, int max_row) const; + private: + QModelIndex getNextUnreadItemIndex(int default_row, int max_row) const; - // Compares two rows of data. - bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + // Compares two rows of data. + bool lessThan(const QModelIndex& left, const QModelIndex& right) const; - // Source model pointer. - MessagesModel *m_sourceModel; + // Source model pointer. + MessagesModel* m_sourceModel; }; #endif // MESSAGESPROXYMODEL_H diff --git a/src/definitions/definitions.h b/src/definitions/definitions.h index 19020c331..9c5f96513 100755 --- a/src/definitions/definitions.h +++ b/src/definitions/definitions.h @@ -1,258 +1,258 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef DEFINITIONS_H -#define DEFINITIONS_H - -#include - -#define SERVICE_CODE_STD_RSS "std-rss" -#define SERVICE_CODE_TT_RSS "tt-rss" -#define SERVICE_CODE_OWNCLOUD "owncloud" - -#define ARGUMENTS_LIST_SEPARATOR "\n" - -#define ADBLOCK_UPDATE_DAYS_INTERVAL 5 -#define ADBLOCK_ICON_ACTIVE "adblock" -#define ADBLOCK_ICON_DISABLED "adblock-disabled" -#define IS_IN_ARRAY(offset, array) ((offset >= 0) && (offset < array.count())) -#define ADBLOCK_CUSTOMLIST_NAME "customlist.txt" -#define ADBLOCK_LISTS_SUBDIRECTORY "adblock" -#define ADBLOCK_EASYLIST_URL "https://easylist-downloads.adblockplus.org/easylist.txt" -#define DEFAULT_SQL_MESSAGES_FILTER "0 > 1" -#define MAX_MULTICOLUMN_SORT_STATES 3 -#define ENCLOSURES_OUTER_SEPARATOR '#' -#define ECNLOSURES_INNER_SEPARATOR '&' -#define URI_SCHEME_FEED_SHORT "feed:" -#define URI_SCHEME_FEED "feed://" -#define URI_SCHEME_HTTP "http://" -#define RELEASES_LIST "https://api.github.com/repos/martinrotter/rssguard/releases" -#define DEFAULT_LOCALE "en" -#define DEFAULT_FEED_ENCODING "UTF-8" -#define DEFAULT_FEED_TYPE "RSS" -#define URL_REGEXP "^(http|https|feed|ftp):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?$" -#define USER_AGENT_HTTP_HEADER "User-Agent" -#define TEXT_TITLE_LIMIT 30 -#define RESELECT_MESSAGE_THRESSHOLD 500 -#define ICON_SIZE_SETTINGS 16 -#define NO_PARENT_CATEGORY -1 -#define ID_RECYCLE_BIN -2 -#define TRAY_ICON_BUBBLE_TIMEOUT 20000 -#define CLOSE_LOCK_TIMEOUT 500 -#define DOWNLOAD_TIMEOUT 5000 -#define MESSAGES_VIEW_DEFAULT_COL 170 -#define MESSAGES_VIEW_MINIMUM_COL 36 -#define FEEDS_VIEW_COLUMN_COUNT 2 -#define FEED_DOWNLOADER_MAX_THREADS 6 -#define DEFAULT_DAYS_TO_DELETE_MSG 14 -#define ELLIPSIS_LENGTH 3 -#define MIN_CATEGORY_NAME_LENGTH 1 -#define DEFAULT_AUTO_UPDATE_INTERVAL 15 -#define AUTO_UPDATE_INTERVAL 60000 -#define STARTUP_UPDATE_DELAY 30000 -#define TIMEZONE_OFFSET_LIMIT 6 -#define CHANGE_EVENT_DELAY 250 -#define FLAG_ICON_SUBFOLDER "flags" -#define SEACRH_MESSAGES_ACTION_NAME "search" -#define HIGHLIGHTER_ACTION_NAME "highlighter" -#define SPACER_ACTION_NAME "spacer" -#define SEPARATOR_ACTION_NAME "separator" -#define FILTER_WIDTH 150 -#define FILTER_RIGHT_MARGIN 5 -#define FEEDS_VIEW_INDENTATION 10 -#define ACCEPT_HEADER_FOR_FEED_DOWNLOADER "application/atom+xml,application/xml;q=0.9,text/xml;q=0.8,*/*;q=0.7" -#define MIME_TYPE_ITEM_POINTER "rssguard/itempointer" -#define DOWNLOADER_ICON_SIZE 48 -#define NOTIFICATION_ICON_SIZE 32 -#define GOOGLE_SEARCH_URL "https://www.google.com/search?q=%1&ie=utf-8&oe=utf-8" -#define GOOGLE_SUGGEST_URL "http://suggestqueries.google.com/complete/search?output=toolbar&hl=en&q=%1" -#define ENCRYPTION_FILE_NAME "key.private" -#define RELOAD_MODEL_BORDER_NUM 10 - -#define MAX_ZOOM_FACTOR 5.0f -#define MIN_ZOOM_FACTOR 0.25f -#define DEFAULT_ZOOM_FACTOR 1.0f -#define ZOOM_FACTOR_STEP 0.1f - -#define INTERNAL_URL_MESSAGE "http://rssguard.message" -#define INTERNAL_URL_BLANK "http://rssguard.blank" -#define INTERNAL_URL_MESSAGE_HOST "rssguard.message" -#define INTERNAL_URL_BLANK_HOST "rssguard.blank" - -#define FEED_INITIAL_OPML_PATTERN "feeds-%1.opml" - -#define FEED_REGEX_MATCHER "]+type=\\\"application/(atom|rss)\\+xml\\\"[^>]*>" -#define FEED_HREF_REGEX_MATCHER "href\\=\\\"[^\\\"]+\\\"" - -#define PLACEHOLDER_UNREAD_COUNTS "%unread" -#define PLACEHOLDER_ALL_COUNTS "%all" - -#define BACKUP_NAME_SETTINGS "config" -#define BACKUP_SUFFIX_SETTINGS ".ini.backup" -#define BACKUP_NAME_DATABASE "database" -#define BACKUP_SUFFIX_DATABASE ".db.backup" - -#define APP_DB_MYSQL_DRIVER "QMYSQL" -#define APP_DB_MYSQL_INIT "db_init_mysql.sql" -#define APP_DB_MYSQL_TEST "MySQLTest" -#define APP_DB_MYSQL_PORT 3306 - -#define APP_DB_SQLITE_DRIVER "QSQLITE" -#define APP_DB_SQLITE_INIT "db_init_sqlite.sql" -#define APP_DB_SQLITE_PATH "database/local" -#define APP_DB_SQLITE_FILE "database.db" - -// Keep this in sync with schema versions declared in SQL initialization code. -#define APP_DB_SCHEMA_VERSION "8" -#define APP_DB_UPDATE_FILE_PATTERN "db_update_%1_%2_%3.sql" -#define APP_DB_COMMENT_SPLIT "-- !\n" -#define APP_DB_NAME_PLACEHOLDER "##" - -#define APP_CFG_PATH "config" -#define APP_CFG_FILE "config.ini" - -#define APP_QUIT_INSTANCE "-q" -#define APP_IS_RUNNING "app_is_running" -#define APP_SKIN_USER_FOLDER "skins" -#define APP_SKIN_DEFAULT "vergilius" -#define APP_SKIN_METADATA_FILE "metadata.xml" -#define APP_STYLE_DEFAULT "Fusion" -#define APP_THEME_DEFAULT "Faenza" -#define APP_NO_THEME "" -#define APP_THEME_SUFFIX ".png" - -#ifndef QSL -// Thin macro wrapper for literal strings. -// They are much more memory efficient and faster. -// Use it for all literals except for two cases: -// a) Methods which take QLatin1String (use QLatin1String for literal argument too), -// b) Construction of empty literals "", use QString() instead of QStringLiteral(""). -#define QSL(x) QStringLiteral(x) -#endif - -#ifndef QL1S -// Macro for latin strings. Latin strings are -// faster than QStrings created from literals. -#define QL1S(x) QLatin1String(x) -#endif - -#ifndef QL1C -// Macro for latin chars. -#define QL1C(x) QLatin1Char(x) -#endif - -// Indexes of columns as they are DEFINED IN THE TABLE for MESSAGES. -#define MSG_DB_ID_INDEX 0 -#define MSG_DB_READ_INDEX 1 -#define MSG_DB_DELETED_INDEX 2 -#define MSG_DB_IMPORTANT_INDEX 3 -#define MSG_DB_FEED_TITLE_INDEX 4 -#define MSG_DB_TITLE_INDEX 5 -#define MSG_DB_URL_INDEX 6 -#define MSG_DB_AUTHOR_INDEX 7 -#define MSG_DB_DCREATED_INDEX 8 -#define MSG_DB_CONTENTS_INDEX 9 -#define MSG_DB_PDELETED_INDEX 10 -#define MSG_DB_ENCLOSURES_INDEX 11 -#define MSG_DB_ACCOUNT_ID_INDEX 12 -#define MSG_DB_CUSTOM_ID_INDEX 13 -#define MSG_DB_CUSTOM_HASH_INDEX 14 -#define MSG_DB_FEED_CUSTOM_ID_INDEX 15 - -// Indexes of columns as they are DEFINED IN THE TABLE for CATEGORIES. -#define CAT_DB_ID_INDEX 0 -#define CAT_DB_PARENT_ID_INDEX 1 -#define CAT_DB_TITLE_INDEX 2 -#define CAT_DB_DESCRIPTION_INDEX 3 -#define CAT_DB_DCREATED_INDEX 4 -#define CAT_DB_ICON_INDEX 5 -#define CAT_DB_ACCOUNT_ID_INDEX 6 -#define CAT_DB_CUSTOM_ID_INDEX 7 - -// Indexes of columns as they are DEFINED IN THE TABLE for FEEDS. -#define FDS_DB_ID_INDEX 0 -#define FDS_DB_TITLE_INDEX 1 -#define FDS_DB_DESCRIPTION_INDEX 2 -#define FDS_DB_DCREATED_INDEX 3 -#define FDS_DB_ICON_INDEX 4 -#define FDS_DB_CATEGORY_INDEX 5 -#define FDS_DB_ENCODING_INDEX 6 -#define FDS_DB_URL_INDEX 7 -#define FDS_DB_PROTECTED_INDEX 8 -#define FDS_DB_USERNAME_INDEX 9 -#define FDS_DB_PASSWORD_INDEX 10 -#define FDS_DB_UPDATE_TYPE_INDEX 11 -#define FDS_DB_UPDATE_INTERVAL_INDEX 12 -#define FDS_DB_TYPE_INDEX 13 -#define FDS_DB_ACCOUNT_ID_INDEX 14 -#define FDS_DB_CUSTOM_ID_INDEX 15 - -// Indexes of columns for feed models. -#define FDS_MODEL_TITLE_INDEX 0 -#define FDS_MODEL_COUNTS_INDEX 1 - -#if defined(Q_OS_LINUX) -#define OS_ID "Linux" -#elif defined(Q_OS_OSX) -#define OS_ID "Mac OS X" -#elif defined(Q_OS_WIN) -#define OS_ID "Windows" -#else -#define OS_ID "" -#endif - -#if defined(Q_OS_LINUX) - -#define APP_DESKTOP_SOURCE_ENTRY_FILE "rssguard.desktop.autostart" -#define APP_DESKTOP_ENTRY_FILE "rssguard.desktop" - -#define APP_DESKTOP_ENTRY_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/autostart") -#define APP_LANG_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/translations") -#define APP_SKIN_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/skins") -#define APP_INFO_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/information") -#define APP_THEME_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/icons") -#define APP_SQL_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/sql") -#define APP_ICON_PATH QApplication::applicationDirPath() + QString("/../share/pixmaps/rssguard.png") -#define APP_ICON_PLAIN_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/icons/rssguard_plain.png") -#define APP_INITIAL_FEEDS_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/initial_feeds") - -#elif defined(Q_OS_OSX) - -#define APP_LANG_PATH QApplication::applicationDirPath() + QString("/../Resources/translations") -#define APP_SKIN_PATH QApplication::applicationDirPath() + QString("/../Resources/skins") -#define APP_INFO_PATH QApplication::applicationDirPath() + QString("/../Resources/information") -#define APP_THEME_PATH QApplication::applicationDirPath() + QString("/../Resources/icons") -#define APP_SQL_PATH QApplication::applicationDirPath() + QString("/../Resources/sql") -#define APP_ICON_PATH QApplication::applicationDirPath() + QString("/../Resources/icons/rssguard.png") -#define APP_INFO_PATH QApplication::applicationDirPath() + QString("/../Resources/information") -#define APP_ICON_PLAIN_PATH QApplication::applicationDirPath() + QString("/../Resources/icons/rssguard_plain.png") -#define APP_INITIAL_FEEDS_PATH QApplication::applicationDirPath() + QString("/../Resources/initial_feeds") - -#elif defined(Q_OS_WIN) - -#define APP_LANG_PATH QApplication::applicationDirPath() + QString("/translations") -#define APP_SKIN_PATH QApplication::applicationDirPath() + QString("/skins") -#define APP_INFO_PATH QApplication::applicationDirPath() -#define APP_THEME_PATH QApplication::applicationDirPath() + QString("/icons") -#define APP_SQL_PATH QApplication::applicationDirPath() + QString("/sql") -#define APP_ICON_PATH QApplication::applicationDirPath() + QString("/rssguard.png") -#define APP_ICON_PLAIN_PATH QApplication::applicationDirPath() + QString("/rssguard_plain.png") -#define APP_INITIAL_FEEDS_PATH QApplication::applicationDirPath() + QString("/initial_feeds") - -#endif - -#endif // DEFINITIONS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef DEFINITIONS_H +#define DEFINITIONS_H + +#include + +#define SERVICE_CODE_STD_RSS "std-rss" +#define SERVICE_CODE_TT_RSS "tt-rss" +#define SERVICE_CODE_OWNCLOUD "owncloud" + +#define ARGUMENTS_LIST_SEPARATOR "\n" + +#define ADBLOCK_UPDATE_DAYS_INTERVAL 5 +#define ADBLOCK_ICON_ACTIVE "adblock" +#define ADBLOCK_ICON_DISABLED "adblock-disabled" +#define IS_IN_ARRAY(offset, array) ((offset >= 0) && (offset < array.count())) +#define ADBLOCK_CUSTOMLIST_NAME "customlist.txt" +#define ADBLOCK_LISTS_SUBDIRECTORY "adblock" +#define ADBLOCK_EASYLIST_URL "https://easylist-downloads.adblockplus.org/easylist.txt" +#define DEFAULT_SQL_MESSAGES_FILTER "0 > 1" +#define MAX_MULTICOLUMN_SORT_STATES 3 +#define ENCLOSURES_OUTER_SEPARATOR '#' +#define ECNLOSURES_INNER_SEPARATOR '&' +#define URI_SCHEME_FEED_SHORT "feed:" +#define URI_SCHEME_FEED "feed://" +#define URI_SCHEME_HTTP "http://" +#define RELEASES_LIST "https://api.github.com/repos/martinrotter/rssguard/releases" +#define DEFAULT_LOCALE "en" +#define DEFAULT_FEED_ENCODING "UTF-8" +#define DEFAULT_FEED_TYPE "RSS" +#define URL_REGEXP "^(http|https|feed|ftp):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?$" +#define USER_AGENT_HTTP_HEADER "User-Agent" +#define TEXT_TITLE_LIMIT 30 +#define RESELECT_MESSAGE_THRESSHOLD 500 +#define ICON_SIZE_SETTINGS 16 +#define NO_PARENT_CATEGORY -1 +#define ID_RECYCLE_BIN -2 +#define TRAY_ICON_BUBBLE_TIMEOUT 20000 +#define CLOSE_LOCK_TIMEOUT 500 +#define DOWNLOAD_TIMEOUT 5000 +#define MESSAGES_VIEW_DEFAULT_COL 170 +#define MESSAGES_VIEW_MINIMUM_COL 36 +#define FEEDS_VIEW_COLUMN_COUNT 2 +#define FEED_DOWNLOADER_MAX_THREADS 6 +#define DEFAULT_DAYS_TO_DELETE_MSG 14 +#define ELLIPSIS_LENGTH 3 +#define MIN_CATEGORY_NAME_LENGTH 1 +#define DEFAULT_AUTO_UPDATE_INTERVAL 15 +#define AUTO_UPDATE_INTERVAL 60000 +#define STARTUP_UPDATE_DELAY 30000 +#define TIMEZONE_OFFSET_LIMIT 6 +#define CHANGE_EVENT_DELAY 250 +#define FLAG_ICON_SUBFOLDER "flags" +#define SEACRH_MESSAGES_ACTION_NAME "search" +#define HIGHLIGHTER_ACTION_NAME "highlighter" +#define SPACER_ACTION_NAME "spacer" +#define SEPARATOR_ACTION_NAME "separator" +#define FILTER_WIDTH 150 +#define FILTER_RIGHT_MARGIN 5 +#define FEEDS_VIEW_INDENTATION 10 +#define ACCEPT_HEADER_FOR_FEED_DOWNLOADER "application/atom+xml,application/xml;q=0.9,text/xml;q=0.8,*/*;q=0.7" +#define MIME_TYPE_ITEM_POINTER "rssguard/itempointer" +#define DOWNLOADER_ICON_SIZE 48 +#define NOTIFICATION_ICON_SIZE 32 +#define GOOGLE_SEARCH_URL "https://www.google.com/search?q=%1&ie=utf-8&oe=utf-8" +#define GOOGLE_SUGGEST_URL "http://suggestqueries.google.com/complete/search?output=toolbar&hl=en&q=%1" +#define ENCRYPTION_FILE_NAME "key.private" +#define RELOAD_MODEL_BORDER_NUM 10 + +#define MAX_ZOOM_FACTOR 5.0f +#define MIN_ZOOM_FACTOR 0.25f +#define DEFAULT_ZOOM_FACTOR 1.0f +#define ZOOM_FACTOR_STEP 0.1f + +#define INTERNAL_URL_MESSAGE "http://rssguard.message" +#define INTERNAL_URL_BLANK "http://rssguard.blank" +#define INTERNAL_URL_MESSAGE_HOST "rssguard.message" +#define INTERNAL_URL_BLANK_HOST "rssguard.blank" + +#define FEED_INITIAL_OPML_PATTERN "feeds-%1.opml" + +#define FEED_REGEX_MATCHER "]+type=\\\"application/(atom|rss)\\+xml\\\"[^>]*>" +#define FEED_HREF_REGEX_MATCHER "href\\=\\\"[^\\\"]+\\\"" + +#define PLACEHOLDER_UNREAD_COUNTS "%unread" +#define PLACEHOLDER_ALL_COUNTS "%all" + +#define BACKUP_NAME_SETTINGS "config" +#define BACKUP_SUFFIX_SETTINGS ".ini.backup" +#define BACKUP_NAME_DATABASE "database" +#define BACKUP_SUFFIX_DATABASE ".db.backup" + +#define APP_DB_MYSQL_DRIVER "QMYSQL" +#define APP_DB_MYSQL_INIT "db_init_mysql.sql" +#define APP_DB_MYSQL_TEST "MySQLTest" +#define APP_DB_MYSQL_PORT 3306 + +#define APP_DB_SQLITE_DRIVER "QSQLITE" +#define APP_DB_SQLITE_INIT "db_init_sqlite.sql" +#define APP_DB_SQLITE_PATH "database/local" +#define APP_DB_SQLITE_FILE "database.db" + +// Keep this in sync with schema versions declared in SQL initialization code. +#define APP_DB_SCHEMA_VERSION "8" +#define APP_DB_UPDATE_FILE_PATTERN "db_update_%1_%2_%3.sql" +#define APP_DB_COMMENT_SPLIT "-- !\n" +#define APP_DB_NAME_PLACEHOLDER "##" + +#define APP_CFG_PATH "config" +#define APP_CFG_FILE "config.ini" + +#define APP_QUIT_INSTANCE "-q" +#define APP_IS_RUNNING "app_is_running" +#define APP_SKIN_USER_FOLDER "skins" +#define APP_SKIN_DEFAULT "vergilius" +#define APP_SKIN_METADATA_FILE "metadata.xml" +#define APP_STYLE_DEFAULT "Fusion" +#define APP_THEME_DEFAULT "Faenza" +#define APP_NO_THEME "" +#define APP_THEME_SUFFIX ".png" + +#ifndef QSL +// Thin macro wrapper for literal strings. +// They are much more memory efficient and faster. +// Use it for all literals except for two cases: +// a) Methods which take QLatin1String (use QLatin1String for literal argument too), +// b) Construction of empty literals "", use QString() instead of QStringLiteral(""). +#define QSL(x) QStringLiteral(x) +#endif + +#ifndef QL1S +// Macro for latin strings. Latin strings are +// faster than QStrings created from literals. +#define QL1S(x) QLatin1String(x) +#endif + +#ifndef QL1C +// Macro for latin chars. +#define QL1C(x) QLatin1Char(x) +#endif + +// Indexes of columns as they are DEFINED IN THE TABLE for MESSAGES. +#define MSG_DB_ID_INDEX 0 +#define MSG_DB_READ_INDEX 1 +#define MSG_DB_DELETED_INDEX 2 +#define MSG_DB_IMPORTANT_INDEX 3 +#define MSG_DB_FEED_TITLE_INDEX 4 +#define MSG_DB_TITLE_INDEX 5 +#define MSG_DB_URL_INDEX 6 +#define MSG_DB_AUTHOR_INDEX 7 +#define MSG_DB_DCREATED_INDEX 8 +#define MSG_DB_CONTENTS_INDEX 9 +#define MSG_DB_PDELETED_INDEX 10 +#define MSG_DB_ENCLOSURES_INDEX 11 +#define MSG_DB_ACCOUNT_ID_INDEX 12 +#define MSG_DB_CUSTOM_ID_INDEX 13 +#define MSG_DB_CUSTOM_HASH_INDEX 14 +#define MSG_DB_FEED_CUSTOM_ID_INDEX 15 + +// Indexes of columns as they are DEFINED IN THE TABLE for CATEGORIES. +#define CAT_DB_ID_INDEX 0 +#define CAT_DB_PARENT_ID_INDEX 1 +#define CAT_DB_TITLE_INDEX 2 +#define CAT_DB_DESCRIPTION_INDEX 3 +#define CAT_DB_DCREATED_INDEX 4 +#define CAT_DB_ICON_INDEX 5 +#define CAT_DB_ACCOUNT_ID_INDEX 6 +#define CAT_DB_CUSTOM_ID_INDEX 7 + +// Indexes of columns as they are DEFINED IN THE TABLE for FEEDS. +#define FDS_DB_ID_INDEX 0 +#define FDS_DB_TITLE_INDEX 1 +#define FDS_DB_DESCRIPTION_INDEX 2 +#define FDS_DB_DCREATED_INDEX 3 +#define FDS_DB_ICON_INDEX 4 +#define FDS_DB_CATEGORY_INDEX 5 +#define FDS_DB_ENCODING_INDEX 6 +#define FDS_DB_URL_INDEX 7 +#define FDS_DB_PROTECTED_INDEX 8 +#define FDS_DB_USERNAME_INDEX 9 +#define FDS_DB_PASSWORD_INDEX 10 +#define FDS_DB_UPDATE_TYPE_INDEX 11 +#define FDS_DB_UPDATE_INTERVAL_INDEX 12 +#define FDS_DB_TYPE_INDEX 13 +#define FDS_DB_ACCOUNT_ID_INDEX 14 +#define FDS_DB_CUSTOM_ID_INDEX 15 + +// Indexes of columns for feed models. +#define FDS_MODEL_TITLE_INDEX 0 +#define FDS_MODEL_COUNTS_INDEX 1 + +#if defined(Q_OS_LINUX) +#define OS_ID "Linux" +#elif defined(Q_OS_OSX) +#define OS_ID "Mac OS X" +#elif defined(Q_OS_WIN) +#define OS_ID "Windows" +#else +#define OS_ID "" +#endif + +#if defined(Q_OS_LINUX) + +#define APP_DESKTOP_SOURCE_ENTRY_FILE "rssguard.desktop.autostart" +#define APP_DESKTOP_ENTRY_FILE "rssguard.desktop" + +#define APP_DESKTOP_ENTRY_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/autostart") +#define APP_LANG_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/translations") +#define APP_SKIN_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/skins") +#define APP_INFO_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/information") +#define APP_THEME_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/icons") +#define APP_SQL_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/sql") +#define APP_ICON_PATH QApplication::applicationDirPath() + QString("/../share/pixmaps/rssguard.png") +#define APP_ICON_PLAIN_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/icons/rssguard_plain.png") +#define APP_INITIAL_FEEDS_PATH QApplication::applicationDirPath() + QString("/../share/rssguard/initial_feeds") + +#elif defined(Q_OS_OSX) + +#define APP_LANG_PATH QApplication::applicationDirPath() + QString("/../Resources/translations") +#define APP_SKIN_PATH QApplication::applicationDirPath() + QString("/../Resources/skins") +#define APP_INFO_PATH QApplication::applicationDirPath() + QString("/../Resources/information") +#define APP_THEME_PATH QApplication::applicationDirPath() + QString("/../Resources/icons") +#define APP_SQL_PATH QApplication::applicationDirPath() + QString("/../Resources/sql") +#define APP_ICON_PATH QApplication::applicationDirPath() + QString("/../Resources/icons/rssguard.png") +#define APP_INFO_PATH QApplication::applicationDirPath() + QString("/../Resources/information") +#define APP_ICON_PLAIN_PATH QApplication::applicationDirPath() + QString("/../Resources/icons/rssguard_plain.png") +#define APP_INITIAL_FEEDS_PATH QApplication::applicationDirPath() + QString("/../Resources/initial_feeds") + +#elif defined(Q_OS_WIN) + +#define APP_LANG_PATH QApplication::applicationDirPath() + QString("/translations") +#define APP_SKIN_PATH QApplication::applicationDirPath() + QString("/skins") +#define APP_INFO_PATH QApplication::applicationDirPath() +#define APP_THEME_PATH QApplication::applicationDirPath() + QString("/icons") +#define APP_SQL_PATH QApplication::applicationDirPath() + QString("/sql") +#define APP_ICON_PATH QApplication::applicationDirPath() + QString("/rssguard.png") +#define APP_ICON_PLAIN_PATH QApplication::applicationDirPath() + QString("/rssguard_plain.png") +#define APP_INITIAL_FEEDS_PATH QApplication::applicationDirPath() + QString("/initial_feeds") + +#endif + +#endif // DEFINITIONS_H diff --git a/src/dynamic-shortcuts/dynamicshortcuts.cpp b/src/dynamic-shortcuts/dynamicshortcuts.cpp index 68c007e41..7898e1308 100755 --- a/src/dynamic-shortcuts/dynamicshortcuts.cpp +++ b/src/dynamic-shortcuts/dynamicshortcuts.cpp @@ -27,21 +27,21 @@ DynamicShortcuts::DynamicShortcuts() { } -void DynamicShortcuts::save(const QList &actions) { - Settings *settings = qApp->settings(); +void DynamicShortcuts::save(const QList& actions) { + Settings* settings = qApp->settings(); - foreach (const QAction *action, actions) { - settings->setValue(GROUP(Keyboard), action->objectName(), action->shortcut().toString(QKeySequence::PortableText)); - } + foreach (const QAction* action, actions) { + settings->setValue(GROUP(Keyboard), action->objectName(), action->shortcut().toString(QKeySequence::PortableText)); + } } -void DynamicShortcuts::load(const QList &actions) { - Settings *settings = qApp->settings(); +void DynamicShortcuts::load(const QList& actions) { + Settings* settings = qApp->settings(); - foreach (QAction *action, actions) { - QString shortcut_for_action = settings->value(GROUP(Keyboard), - action->objectName(), - action->shortcut().toString(QKeySequence::PortableText)).toString(); - action->setShortcut(QKeySequence::fromString(shortcut_for_action, QKeySequence::PortableText)); - } + foreach (QAction* action, actions) { + QString shortcut_for_action = settings->value(GROUP(Keyboard), + action->objectName(), + action->shortcut().toString(QKeySequence::PortableText)).toString(); + action->setShortcut(QKeySequence::fromString(shortcut_for_action, QKeySequence::PortableText)); + } } diff --git a/src/dynamic-shortcuts/dynamicshortcuts.h b/src/dynamic-shortcuts/dynamicshortcuts.h index c42984e13..23026399c 100755 --- a/src/dynamic-shortcuts/dynamicshortcuts.h +++ b/src/dynamic-shortcuts/dynamicshortcuts.h @@ -24,18 +24,18 @@ class QAction; class DynamicShortcuts { - public: - // Checks the application settings and then initializes shortcut of - // each action from actions from the settings. - static void load(const QList &actions); + public: + // Checks the application settings and then initializes shortcut of + // each action from actions from the settings. + static void load(const QList& actions); - // Stores shortcut of each action from actions into the application - // settings. - static void save(const QList &actions); + // Stores shortcut of each action from actions into the application + // settings. + static void save(const QList& actions); - private: - // Constructor. - explicit DynamicShortcuts(); + private: + // Constructor. + explicit DynamicShortcuts(); }; #endif // DYNAMICSHORTCUTS_H diff --git a/src/dynamic-shortcuts/dynamicshortcutswidget.cpp b/src/dynamic-shortcuts/dynamicshortcutswidget.cpp index 032e48322..289d03b60 100755 --- a/src/dynamic-shortcuts/dynamicshortcutswidget.cpp +++ b/src/dynamic-shortcuts/dynamicshortcutswidget.cpp @@ -26,93 +26,85 @@ #include -DynamicShortcutsWidget::DynamicShortcutsWidget(QWidget *parent) : QWidget(parent) { - // Create layout for this control and set is as active. - m_layout = new QGridLayout(this); - m_layout->setMargin(0); - - setLayout(m_layout); +DynamicShortcutsWidget::DynamicShortcutsWidget(QWidget* parent) : QWidget(parent) { + // Create layout for this control and set is as active. + m_layout = new QGridLayout(this); + m_layout->setMargin(0); + setLayout(m_layout); } DynamicShortcutsWidget::~DynamicShortcutsWidget() { - delete m_layout; + delete m_layout; } bool DynamicShortcutsWidget::areShortcutsUnique() const { - QList all_shortcuts; + QList all_shortcuts; - // Obtain all shortcuts. - foreach (const ActionBinding &binding, m_actionBindings) { - const QKeySequence new_shortcut = binding.second->shortcut(); + // Obtain all shortcuts. + foreach (const ActionBinding& binding, m_actionBindings) { + const QKeySequence new_shortcut = binding.second->shortcut(); - if (!new_shortcut.isEmpty() && all_shortcuts.contains(new_shortcut)) { - // Problem, two identical non-empty shortcuts found. - return false; - } - else { - all_shortcuts.append(binding.second->shortcut()); - } - } + if (!new_shortcut.isEmpty() && all_shortcuts.contains(new_shortcut)) { + // Problem, two identical non-empty shortcuts found. + return false; + } - return true; + else { + all_shortcuts.append(binding.second->shortcut()); + } + } + + return true; } void DynamicShortcutsWidget::updateShortcuts() { - foreach (const ActionBinding &binding, m_actionBindings) { - binding.first->setShortcut(binding.second->shortcut()); - } + foreach (const ActionBinding& binding, m_actionBindings) { + binding.first->setShortcut(binding.second->shortcut()); + } } void DynamicShortcutsWidget::populate(QList actions) { - m_actionBindings.clear(); - qSort(actions.begin(), actions.end(), DynamicShortcutsWidget::lessThan); + m_actionBindings.clear(); + qSort(actions.begin(), actions.end(), DynamicShortcutsWidget::lessThan); + int row_id = 0; - int row_id = 0; + // FIXME: Maybe separate actions into "categories". Each category will start with label. + // I will assign each QAaction a property called "category" with some enum value. + // Like: + // File, FeedsCategories, Messages, Tools, WebBrowser, Help + // This will be setup in FormMain::allActions(). + // Then here I will process actions into categories. - // FIXME: Maybe separate actions into "categories". Each category will start with label. - // I will assign each QAaction a property called "category" with some enum value. - // Like: - // File, FeedsCategories, Messages, Tools, WebBrowser, Help - // This will be setup in FormMain::allActions(). - // Then here I will process actions into categories. + foreach (QAction* action, actions) { + // Create shortcut catcher for this action and set default shortcut. + ShortcutCatcher* catcher = new ShortcutCatcher(this); + catcher->setDefaultShortcut(action->shortcut()); + // Store information for re-initialization of shortcuts + // of actions when widget gets "confirmed". + QPair new_binding; + new_binding.first = action; + new_binding.second = catcher; + m_actionBindings << new_binding; + // Add new catcher to our control. + QLabel* action_label = new QLabel(this); + action_label->setText(action->text().remove(QSL("&"))); + action_label->setToolTip(action->toolTip()); + action_label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + QLabel* action_icon = new QLabel(this); + action_icon->setPixmap(action->icon().pixmap(ICON_SIZE_SETTINGS, ICON_SIZE_SETTINGS)); + action_icon->setToolTip(action->toolTip()); + m_layout->addWidget(action_icon, row_id, 0); + m_layout->addWidget(action_label, row_id, 1); + m_layout->addWidget(catcher, row_id, 2); + row_id++; + connect(catcher, &ShortcutCatcher::shortcutChanged, this, &DynamicShortcutsWidget::setupChanged); + } - foreach (QAction *action, actions) { - // Create shortcut catcher for this action and set default shortcut. - ShortcutCatcher *catcher = new ShortcutCatcher(this); - catcher->setDefaultShortcut(action->shortcut()); - - // Store information for re-initialization of shortcuts - // of actions when widget gets "confirmed". - QPair new_binding; - new_binding.first = action; - new_binding.second = catcher; - - m_actionBindings << new_binding; - - // Add new catcher to our control. - QLabel *action_label = new QLabel(this); - action_label->setText(action->text().remove(QSL("&"))); - action_label->setToolTip(action->toolTip()); - action_label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); - - QLabel *action_icon = new QLabel(this); - action_icon->setPixmap(action->icon().pixmap(ICON_SIZE_SETTINGS, ICON_SIZE_SETTINGS)); - action_icon->setToolTip(action->toolTip()); - - m_layout->addWidget(action_icon, row_id, 0); - m_layout->addWidget(action_label, row_id, 1); - m_layout->addWidget(catcher, row_id, 2); - - row_id++; - - connect(catcher, &ShortcutCatcher::shortcutChanged, this, &DynamicShortcutsWidget::setupChanged); - } - - // Make sure that "spacer" is added. - m_layout->setRowStretch(row_id, 1); - m_layout->setColumnStretch(1, 1); + // Make sure that "spacer" is added. + m_layout->setRowStretch(row_id, 1); + m_layout->setColumnStretch(1, 1); } -bool DynamicShortcutsWidget::lessThan(QAction *lhs, QAction *rhs) { - return QString::localeAwareCompare(lhs->text().replace(QL1S("&"), QString()), rhs->text().replace(QL1S("&"), QString())) < 0; +bool DynamicShortcutsWidget::lessThan(QAction* lhs, QAction* rhs) { + return QString::localeAwareCompare(lhs->text().replace(QL1S("&"), QString()), rhs->text().replace(QL1S("&"), QString())) < 0; } diff --git a/src/dynamic-shortcuts/dynamicshortcutswidget.h b/src/dynamic-shortcuts/dynamicshortcutswidget.h index 6836935a9..9beb77b90 100755 --- a/src/dynamic-shortcuts/dynamicshortcutswidget.h +++ b/src/dynamic-shortcuts/dynamicshortcutswidget.h @@ -27,38 +27,38 @@ class ShortcutCatcher; typedef QPair ActionBinding; class DynamicShortcutsWidget : public QWidget { - Q_OBJECT - - public: - // Constructors and destructors. - explicit DynamicShortcutsWidget(QWidget *parent = 0); - virtual ~DynamicShortcutsWidget(); + Q_OBJECT - // Updates shortcuts of all actions according to changes. - // NOTE: No access to settings is done here. - // Shortcuts are fetched from settings when applications starts - // and stored back to settings when application quits. - void updateShortcuts(); + public: + // Constructors and destructors. + explicit DynamicShortcutsWidget(QWidget* parent = 0); + virtual ~DynamicShortcutsWidget(); - // Returns true if all shortcuts are unique, - // otherwise false. - bool areShortcutsUnique() const; + // Updates shortcuts of all actions according to changes. + // NOTE: No access to settings is done here. + // Shortcuts are fetched from settings when applications starts + // and stored back to settings when application quits. + void updateShortcuts(); - // Populates this widget with shortcut widgets for given actions. - // NOTE: This gets initial shortcut for each action from its properties, NOT from - // the application settings, so shortcuts from settings need to be - // assigned to actions before calling this method. - void populate(QList actions); + // Returns true if all shortcuts are unique, + // otherwise false. + bool areShortcutsUnique() const; - signals: - void setupChanged(); + // Populates this widget with shortcut widgets for given actions. + // NOTE: This gets initial shortcut for each action from its properties, NOT from + // the application settings, so shortcuts from settings need to be + // assigned to actions before calling this method. + void populate(QList actions); - private: - static bool lessThan(QAction *lhs, QAction *rhs); + signals: + void setupChanged(); - private: - QGridLayout *m_layout; - QList m_actionBindings; + private: + static bool lessThan(QAction* lhs, QAction* rhs); + + private: + QGridLayout* m_layout; + QList m_actionBindings; }; #endif // DYNAMICSHORTCUTSOVERVIEW_H diff --git a/src/dynamic-shortcuts/shortcutbutton.cpp b/src/dynamic-shortcuts/shortcutbutton.cpp index 8550abb33..f17d405c9 100755 --- a/src/dynamic-shortcuts/shortcutbutton.cpp +++ b/src/dynamic-shortcuts/shortcutbutton.cpp @@ -50,93 +50,94 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -ShortcutButton::ShortcutButton(ShortcutCatcher *catcher, QWidget *parent) - : QPushButton(parent), m_catcher(catcher) { - setMinimumWidth(100); +ShortcutButton::ShortcutButton(ShortcutCatcher* catcher, QWidget* parent) + : QPushButton(parent), m_catcher(catcher) { + setMinimumWidth(100); } ShortcutButton::~ShortcutButton() { } -void ShortcutButton::keyPressEvent(QKeyEvent *event) { - int pressed_key = event->key(); +void ShortcutButton::keyPressEvent(QKeyEvent* event) { + int pressed_key = event->key(); - if (pressed_key == -1) { - m_catcher->doneRecording(); - } + if (pressed_key == -1) { + m_catcher->doneRecording(); + } - const Qt::KeyboardModifiers new_modifiers = event->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META); + const Qt::KeyboardModifiers new_modifiers = event->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META); - if (!m_catcher->m_isRecording && (pressed_key == Qt::Key_Return || pressed_key == Qt::Key_Space)) { - return; - } + if (!m_catcher->m_isRecording && (pressed_key == Qt::Key_Return || pressed_key == Qt::Key_Space)) { + return; + } - if (!m_catcher->m_isRecording) { - QPushButton::keyPressEvent(event); - return; - } + if (!m_catcher->m_isRecording) { + QPushButton::keyPressEvent(event); + return; + } - event->accept(); - m_catcher->m_modifierKeys = new_modifiers; + event->accept(); + m_catcher->m_modifierKeys = new_modifiers; - switch(pressed_key) { - case Qt::Key_AltGr: - return; + switch (pressed_key) { + case Qt::Key_AltGr: + return; - case Qt::Key_Shift: - case Qt::Key_Control: - case Qt::Key_Alt: - case Qt::Key_Meta: - case Qt::Key_Menu: - m_catcher->controlModifierlessTimout(); - m_catcher->updateDisplayShortcut(); - break; + case Qt::Key_Shift: + case Qt::Key_Control: + case Qt::Key_Alt: + case Qt::Key_Meta: + case Qt::Key_Menu: + m_catcher->controlModifierlessTimout(); + m_catcher->updateDisplayShortcut(); + break; - default: - // We now have a valid key press. - if (pressed_key) { - if ((pressed_key == Qt::Key_Backtab) && (m_catcher->m_modifierKeys & Qt::SHIFT)) { - pressed_key = Qt::Key_Tab | m_catcher->m_modifierKeys; - } - else { - pressed_key |= m_catcher->m_modifierKeys; - } + default: - if (m_catcher->m_numKey == 0) { - m_catcher->m_currentSequence = QKeySequence(pressed_key); - } + // We now have a valid key press. + if (pressed_key) { + if ((pressed_key == Qt::Key_Backtab) && (m_catcher->m_modifierKeys & Qt::SHIFT)) { + pressed_key = Qt::Key_Tab | m_catcher->m_modifierKeys; + } - m_catcher->m_numKey++; + else { + pressed_key |= m_catcher->m_modifierKeys; + } - if (m_catcher->m_numKey >= 4) { - m_catcher->doneRecording(); - return; - } + if (m_catcher->m_numKey == 0) { + m_catcher->m_currentSequence = QKeySequence(pressed_key); + } - m_catcher->controlModifierlessTimout(); - m_catcher->updateDisplayShortcut(); - } - } + m_catcher->m_numKey++; + + if (m_catcher->m_numKey >= 4) { + m_catcher->doneRecording(); + return; + } + + m_catcher->controlModifierlessTimout(); + m_catcher->updateDisplayShortcut(); + } + } } -void ShortcutButton::keyReleaseEvent(QKeyEvent *event) { - if (event->key() == -1){ - return; - } +void ShortcutButton::keyReleaseEvent(QKeyEvent* event) { + if (event->key() == -1) { + return; + } - if (!m_catcher->m_isRecording) { - QPushButton::keyReleaseEvent(event); - return; - } + if (!m_catcher->m_isRecording) { + QPushButton::keyReleaseEvent(event); + return; + } - event->accept(); + event->accept(); + const Qt::KeyboardModifiers new_modifiers = event->modifiers() & + (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META); - const Qt::KeyboardModifiers new_modifiers = event->modifiers() & - (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META); - - if (((uint) new_modifiers & m_catcher->m_modifierKeys) < m_catcher->m_modifierKeys) { - m_catcher->m_modifierKeys = new_modifiers; - m_catcher->controlModifierlessTimout(); - m_catcher->updateDisplayShortcut(); - } + if (((uint) new_modifiers & m_catcher->m_modifierKeys) < m_catcher->m_modifierKeys) { + m_catcher->m_modifierKeys = new_modifiers; + m_catcher->controlModifierlessTimout(); + m_catcher->updateDisplayShortcut(); + } } diff --git a/src/dynamic-shortcuts/shortcutbutton.h b/src/dynamic-shortcuts/shortcutbutton.h index c42bbc1da..328ab3399 100755 --- a/src/dynamic-shortcuts/shortcutbutton.h +++ b/src/dynamic-shortcuts/shortcutbutton.h @@ -52,19 +52,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. class ShortcutCatcher; class ShortcutButton : public QPushButton { - Q_OBJECT + Q_OBJECT - public: - // Constructors and destructors. - explicit ShortcutButton(ShortcutCatcher *catcher, QWidget *parent = 0); - virtual ~ShortcutButton(); + public: + // Constructors and destructors. + explicit ShortcutButton(ShortcutCatcher* catcher, QWidget* parent = 0); + virtual ~ShortcutButton(); - protected: - void keyPressEvent(QKeyEvent *event); - void keyReleaseEvent(QKeyEvent *event); + protected: + void keyPressEvent(QKeyEvent* event); + void keyReleaseEvent(QKeyEvent* event); - private: - ShortcutCatcher *m_catcher; + private: + ShortcutCatcher* m_catcher; }; #endif // SHORTCUTBUTTON_H diff --git a/src/dynamic-shortcuts/shortcutcatcher.cpp b/src/dynamic-shortcuts/shortcutcatcher.cpp index 21562d3f8..261468fc8 100755 --- a/src/dynamic-shortcuts/shortcutcatcher.cpp +++ b/src/dynamic-shortcuts/shortcutcatcher.cpp @@ -52,123 +52,118 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -ShortcutCatcher::ShortcutCatcher(QWidget *parent) - : QWidget(parent) { - // Setup layout of the control - m_layout = new QHBoxLayout(this); - m_layout->setMargin(0); - m_layout->setSpacing(1); - - // Create reset button. - m_btnReset = new PlainToolButton(this); - m_btnReset->setIcon(qApp->icons()->fromTheme(QSL("document-revert"))); - m_btnReset->setFocusPolicy(Qt::NoFocus); - m_btnReset->setToolTip(tr("Reset to original shortcut.")); - - // Create clear button. - m_btnClear = new PlainToolButton(this); - m_btnClear->setIcon(qApp->icons()->fromTheme(QSL("list-remove"))); - m_btnClear->setFocusPolicy(Qt::NoFocus); - m_btnClear->setToolTip(tr("Clear current shortcut.")); - - // Clear main shortcut catching button. - m_btnChange = new ShortcutButton(this); - m_btnChange->setFocusPolicy(Qt::StrongFocus); - m_btnChange->setToolTip(tr("Click and hit new shortcut.")); - - // Add both buttons to the layout. - m_layout->addWidget(m_btnChange); - m_layout->addWidget(m_btnReset); - m_layout->addWidget(m_btnClear); - - // Establish needed connections. - connect(m_btnReset, &QToolButton::clicked, this, &ShortcutCatcher::resetShortcut); - connect(m_btnClear, &QToolButton::clicked, this, &ShortcutCatcher::clearShortcut); - connect(m_btnChange, &QToolButton::clicked, this, &ShortcutCatcher::startRecording); - - // Prepare initial state of the control. - updateDisplayShortcut(); +ShortcutCatcher::ShortcutCatcher(QWidget* parent) + : QWidget(parent) { + // Setup layout of the control + m_layout = new QHBoxLayout(this); + m_layout->setMargin(0); + m_layout->setSpacing(1); + // Create reset button. + m_btnReset = new PlainToolButton(this); + m_btnReset->setIcon(qApp->icons()->fromTheme(QSL("document-revert"))); + m_btnReset->setFocusPolicy(Qt::NoFocus); + m_btnReset->setToolTip(tr("Reset to original shortcut.")); + // Create clear button. + m_btnClear = new PlainToolButton(this); + m_btnClear->setIcon(qApp->icons()->fromTheme(QSL("list-remove"))); + m_btnClear->setFocusPolicy(Qt::NoFocus); + m_btnClear->setToolTip(tr("Clear current shortcut.")); + // Clear main shortcut catching button. + m_btnChange = new ShortcutButton(this); + m_btnChange->setFocusPolicy(Qt::StrongFocus); + m_btnChange->setToolTip(tr("Click and hit new shortcut.")); + // Add both buttons to the layout. + m_layout->addWidget(m_btnChange); + m_layout->addWidget(m_btnReset); + m_layout->addWidget(m_btnClear); + // Establish needed connections. + connect(m_btnReset, &QToolButton::clicked, this, &ShortcutCatcher::resetShortcut); + connect(m_btnClear, &QToolButton::clicked, this, &ShortcutCatcher::clearShortcut); + connect(m_btnChange, &QToolButton::clicked, this, &ShortcutCatcher::startRecording); + // Prepare initial state of the control. + updateDisplayShortcut(); } ShortcutCatcher::~ShortcutCatcher() { - delete m_btnReset; - delete m_btnChange; - delete m_btnClear; - delete m_layout; + delete m_btnReset; + delete m_btnChange; + delete m_btnClear; + delete m_layout; } void ShortcutCatcher::startRecording() { - m_numKey = 0; - m_modifierKeys = 0; - m_currentSequence = QKeySequence(); - m_isRecording = true; - m_btnChange->setDown(true); - m_btnChange->grabKeyboard(); - - updateDisplayShortcut(); + m_numKey = 0; + m_modifierKeys = 0; + m_currentSequence = QKeySequence(); + m_isRecording = true; + m_btnChange->setDown(true); + m_btnChange->grabKeyboard(); + updateDisplayShortcut(); } void ShortcutCatcher::doneRecording() { - m_isRecording = false; - m_btnChange->releaseKeyboard(); - m_btnChange->setDown(false); - - updateDisplayShortcut(); - - emit shortcutChanged(m_currentSequence); + m_isRecording = false; + m_btnChange->releaseKeyboard(); + m_btnChange->setDown(false); + updateDisplayShortcut(); + emit shortcutChanged(m_currentSequence); } void ShortcutCatcher::controlModifierlessTimout() { - if (m_numKey && !m_modifierKeys) { - doneRecording(); - } + if (m_numKey && !m_modifierKeys) { + doneRecording(); + } } void ShortcutCatcher::updateDisplayShortcut() { - QString str = m_currentSequence.toString(QKeySequence::NativeText); - str.replace(QL1S("&"), QL1S("&&")); + QString str = m_currentSequence.toString(QKeySequence::NativeText); + str.replace(QL1S("&"), QL1S("&&")); - if (m_isRecording) { - if (m_modifierKeys) { - if (!str.isEmpty()) { - str.append(QSL(",")); - } - if (m_modifierKeys & Qt::META) { - str += QL1S("Meta + "); - } - if (m_modifierKeys & Qt::CTRL) { - str += QL1S("Ctrl + "); - } - if (m_modifierKeys & Qt::ALT) { - str += QL1S("Alt + "); - } - if (m_modifierKeys & Qt::SHIFT) { - str += QL1S("Shift + "); - } - } - } + if (m_isRecording) { + if (m_modifierKeys) { + if (!str.isEmpty()) { + str.append(QSL(",")); + } - m_btnChange->setText(str); + if (m_modifierKeys & Qt::META) { + str += QL1S("Meta + "); + } + + if (m_modifierKeys & Qt::CTRL) { + str += QL1S("Ctrl + "); + } + + if (m_modifierKeys & Qt::ALT) { + str += QL1S("Alt + "); + } + + if (m_modifierKeys & Qt::SHIFT) { + str += QL1S("Shift + "); + } + } + } + + m_btnChange->setText(str); } QKeySequence ShortcutCatcher::shortcut() const { - return m_currentSequence; + return m_currentSequence; } -void ShortcutCatcher::setDefaultShortcut(const QKeySequence &key) { - m_defaultSequence = key; - setShortcut(key); +void ShortcutCatcher::setDefaultShortcut(const QKeySequence& key) { + m_defaultSequence = key; + setShortcut(key); } -void ShortcutCatcher::setShortcut(const QKeySequence &key) { - m_currentSequence = key; - doneRecording(); +void ShortcutCatcher::setShortcut(const QKeySequence& key) { + m_currentSequence = key; + doneRecording(); } void ShortcutCatcher::resetShortcut() { - setShortcut(m_defaultSequence); + setShortcut(m_defaultSequence); } void ShortcutCatcher::clearShortcut() { - setShortcut(QKeySequence()); + setShortcut(QKeySequence()); } diff --git a/src/dynamic-shortcuts/shortcutcatcher.h b/src/dynamic-shortcuts/shortcutcatcher.h index 6fbbda847..f1f80b2eb 100755 --- a/src/dynamic-shortcuts/shortcutcatcher.h +++ b/src/dynamic-shortcuts/shortcutcatcher.h @@ -54,45 +54,45 @@ class QToolButton; class ShortcutButton; class ShortcutCatcher : public QWidget { - Q_OBJECT + Q_OBJECT - friend class ShortcutButton; + friend class ShortcutButton; - public: - // Constructors and destructors. - explicit ShortcutCatcher(QWidget *parent = 0); - virtual ~ShortcutCatcher(); + public: + // Constructors and destructors. + explicit ShortcutCatcher(QWidget* parent = 0); + virtual ~ShortcutCatcher(); - void controlModifierlessTimout(); - void updateDisplayShortcut(); + void controlModifierlessTimout(); + void updateDisplayShortcut(); - QKeySequence shortcut() const; - void setDefaultShortcut(const QKeySequence &key); - void setShortcut(const QKeySequence &key); + QKeySequence shortcut() const; + void setDefaultShortcut(const QKeySequence& key); + void setShortcut(const QKeySequence& key); - public slots: - void resetShortcut(); - void clearShortcut(); + public slots: + void resetShortcut(); + void clearShortcut(); - private slots: - void startRecording(); - void doneRecording(); + private slots: + void startRecording(); + void doneRecording(); - signals: - void shortcutChanged(const QKeySequence &seguence); + signals: + void shortcutChanged(const QKeySequence& seguence); - private: - QToolButton *m_btnReset; - QToolButton *m_btnClear; - ShortcutButton *m_btnChange; - QHBoxLayout *m_layout; + private: + QToolButton* m_btnReset; + QToolButton* m_btnClear; + ShortcutButton* m_btnChange; + QHBoxLayout* m_layout; - QKeySequence m_currentSequence; - QKeySequence m_defaultSequence; + QKeySequence m_currentSequence; + QKeySequence m_defaultSequence; - bool m_isRecording; - int m_numKey; - uint m_modifierKeys; + bool m_isRecording; + int m_numKey; + uint m_modifierKeys; }; #endif // KEYSEQUENCECATCHER_H diff --git a/src/exceptions/applicationexception.cpp b/src/exceptions/applicationexception.cpp index 62d46cfea..e7cd17a24 100755 --- a/src/exceptions/applicationexception.cpp +++ b/src/exceptions/applicationexception.cpp @@ -1,29 +1,29 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "exceptions/applicationexception.h" - - -ApplicationException::ApplicationException(const QString &message) : m_message(message) { -} - -ApplicationException::~ApplicationException() { -} - -QString ApplicationException::message() const { - return m_message; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "exceptions/applicationexception.h" + + +ApplicationException::ApplicationException(const QString& message) : m_message(message) { +} + +ApplicationException::~ApplicationException() { +} + +QString ApplicationException::message() const { + return m_message; +} diff --git a/src/exceptions/applicationexception.h b/src/exceptions/applicationexception.h index 256edc684..c5371ae83 100755 --- a/src/exceptions/applicationexception.h +++ b/src/exceptions/applicationexception.h @@ -1,35 +1,35 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef APPLICATIONEXCEPTION_H -#define APPLICATIONEXCEPTION_H - -#include - - -class ApplicationException { - public: - explicit ApplicationException(const QString &message = QString()); - virtual ~ApplicationException(); - - QString message() const; - - private: - QString m_message; -}; - -#endif // APPLICATIONEXCEPTION_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef APPLICATIONEXCEPTION_H +#define APPLICATIONEXCEPTION_H + +#include + + +class ApplicationException { + public: + explicit ApplicationException(const QString& message = QString()); + virtual ~ApplicationException(); + + QString message() const; + + private: + QString m_message; +}; + +#endif // APPLICATIONEXCEPTION_H diff --git a/src/exceptions/ioexception.cpp b/src/exceptions/ioexception.cpp index 85e88ece2..6b093f50e 100755 --- a/src/exceptions/ioexception.cpp +++ b/src/exceptions/ioexception.cpp @@ -1,25 +1,25 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "exceptions/ioexception.h" - - -IOException::IOException(const QString &message) : ApplicationException(message) { -} - -IOException::~IOException() { -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "exceptions/ioexception.h" + + +IOException::IOException(const QString& message) : ApplicationException(message) { +} + +IOException::~IOException() { +} diff --git a/src/exceptions/ioexception.h b/src/exceptions/ioexception.h index 9c81e0ec1..213086992 100755 --- a/src/exceptions/ioexception.h +++ b/src/exceptions/ioexception.h @@ -1,30 +1,30 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef IOEXCEPTION_H -#define IOEXCEPTION_H - -#include "exceptions/applicationexception.h" - - -class IOException : public ApplicationException { - public: - explicit IOException(const QString &message = QString()); - virtual ~IOException(); -}; - -#endif // IOEXCEPTION_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef IOEXCEPTION_H +#define IOEXCEPTION_H + +#include "exceptions/applicationexception.h" + + +class IOException : public ApplicationException { + public: + explicit IOException(const QString& message = QString()); + virtual ~IOException(); +}; + +#endif // IOEXCEPTION_H diff --git a/src/gui/baselineedit.cpp b/src/gui/baselineedit.cpp index a3e5750e3..07d05c29c 100755 --- a/src/gui/baselineedit.cpp +++ b/src/gui/baselineedit.cpp @@ -1,41 +1,41 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/baselineedit.h" - -#include - - -BaseLineEdit::BaseLineEdit(QWidget *parent) : QLineEdit(parent) { -} - -BaseLineEdit::~BaseLineEdit() { -} - -void BaseLineEdit::submit(const QString &text) { - setText(text); - emit submitted(text); -} - -void BaseLineEdit::keyPressEvent(QKeyEvent *event) { - if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { - emit submitted(text()); - event->accept(); - } - - QLineEdit::keyPressEvent(event); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/baselineedit.h" + +#include + + +BaseLineEdit::BaseLineEdit(QWidget* parent) : QLineEdit(parent) { +} + +BaseLineEdit::~BaseLineEdit() { +} + +void BaseLineEdit::submit(const QString& text) { + setText(text); + emit submitted(text); +} + +void BaseLineEdit::keyPressEvent(QKeyEvent* event) { + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { + emit submitted(text()); + event->accept(); + } + + QLineEdit::keyPressEvent(event); +} diff --git a/src/gui/baselineedit.h b/src/gui/baselineedit.h index e955cdc04..67fe8a9f5 100755 --- a/src/gui/baselineedit.h +++ b/src/gui/baselineedit.h @@ -1,43 +1,43 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef BASELINEEDIT_H -#define BASELINEEDIT_H - -#include - - -class BaseLineEdit : public QLineEdit { - Q_OBJECT - - public: - // Constructors and destructors. - explicit BaseLineEdit(QWidget *parent = 0); - virtual ~BaseLineEdit(); - - public slots: - void submit(const QString &text); - - protected: - void keyPressEvent(QKeyEvent *event); - - signals: - // Emitted if user hits ENTER button. - void submitted(const QString &text); -}; - -#endif // BASELINEEDIT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef BASELINEEDIT_H +#define BASELINEEDIT_H + +#include + + +class BaseLineEdit : public QLineEdit { + Q_OBJECT + + public: + // Constructors and destructors. + explicit BaseLineEdit(QWidget* parent = 0); + virtual ~BaseLineEdit(); + + public slots: + void submit(const QString& text); + + protected: + void keyPressEvent(QKeyEvent* event); + + signals: + // Emitted if user hits ENTER button. + void submitted(const QString& text); +}; + +#endif // BASELINEEDIT_H diff --git a/src/gui/basetoolbar.cpp b/src/gui/basetoolbar.cpp index 7b719edbb..e2300367e 100755 --- a/src/gui/basetoolbar.cpp +++ b/src/gui/basetoolbar.cpp @@ -1,50 +1,50 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/basetoolbar.h" - -#include "definitions/definitions.h" -#include "gui/dialogs/formmain.h" -#include "miscellaneous/settings.h" - -#include - - -BaseToolBar::BaseToolBar(const QString &title, QWidget *parent) : QToolBar(title, parent) { - // Update right margin of filter textbox. - QMargins margins = contentsMargins(); - margins.setRight(margins.right() + FILTER_RIGHT_MARGIN); - setContentsMargins(margins); -} - -BaseToolBar::~BaseToolBar() { - qDebug("Destroying BaseToolBar instance."); -} - -void BaseBar::loadSavedActions() { - loadSpecificActions(getSpecificActions(savedActions())); -} - -QAction *BaseBar::findMatchingAction(const QString &action, const QList &actions) const { - foreach (QAction *act, actions) { - if (act->objectName() == action) { - return act; - } - } - - return nullptr; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/basetoolbar.h" + +#include "definitions/definitions.h" +#include "gui/dialogs/formmain.h" +#include "miscellaneous/settings.h" + +#include + + +BaseToolBar::BaseToolBar(const QString& title, QWidget* parent) : QToolBar(title, parent) { + // Update right margin of filter textbox. + QMargins margins = contentsMargins(); + margins.setRight(margins.right() + FILTER_RIGHT_MARGIN); + setContentsMargins(margins); +} + +BaseToolBar::~BaseToolBar() { + qDebug("Destroying BaseToolBar instance."); +} + +void BaseBar::loadSavedActions() { + loadSpecificActions(getSpecificActions(savedActions())); +} + +QAction* BaseBar::findMatchingAction(const QString& action, const QList& actions) const { + foreach (QAction* act, actions) { + if (act->objectName() == action) { + return act; + } + } + + return nullptr; +} diff --git a/src/gui/basetoolbar.h b/src/gui/basetoolbar.h index d092c217d..8a52aff92 100755 --- a/src/gui/basetoolbar.h +++ b/src/gui/basetoolbar.h @@ -1,60 +1,60 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef TOOLBAR_H -#define TOOLBAR_H - -#include - - -class BaseBar { - public: - // Returns all actions which can be added to the toolbar. - virtual QList availableActions() const = 0; - - // Returns all changeable actions which are currently included - // in the toolbar. - virtual QList changeableActions() const = 0; - - // Sets new "actions" to the toolbar and perhaps saves the toolbar - // state into the settings. - virtual void saveChangeableActions(const QStringList &actions) = 0; - - // Returns list of default actions. - virtual QStringList defaultActions() const = 0; - virtual QStringList savedActions() const = 0; - - // Loads the toolbar state from settings. - virtual void loadSavedActions(); - - virtual QList getSpecificActions(const QStringList &actions) = 0; - virtual void loadSpecificActions(const QList &actions) = 0; - - protected: - QAction *findMatchingAction(const QString &action, const QList &actions) const; -}; - -class BaseToolBar : public QToolBar, public BaseBar { - Q_OBJECT - - public: - // Constructors and destructors. - explicit BaseToolBar(const QString &title, QWidget *parent = 0); - virtual ~BaseToolBar(); -}; - -#endif // TOOLBAR_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef TOOLBAR_H +#define TOOLBAR_H + +#include + + +class BaseBar { + public: + // Returns all actions which can be added to the toolbar. + virtual QList availableActions() const = 0; + + // Returns all changeable actions which are currently included + // in the toolbar. + virtual QList changeableActions() const = 0; + + // Sets new "actions" to the toolbar and perhaps saves the toolbar + // state into the settings. + virtual void saveChangeableActions(const QStringList& actions) = 0; + + // Returns list of default actions. + virtual QStringList defaultActions() const = 0; + virtual QStringList savedActions() const = 0; + + // Loads the toolbar state from settings. + virtual void loadSavedActions(); + + virtual QList getSpecificActions(const QStringList& actions) = 0; + virtual void loadSpecificActions(const QList& actions) = 0; + + protected: + QAction* findMatchingAction(const QString& action, const QList& actions) const; +}; + +class BaseToolBar : public QToolBar, public BaseBar { + Q_OBJECT + + public: + // Constructors and destructors. + explicit BaseToolBar(const QString& title, QWidget* parent = 0); + virtual ~BaseToolBar(); +}; + +#endif // TOOLBAR_H diff --git a/src/gui/clickablelabel.cpp b/src/gui/clickablelabel.cpp index 6606a0ae6..bff58ac6b 100755 --- a/src/gui/clickablelabel.cpp +++ b/src/gui/clickablelabel.cpp @@ -25,61 +25,63 @@ ClickableLabel::ClickableLabel(QWidget* parent) - : QLabel(parent) { + : QLabel(parent) { } QString ClickableLabel::themeIcon() const { - return m_themeIcon; + return m_themeIcon; } -void ClickableLabel::setThemeIcon(const QString &name) { - m_themeIcon = name; - updateIcon(); +void ClickableLabel::setThemeIcon(const QString& name) { + m_themeIcon = name; + updateIcon(); } QIcon ClickableLabel::fallbackIcon() const { - return m_fallbackIcon; + return m_fallbackIcon; } -void ClickableLabel::setFallbackIcon(const QIcon &fallbackIcon) { - m_fallbackIcon = fallbackIcon; - updateIcon(); +void ClickableLabel::setFallbackIcon(const QIcon& fallbackIcon) { + m_fallbackIcon = fallbackIcon; + updateIcon(); } void ClickableLabel::updateIcon() { - if (!m_themeIcon.isEmpty()) { + if (!m_themeIcon.isEmpty()) { + const QIcon icon = qApp->icons()->fromTheme(m_themeIcon); - const QIcon icon = qApp->icons()->fromTheme(m_themeIcon); + if (!icon.isNull()) { + setPixmap(icon.pixmap(size())); + return; + } + } - if (!icon.isNull()) { - setPixmap(icon.pixmap(size())); - return; - } - } - - if (!m_fallbackIcon.isNull()) { - setPixmap(m_fallbackIcon.pixmap(size())); - } + if (!m_fallbackIcon.isNull()) { + setPixmap(m_fallbackIcon.pixmap(size())); + } } -void ClickableLabel::resizeEvent(QResizeEvent *ev) { - QLabel::resizeEvent(ev); - updateIcon(); +void ClickableLabel::resizeEvent(QResizeEvent* ev) { + QLabel::resizeEvent(ev); + updateIcon(); } void ClickableLabel::mouseReleaseEvent(QMouseEvent* ev) { - if (ev->button() == Qt::LeftButton && rect().contains(ev->pos())) { - if (ev->modifiers() == Qt::ControlModifier) { - emit middleClicked(ev->globalPos()); - } - else { - emit clicked(ev->globalPos()); - } - } - else if (ev->button() == Qt::MiddleButton && rect().contains(ev->pos())) { - emit middleClicked(ev->globalPos()); - } - else { - QLabel::mouseReleaseEvent(ev); - } + if (ev->button() == Qt::LeftButton && rect().contains(ev->pos())) { + if (ev->modifiers() == Qt::ControlModifier) { + emit middleClicked(ev->globalPos()); + } + + else { + emit clicked(ev->globalPos()); + } + } + + else if (ev->button() == Qt::MiddleButton && rect().contains(ev->pos())) { + emit middleClicked(ev->globalPos()); + } + + else { + QLabel::mouseReleaseEvent(ev); + } } diff --git a/src/gui/clickablelabel.h b/src/gui/clickablelabel.h index 7e88fc393..e52b8442b 100755 --- a/src/gui/clickablelabel.h +++ b/src/gui/clickablelabel.h @@ -25,35 +25,35 @@ class QMouseEvent; -class ClickableLabel : public QLabel{ - Q_OBJECT - Q_PROPERTY(QSize fixedsize READ size WRITE setFixedSize) - Q_PROPERTY(int fixedwidth READ width WRITE setFixedWidth) - Q_PROPERTY(int fixedheight READ height WRITE setFixedHeight) - Q_PROPERTY(QString themeIcon READ themeIcon WRITE setThemeIcon) - Q_PROPERTY(QIcon fallbackIcon READ fallbackIcon WRITE setFallbackIcon) +class ClickableLabel : public QLabel { + Q_OBJECT + Q_PROPERTY(QSize fixedsize READ size WRITE setFixedSize) + Q_PROPERTY(int fixedwidth READ width WRITE setFixedWidth) + Q_PROPERTY(int fixedheight READ height WRITE setFixedHeight) + Q_PROPERTY(QString themeIcon READ themeIcon WRITE setThemeIcon) + Q_PROPERTY(QIcon fallbackIcon READ fallbackIcon WRITE setFallbackIcon) - public: - explicit ClickableLabel(QWidget *parent = 0); + public: + explicit ClickableLabel(QWidget* parent = 0); - QString themeIcon() const; - void setThemeIcon(const QString &name); + QString themeIcon() const; + void setThemeIcon(const QString& name); - QIcon fallbackIcon() const; - void setFallbackIcon(const QIcon &fallbackIcon); + QIcon fallbackIcon() const; + void setFallbackIcon(const QIcon& fallbackIcon); - signals: - void clicked(QPoint); - void middleClicked(QPoint); + signals: + void clicked(QPoint); + void middleClicked(QPoint); - private: - void updateIcon(); + private: + void updateIcon(); - void resizeEvent(QResizeEvent *ev); - void mouseReleaseEvent(QMouseEvent* ev); + void resizeEvent(QResizeEvent* ev); + void mouseReleaseEvent(QMouseEvent* ev); - QString m_themeIcon; - QIcon m_fallbackIcon; + QString m_themeIcon; + QIcon m_fallbackIcon; }; diff --git a/src/gui/colorlabel.cpp b/src/gui/colorlabel.cpp index 70d32e10b..61816125e 100755 --- a/src/gui/colorlabel.cpp +++ b/src/gui/colorlabel.cpp @@ -1,43 +1,42 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/colorlabel.h" - -#include -#include - - -ColorLabel::ColorLabel(QWidget *parent) : QLabel(parent), m_color(QColor()) { - setFixedWidth(20); -} - -ColorLabel::~ColorLabel() { -} - -QColor ColorLabel::color() const { - return m_color; -} - -void ColorLabel::setColor(const QColor &color) { - m_color = color; - - repaint(); -} - -void ColorLabel::paintEvent(QPaintEvent *event) { - QPainter(this).fillRect(event->rect(), m_color); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/colorlabel.h" + +#include +#include + + +ColorLabel::ColorLabel(QWidget* parent) : QLabel(parent), m_color(QColor()) { + setFixedWidth(20); +} + +ColorLabel::~ColorLabel() { +} + +QColor ColorLabel::color() const { + return m_color; +} + +void ColorLabel::setColor(const QColor& color) { + m_color = color; + repaint(); +} + +void ColorLabel::paintEvent(QPaintEvent* event) { + QPainter(this).fillRect(event->rect(), m_color); +} diff --git a/src/gui/colorlabel.h b/src/gui/colorlabel.h index 82f9d666c..9cda8e774 100755 --- a/src/gui/colorlabel.h +++ b/src/gui/colorlabel.h @@ -1,41 +1,41 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef COLORLABEL_H -#define COLORLABEL_H - -#include - - -class ColorLabel : public QLabel { - Q_OBJECT - - public: - explicit ColorLabel(QWidget *parent = 0); - virtual ~ColorLabel(); - - QColor color() const; - void setColor(const QColor &color); - - protected: - void paintEvent(QPaintEvent *event); - - private: - QColor m_color; -}; - -#endif // COLORLABEL_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef COLORLABEL_H +#define COLORLABEL_H + +#include + + +class ColorLabel : public QLabel { + Q_OBJECT + + public: + explicit ColorLabel(QWidget* parent = 0); + virtual ~ColorLabel(); + + QColor color() const; + void setColor(const QColor& color); + + protected: + void paintEvent(QPaintEvent* event); + + private: + QColor m_color; +}; + +#endif // COLORLABEL_H diff --git a/src/gui/comboboxwithstatus.cpp b/src/gui/comboboxwithstatus.cpp index 04981d8e2..d50cb20d2 100755 --- a/src/gui/comboboxwithstatus.cpp +++ b/src/gui/comboboxwithstatus.cpp @@ -1,39 +1,37 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/comboboxwithstatus.h" - -#include "gui/plaintoolbutton.h" - -#include - - -ComboBoxWithStatus::ComboBoxWithStatus(QWidget *parent) - : WidgetWithStatus(parent) { - m_wdgInput = new QComboBox(this); - - // Set correct size for the tool button. - const int txt_input_height = m_wdgInput->sizeHint().height(); - m_btnStatus->setFixedSize(txt_input_height, txt_input_height); - - // Compose the layout. - m_layout->addWidget(m_wdgInput); - m_layout->addWidget(m_btnStatus); -} - -ComboBoxWithStatus::~ComboBoxWithStatus() { -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/comboboxwithstatus.h" + +#include "gui/plaintoolbutton.h" + +#include + + +ComboBoxWithStatus::ComboBoxWithStatus(QWidget* parent) + : WidgetWithStatus(parent) { + m_wdgInput = new QComboBox(this); + // Set correct size for the tool button. + const int txt_input_height = m_wdgInput->sizeHint().height(); + m_btnStatus->setFixedSize(txt_input_height, txt_input_height); + // Compose the layout. + m_layout->addWidget(m_wdgInput); + m_layout->addWidget(m_btnStatus); +} + +ComboBoxWithStatus::~ComboBoxWithStatus() { +} diff --git a/src/gui/comboboxwithstatus.h b/src/gui/comboboxwithstatus.h index 812076095..a81cdb262 100755 --- a/src/gui/comboboxwithstatus.h +++ b/src/gui/comboboxwithstatus.h @@ -1,39 +1,39 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef COMBOBOXWITHSTATUS_H -#define COMBOBOXWITHSTATUS_H - -#include "gui/widgetwithstatus.h" - -#include - - -class ComboBoxWithStatus : public WidgetWithStatus { - Q_OBJECT - - public: - // Constructors and destructors. - explicit ComboBoxWithStatus(QWidget *parent = 0); - virtual ~ComboBoxWithStatus(); - - inline QComboBox *comboBox() const { - return static_cast(m_wdgInput); - } -}; - -#endif // COMBOBOXWITHSTATUS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef COMBOBOXWITHSTATUS_H +#define COMBOBOXWITHSTATUS_H + +#include "gui/widgetwithstatus.h" + +#include + + +class ComboBoxWithStatus : public WidgetWithStatus { + Q_OBJECT + + public: + // Constructors and destructors. + explicit ComboBoxWithStatus(QWidget* parent = 0); + virtual ~ComboBoxWithStatus(); + + inline QComboBox* comboBox() const { + return static_cast(m_wdgInput); + } +}; + +#endif // COMBOBOXWITHSTATUS_H diff --git a/src/gui/dialogs/formabout.cpp b/src/gui/dialogs/formabout.cpp index 2d11e8a65..254889b79 100755 --- a/src/gui/dialogs/formabout.cpp +++ b/src/gui/dialogs/formabout.cpp @@ -1,122 +1,122 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/dialogs/formabout.h" - -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/textfactory.h" -#include "miscellaneous/settingsproperties.h" - -#include -#include - - -FormAbout::FormAbout(QWidget *parent) : QDialog(parent), m_ui(new Ui::FormAbout()) { - m_ui->setupUi(this); - - // Set flags and attributes. - setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); - setWindowIcon(qApp->icons()->fromTheme(QSL("help-about"))); - - //: About RSS Guard dialog title. - setWindowTitle(tr("About %1").arg(APP_NAME)); - - m_ui->m_lblIcon->setPixmap(QPixmap(APP_ICON_PATH)); - - // Load information from embedded text files. - loadLicenseAndInformation(); - - // Load additional paths information. - loadSettingsAndPaths(); -} - -FormAbout::~FormAbout() { - qDebug("Destroying FormAbout instance."); -} - -void FormAbout::loadSettingsAndPaths() { - if (qApp->settings()->type() == SettingsProperties::Portable) { - m_ui->m_txtPathsSettingsType->setText(tr("FULLY portable")); - } - else { - m_ui->m_txtPathsSettingsType->setText(tr("NOT portable")); - } - - m_ui->m_txtPathsDatabaseRoot->setText(QDir::toNativeSeparators(qApp->getUserDataPath() + - QDir::separator() + - QString(APP_DB_SQLITE_PATH))); - m_ui->m_txtPathsSettingsFile->setText(QDir::toNativeSeparators(qApp->settings()->fileName())); - m_ui->m_txtPathsSkinsRoot->setText(QDir::toNativeSeparators(qApp->skins()->getUserSkinBaseFolder())); -} - -void FormAbout::loadLicenseAndInformation() { - QFile file; - - file.setFileName(APP_INFO_PATH + QL1S("/COPYING_GNU_GPL_HTML")); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - m_ui->m_txtLicenseGnu->setText(QString::fromUtf8(file.readAll())); - } - else { - m_ui->m_txtLicenseGnu->setText(tr("License not found.")); - } - file.close(); - - file.setFileName(APP_INFO_PATH + QL1S("/COPYING_BSD")); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - m_ui->m_txtLicenseBsd->setText(QString::fromUtf8(file.readAll())); - } - else { - m_ui->m_txtLicenseBsd->setText(tr("License not found.")); - } - file.close(); - - file.setFileName(APP_INFO_PATH + QL1S("/CHANGELOG")); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - m_ui->m_txtChangelog->setText(QString::fromUtf8(file.readAll())); - } - else { - m_ui->m_txtChangelog->setText(tr("Changelog not found.")); - } - file.close(); - - // Set other informative texts. - m_ui->m_lblDesc->setText(tr("%8
" - "Version: %1 (built on %2/%3)
" - "Revision: %4
" - "Build date: %5
" - "Qt: %6 (compiled against %7)
").arg(qApp->applicationVersion(), - APP_SYSTEM_NAME, - APP_SYSTEM_VERSION, - APP_REVISION, - TextFactory::parseDateTime(QString("%1 %2").arg(__DATE__, - __TIME__)).toString(Qt::DefaultLocaleShortDate), - qVersion(), - QT_VERSION_STR, - APP_NAME)); - - m_ui->m_txtInfo->setText(tr("%5 is a (very) tiny feed reader." - "

This software is distributed under the terms of GNU General Public License, version 3." - "

Contacts:" - "
  • %1 ~e-mail
  • " - "
  • %2 ~website
" - "You can obtain source code for %5 from its website." - "


Copyright (C) 2011-%3 %4").arg(APP_EMAIL, - APP_URL, - QString::number(QDateTime::currentDateTime().date().year()), - APP_AUTHOR, - APP_NAME)); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/dialogs/formabout.h" + +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/textfactory.h" +#include "miscellaneous/settingsproperties.h" + +#include +#include + + +FormAbout::FormAbout(QWidget* parent) : QDialog(parent), m_ui(new Ui::FormAbout()) { + m_ui->setupUi(this); + // Set flags and attributes. + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); + setWindowIcon(qApp->icons()->fromTheme(QSL("help-about"))); + //: About RSS Guard dialog title. + setWindowTitle(tr("About %1").arg(APP_NAME)); + m_ui->m_lblIcon->setPixmap(QPixmap(APP_ICON_PATH)); + // Load information from embedded text files. + loadLicenseAndInformation(); + // Load additional paths information. + loadSettingsAndPaths(); +} + +FormAbout::~FormAbout() { + qDebug("Destroying FormAbout instance."); +} + +void FormAbout::loadSettingsAndPaths() { + if (qApp->settings()->type() == SettingsProperties::Portable) { + m_ui->m_txtPathsSettingsType->setText(tr("FULLY portable")); + } + + else { + m_ui->m_txtPathsSettingsType->setText(tr("NOT portable")); + } + + m_ui->m_txtPathsDatabaseRoot->setText(QDir::toNativeSeparators(qApp->getUserDataPath() + + QDir::separator() + + QString(APP_DB_SQLITE_PATH))); + m_ui->m_txtPathsSettingsFile->setText(QDir::toNativeSeparators(qApp->settings()->fileName())); + m_ui->m_txtPathsSkinsRoot->setText(QDir::toNativeSeparators(qApp->skins()->getUserSkinBaseFolder())); +} + +void FormAbout::loadLicenseAndInformation() { + QFile file; + file.setFileName(APP_INFO_PATH + QL1S("/COPYING_GNU_GPL_HTML")); + + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + m_ui->m_txtLicenseGnu->setText(QString::fromUtf8(file.readAll())); + } + + else { + m_ui->m_txtLicenseGnu->setText(tr("License not found.")); + } + + file.close(); + file.setFileName(APP_INFO_PATH + QL1S("/COPYING_BSD")); + + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + m_ui->m_txtLicenseBsd->setText(QString::fromUtf8(file.readAll())); + } + + else { + m_ui->m_txtLicenseBsd->setText(tr("License not found.")); + } + + file.close(); + file.setFileName(APP_INFO_PATH + QL1S("/CHANGELOG")); + + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + m_ui->m_txtChangelog->setText(QString::fromUtf8(file.readAll())); + } + + else { + m_ui->m_txtChangelog->setText(tr("Changelog not found.")); + } + + file.close(); + // Set other informative texts. + m_ui->m_lblDesc->setText(tr("%8
" + "Version: %1 (built on %2/%3)
" + "Revision: %4
" + "Build date: %5
" + "Qt: %6 (compiled against %7)
").arg(qApp->applicationVersion(), + APP_SYSTEM_NAME, + APP_SYSTEM_VERSION, + APP_REVISION, + TextFactory::parseDateTime(QString("%1 %2").arg(__DATE__, + __TIME__)).toString(Qt::DefaultLocaleShortDate), + qVersion(), + QT_VERSION_STR, + APP_NAME)); + m_ui->m_txtInfo->setText(tr("%5 is a (very) tiny feed reader." + "

This software is distributed under the terms of GNU General Public License, version 3." + "

Contacts:" + "
  • %1 ~e-mail
  • " + "
  • %2 ~website
" + "You can obtain source code for %5 from its website." + "


Copyright (C) 2011-%3 %4").arg(APP_EMAIL, + APP_URL, + QString::number(QDateTime::currentDateTime().date().year()), + APP_AUTHOR, + APP_NAME)); +} diff --git a/src/gui/dialogs/formabout.h b/src/gui/dialogs/formabout.h index 80022f228..b26aa195f 100755 --- a/src/gui/dialogs/formabout.h +++ b/src/gui/dialogs/formabout.h @@ -1,43 +1,43 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FORMABOUT_H -#define FORMABOUT_H - -#include - -#include "ui_formabout.h" - -#include "definitions/definitions.h" - - -class FormAbout : public QDialog { - Q_OBJECT - - public: - // Constructors and destructors. - explicit FormAbout(QWidget *parent); - virtual ~FormAbout(); - - private: - void loadLicenseAndInformation(); - void loadSettingsAndPaths(); - - QScopedPointer m_ui; -}; - -#endif // FORMABOUT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FORMABOUT_H +#define FORMABOUT_H + +#include + +#include "ui_formabout.h" + +#include "definitions/definitions.h" + + +class FormAbout : public QDialog { + Q_OBJECT + + public: + // Constructors and destructors. + explicit FormAbout(QWidget* parent); + virtual ~FormAbout(); + + private: + void loadLicenseAndInformation(); + void loadSettingsAndPaths(); + + QScopedPointer m_ui; +}; + +#endif // FORMABOUT_H diff --git a/src/gui/dialogs/formaddaccount.cpp b/src/gui/dialogs/formaddaccount.cpp index 54baa7eff..ab3d87539 100755 --- a/src/gui/dialogs/formaddaccount.cpp +++ b/src/gui/dialogs/formaddaccount.cpp @@ -26,61 +26,58 @@ #include -FormAddAccount::FormAddAccount(const QList &entry_points, FeedsModel *model, QWidget *parent) - : QDialog(parent), m_ui(new Ui::FormAddAccount), m_model(model), m_entryPoints(entry_points) { - m_ui->setupUi(this); - - // Set flags and attributes. - setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); - setWindowIcon(qApp->icons()->fromTheme(QSL("document-new"))); - - connect(m_ui->m_listEntryPoints, &QListWidget::itemDoubleClicked, this, &FormAddAccount::addSelectedAccount); - connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &FormAddAccount::addSelectedAccount); - connect(m_ui->m_listEntryPoints, &QListWidget::itemSelectionChanged, this, &FormAddAccount::displayActiveEntryPointDetails); - loadEntryPoints(); +FormAddAccount::FormAddAccount(const QList& entry_points, FeedsModel* model, QWidget* parent) + : QDialog(parent), m_ui(new Ui::FormAddAccount), m_model(model), m_entryPoints(entry_points) { + m_ui->setupUi(this); + // Set flags and attributes. + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); + setWindowIcon(qApp->icons()->fromTheme(QSL("document-new"))); + connect(m_ui->m_listEntryPoints, &QListWidget::itemDoubleClicked, this, &FormAddAccount::addSelectedAccount); + connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &FormAddAccount::addSelectedAccount); + connect(m_ui->m_listEntryPoints, &QListWidget::itemSelectionChanged, this, &FormAddAccount::displayActiveEntryPointDetails); + loadEntryPoints(); } FormAddAccount::~FormAddAccount() { - qDebug("Destroying FormAddAccount instance."); + qDebug("Destroying FormAddAccount instance."); } void FormAddAccount::addSelectedAccount() { - accept(); + accept(); + ServiceEntryPoint* point = selectedEntryPoint(); + ServiceRoot* new_root = point->createNewRoot(); - ServiceEntryPoint *point = selectedEntryPoint(); - ServiceRoot *new_root = point->createNewRoot(); + if (new_root != nullptr) { + m_model->addServiceAccount(new_root, true); + } - if (new_root != nullptr) { - m_model->addServiceAccount(new_root, true); - } - else { - qCritical("Cannot create new account."); - } + else { + qCritical("Cannot create new account."); + } } void FormAddAccount::displayActiveEntryPointDetails() { - const ServiceEntryPoint *point = selectedEntryPoint(); - - m_ui->m_txtAuthor->setText(point->author()); - m_ui->m_txtDescription->setText(point->description()); - m_ui->m_txtName->setText(point->name()); - m_ui->m_txtVersion->setText(point->version()); + const ServiceEntryPoint* point = selectedEntryPoint(); + m_ui->m_txtAuthor->setText(point->author()); + m_ui->m_txtDescription->setText(point->description()); + m_ui->m_txtName->setText(point->name()); + m_ui->m_txtVersion->setText(point->version()); } -ServiceEntryPoint *FormAddAccount::selectedEntryPoint() const { - return m_entryPoints.at(m_ui->m_listEntryPoints->currentRow()); +ServiceEntryPoint* FormAddAccount::selectedEntryPoint() const { + return m_entryPoints.at(m_ui->m_listEntryPoints->currentRow()); } void FormAddAccount::loadEntryPoints() { - foreach (const ServiceEntryPoint *entry_point, m_entryPoints) { - QListWidgetItem *item = new QListWidgetItem(entry_point->icon(), entry_point->name(), m_ui->m_listEntryPoints); + foreach (const ServiceEntryPoint* entry_point, m_entryPoints) { + QListWidgetItem* item = new QListWidgetItem(entry_point->icon(), entry_point->name(), m_ui->m_listEntryPoints); - if (entry_point->isSingleInstanceService() && m_model->containsServiceRootFromEntryPoint(entry_point)) { - // Oops, this item cannot be added, it is single instance and is already added. - item->setFlags(Qt::NoItemFlags); - item->setToolTip(tr("This account can be added only once.")); - } - } + if (entry_point->isSingleInstanceService() && m_model->containsServiceRootFromEntryPoint(entry_point)) { + // Oops, this item cannot be added, it is single instance and is already added. + item->setFlags(Qt::NoItemFlags); + item->setToolTip(tr("This account can be added only once.")); + } + } - m_ui->m_listEntryPoints->setCurrentRow(m_entryPoints.size() - 1); + m_ui->m_listEntryPoints->setCurrentRow(m_entryPoints.size() - 1); } diff --git a/src/gui/dialogs/formaddaccount.h b/src/gui/dialogs/formaddaccount.h index 0e3b1c520..3a9faa359 100755 --- a/src/gui/dialogs/formaddaccount.h +++ b/src/gui/dialogs/formaddaccount.h @@ -27,23 +27,23 @@ class ServiceEntryPoint; class FeedsModel; class FormAddAccount : public QDialog { - Q_OBJECT + Q_OBJECT - public: - explicit FormAddAccount(const QList &entry_points, FeedsModel *model, QWidget *parent = 0); - virtual ~FormAddAccount(); + public: + explicit FormAddAccount(const QList& entry_points, FeedsModel* model, QWidget* parent = 0); + virtual ~FormAddAccount(); - private slots: - void addSelectedAccount(); - void displayActiveEntryPointDetails(); + private slots: + void addSelectedAccount(); + void displayActiveEntryPointDetails(); - private: - ServiceEntryPoint *selectedEntryPoint() const; - void loadEntryPoints(); + private: + ServiceEntryPoint* selectedEntryPoint() const; + void loadEntryPoints(); - QScopedPointer m_ui; - FeedsModel *m_model; - QList m_entryPoints; + QScopedPointer m_ui; + FeedsModel* m_model; + QList m_entryPoints; }; #endif // FORMADDACCOUNT_H diff --git a/src/gui/dialogs/formbackupdatabasesettings.cpp b/src/gui/dialogs/formbackupdatabasesettings.cpp index d1ecb78f5..47f549dec 100755 --- a/src/gui/dialogs/formbackupdatabasesettings.cpp +++ b/src/gui/dialogs/formbackupdatabasesettings.cpp @@ -28,74 +28,73 @@ #include -FormBackupDatabaseSettings::FormBackupDatabaseSettings(QWidget *parent) : QDialog(parent), m_ui(new Ui::FormBackupDatabaseSettings) { - m_ui->setupUi(this); - m_ui->m_txtBackupName->lineEdit()->setPlaceholderText(tr("Common name for backup files")); +FormBackupDatabaseSettings::FormBackupDatabaseSettings(QWidget* parent) : QDialog(parent), m_ui(new Ui::FormBackupDatabaseSettings) { + m_ui->setupUi(this); + m_ui->m_txtBackupName->lineEdit()->setPlaceholderText(tr("Common name for backup files")); + setWindowIcon(qApp->icons()->fromTheme(QSL("document-export"))); + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); + connect(m_ui->m_checkBackupDatabase, &QCheckBox::toggled, this, &FormBackupDatabaseSettings::checkOkButton); + connect(m_ui->m_checkBackupSettings, &QCheckBox::toggled, this, &FormBackupDatabaseSettings::checkOkButton); + connect(m_ui->m_buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &FormBackupDatabaseSettings::performBackup); + connect(m_ui->m_txtBackupName->lineEdit(), &BaseLineEdit::textChanged, this, &FormBackupDatabaseSettings::checkBackupNames); + connect(m_ui->m_txtBackupName->lineEdit(), &BaseLineEdit::textChanged, this, &FormBackupDatabaseSettings::checkOkButton); + connect(m_ui->m_btnSelectFolder, &QPushButton::clicked, this, &FormBackupDatabaseSettings::selectFolderInitial); + selectFolder(qApp->getDocumentsFolderPath()); + m_ui->m_txtBackupName->lineEdit()->setText(QString(APP_LOW_NAME) + QL1S("_") + QDateTime::currentDateTime().toString(QSL("yyyyMMddHHmm"))); + m_ui->m_lblResult->setStatus(WidgetWithStatus::Warning, tr("No operation executed yet."), tr("No operation executed yet.")); - setWindowIcon(qApp->icons()->fromTheme(QSL("document-export"))); - setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); - - connect(m_ui->m_checkBackupDatabase, &QCheckBox::toggled, this, &FormBackupDatabaseSettings::checkOkButton); - connect(m_ui->m_checkBackupSettings, &QCheckBox::toggled, this, &FormBackupDatabaseSettings::checkOkButton); - connect(m_ui->m_buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &FormBackupDatabaseSettings::performBackup); - connect(m_ui->m_txtBackupName->lineEdit(), &BaseLineEdit::textChanged, this, &FormBackupDatabaseSettings::checkBackupNames); - connect(m_ui->m_txtBackupName->lineEdit(), &BaseLineEdit::textChanged, this, &FormBackupDatabaseSettings::checkOkButton); - connect(m_ui->m_btnSelectFolder, &QPushButton::clicked, this, &FormBackupDatabaseSettings::selectFolderInitial); - - selectFolder(qApp->getDocumentsFolderPath()); - m_ui->m_txtBackupName->lineEdit()->setText(QString(APP_LOW_NAME) + QL1S("_") + QDateTime::currentDateTime().toString(QSL("yyyyMMddHHmm"))); - m_ui->m_lblResult->setStatus(WidgetWithStatus::Warning, tr("No operation executed yet."), tr("No operation executed yet.")); - - if (qApp->database()->activeDatabaseDriver() != DatabaseFactory::SQLITE && - qApp->database()->activeDatabaseDriver() != DatabaseFactory::SQLITE_MEMORY) { - m_ui->m_checkBackupDatabase->setDisabled(true); - } + if (qApp->database()->activeDatabaseDriver() != DatabaseFactory::SQLITE && + qApp->database()->activeDatabaseDriver() != DatabaseFactory::SQLITE_MEMORY) { + m_ui->m_checkBackupDatabase->setDisabled(true); + } } FormBackupDatabaseSettings::~FormBackupDatabaseSettings() { - qDebug("Destroying FormBackupDatabaseSettings instance."); + qDebug("Destroying FormBackupDatabaseSettings instance."); } void FormBackupDatabaseSettings::performBackup() { - try { - qApp->backupDatabaseSettings(m_ui->m_checkBackupDatabase->isChecked(), m_ui->m_checkBackupSettings->isChecked(), - m_ui->m_lblSelectFolder->label()->text(), m_ui->m_txtBackupName->lineEdit()->text()); - m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, - tr("Backup was created successfully and stored in target directory."), - tr("Backup was created successfully.")); - } - catch (const ApplicationException &ex) { - m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, ex.message(), tr("Backup failed.")); - } + try { + qApp->backupDatabaseSettings(m_ui->m_checkBackupDatabase->isChecked(), m_ui->m_checkBackupSettings->isChecked(), + m_ui->m_lblSelectFolder->label()->text(), m_ui->m_txtBackupName->lineEdit()->text()); + m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, + tr("Backup was created successfully and stored in target directory."), + tr("Backup was created successfully.")); + } + + catch (const ApplicationException& ex) { + m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, ex.message(), tr("Backup failed.")); + } } void FormBackupDatabaseSettings::selectFolderInitial() { - selectFolder(); + selectFolder(); } void FormBackupDatabaseSettings::selectFolder(QString path) { - if (path.isEmpty()) { - path = QFileDialog::getExistingDirectory(this, tr("Select destination directory"), m_ui->m_lblSelectFolder->label()->text()); - } + if (path.isEmpty()) { + path = QFileDialog::getExistingDirectory(this, tr("Select destination directory"), m_ui->m_lblSelectFolder->label()->text()); + } - if (!path.isEmpty()) { - m_ui->m_lblSelectFolder->setStatus(WidgetWithStatus::Ok, QDir::toNativeSeparators(path), - tr("Good destination directory is specified.")); - } + if (!path.isEmpty()) { + m_ui->m_lblSelectFolder->setStatus(WidgetWithStatus::Ok, QDir::toNativeSeparators(path), + tr("Good destination directory is specified.")); + } } -void FormBackupDatabaseSettings::checkBackupNames(const QString &name) { - if (name.simplified().isEmpty()) { - m_ui->m_txtBackupName->setStatus(WidgetWithStatus::Error, tr("Backup name cannot be empty.")); - } - else { - m_ui->m_txtBackupName->setStatus(WidgetWithStatus::Ok, tr("Backup name looks okay.")); - } +void FormBackupDatabaseSettings::checkBackupNames(const QString& name) { + if (name.simplified().isEmpty()) { + m_ui->m_txtBackupName->setStatus(WidgetWithStatus::Error, tr("Backup name cannot be empty.")); + } + + else { + m_ui->m_txtBackupName->setStatus(WidgetWithStatus::Ok, tr("Backup name looks okay.")); + } } void FormBackupDatabaseSettings::checkOkButton() { - m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setDisabled(m_ui->m_txtBackupName->lineEdit()->text().simplified().isEmpty() || - m_ui->m_lblSelectFolder->label()->text().simplified().isEmpty() || - (!m_ui->m_checkBackupDatabase->isChecked() && - !m_ui->m_checkBackupSettings->isChecked())); + m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setDisabled(m_ui->m_txtBackupName->lineEdit()->text().simplified().isEmpty() || + m_ui->m_lblSelectFolder->label()->text().simplified().isEmpty() || + (!m_ui->m_checkBackupDatabase->isChecked() && + !m_ui->m_checkBackupSettings->isChecked())); } diff --git a/src/gui/dialogs/formbackupdatabasesettings.h b/src/gui/dialogs/formbackupdatabasesettings.h index e08e59713..5f73059a3 100755 --- a/src/gui/dialogs/formbackupdatabasesettings.h +++ b/src/gui/dialogs/formbackupdatabasesettings.h @@ -24,23 +24,23 @@ class FormBackupDatabaseSettings : public QDialog { - Q_OBJECT + Q_OBJECT - public: - // Constructors and destructors - explicit FormBackupDatabaseSettings(QWidget *parent = 0); - virtual ~FormBackupDatabaseSettings(); + public: + // Constructors and destructors + explicit FormBackupDatabaseSettings(QWidget* parent = 0); + virtual ~FormBackupDatabaseSettings(); - private slots: - void performBackup(); - void selectFolderInitial(); - void selectFolder(QString path = QString()); - void checkBackupNames(const QString &name); - void checkOkButton(); + private slots: + void performBackup(); + void selectFolderInitial(); + void selectFolder(QString path = QString()); + void checkBackupNames(const QString& name); + void checkOkButton(); - private: - QScopedPointer m_ui; + private: + QScopedPointer m_ui; }; #endif // FORMBACKUPDATABASECONFIG_H diff --git a/src/gui/dialogs/formdatabasecleanup.cpp b/src/gui/dialogs/formdatabasecleanup.cpp index 3dbdbccbe..c7eef7055 100755 --- a/src/gui/dialogs/formdatabasecleanup.cpp +++ b/src/gui/dialogs/formdatabasecleanup.cpp @@ -1,135 +1,131 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/dialogs/formdatabasecleanup.h" - -#include "miscellaneous/application.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/databasefactory.h" - -#include -#include -#include - - -FormDatabaseCleanup::FormDatabaseCleanup(QWidget *parent) : QDialog(parent), m_ui(new Ui::FormDatabaseCleanup), m_cleaner(nullptr) { - m_ui->setupUi(this); - - // Set flags and attributes. - setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint | Qt::WindowTitleHint); - setWindowIcon(qApp->icons()->fromTheme(QSL("edit-clear"))); - - connect(m_ui->m_spinDays, static_cast(&QSpinBox::valueChanged), this, &FormDatabaseCleanup::updateDaysSuffix); - m_ui->m_spinDays->setValue(DEFAULT_DAYS_TO_DELETE_MSG); - m_ui->m_lblResult->setStatus(WidgetWithStatus::Information, tr("I am ready."), tr("I am ready.")); - loadDatabaseInfo(); -} - -FormDatabaseCleanup::~FormDatabaseCleanup() { - qDebug("Destroying FormDatabaseCleanup instance."); -} - -void FormDatabaseCleanup::setCleaner(DatabaseCleaner *cleaner) { - if (m_cleaner != nullptr) { - disconnect(this, 0, m_cleaner, 0); - disconnect(m_cleaner, 0, this, 0); - } - - m_cleaner = cleaner; - - connect(m_ui->m_btnBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &FormDatabaseCleanup::startPurging); - connect(this, &FormDatabaseCleanup::purgeRequested, m_cleaner, &DatabaseCleaner::purgeDatabaseData); - connect(m_cleaner, &DatabaseCleaner::purgeStarted, this, &FormDatabaseCleanup::onPurgeStarted); - connect(m_cleaner, &DatabaseCleaner::purgeProgress, this, &FormDatabaseCleanup::onPurgeProgress); - connect(m_cleaner, &DatabaseCleaner::purgeFinished, this,&FormDatabaseCleanup::onPurgeFinished); -} - -void FormDatabaseCleanup::closeEvent(QCloseEvent *event) { - if (m_ui->m_progressBar->isEnabled()) { - event->ignore(); - } - else { - QDialog::closeEvent(event); - } -} - -void FormDatabaseCleanup::keyPressEvent(QKeyEvent *event) { - if (m_ui->m_progressBar->isEnabled()) { - event->ignore(); - } - else { - QDialog::keyPressEvent(event); - } -} - -void FormDatabaseCleanup::updateDaysSuffix(int number) { - m_ui->m_spinDays->setSuffix(tr(" day(s)", 0, number)); -} - -void FormDatabaseCleanup::startPurging() { - CleanerOrders orders; - - orders.m_removeRecycleBin = m_ui->m_checkRemoveRecycleBin->isChecked(); - orders.m_removeOldMessages = m_ui->m_checkRemoveOldMessages->isChecked(); - orders.m_barrierForRemovingOldMessagesInDays = m_ui->m_spinDays->value(); - orders.m_removeReadMessages = m_ui->m_checkRemoveReadMessages->isChecked(); - orders.m_shrinkDatabase = m_ui->m_checkShrink->isEnabled() && m_ui->m_checkShrink->isChecked(); - orders.m_removeStarredMessages = m_ui->m_checkRemoveStarredMessages->isChecked(); - - emit purgeRequested(orders); -} - -void FormDatabaseCleanup::onPurgeStarted() { - m_ui->m_progressBar->setValue(0); - m_ui->m_progressBar->setEnabled(true); - m_ui->m_btnBox->setEnabled(false); - m_ui->m_lblResult->setStatus(WidgetWithStatus::Information, tr("Database cleanup is running."), tr("Database cleanup is running.")); -} - -void FormDatabaseCleanup::onPurgeProgress(int progress, const QString &description) { - m_ui->m_progressBar->setValue(progress); - m_ui->m_lblResult->setStatus(WidgetWithStatus::Information, description, description); -} - -void FormDatabaseCleanup::onPurgeFinished(bool finished) { - m_ui->m_progressBar->setEnabled(false); - m_ui->m_progressBar->setValue(0); - m_ui->m_btnBox->setEnabled(true); - - if (finished) { - m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, tr("Database cleanup is completed."), tr("Database cleanup is completed.")); - } - else { - m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Database cleanup failed."), tr("Database cleanup failed.")); - } - - loadDatabaseInfo(); -} - -void FormDatabaseCleanup::loadDatabaseInfo() { - qint64 file_size = qApp->database()->getDatabaseFileSize(); - qint64 data_size = qApp->database()->getDatabaseDataSize(); - - QString file_size_str = file_size > 0 ? QString::number(file_size / 1000000.0) + QL1S(" MB") : tr("unknown"); - QString data_size_str = data_size > 0 ? QString::number(data_size / 1000000.0) + QL1S(" MB") : tr("unknown"); - - m_ui->m_txtFileSize->setText(tr("file: %1, data: %2").arg(file_size_str, data_size_str)); - m_ui->m_txtDatabaseType->setText(qApp->database()->humanDriverName(qApp->database()->activeDatabaseDriver())); - m_ui->m_checkShrink->setEnabled(qApp->database()->activeDatabaseDriver() == DatabaseFactory::SQLITE || - qApp->database()->activeDatabaseDriver() == DatabaseFactory::SQLITE_MEMORY); - m_ui->m_checkShrink->setChecked(m_ui->m_checkShrink->isEnabled()); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/dialogs/formdatabasecleanup.h" + +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/databasefactory.h" + +#include +#include +#include + + +FormDatabaseCleanup::FormDatabaseCleanup(QWidget* parent) : QDialog(parent), m_ui(new Ui::FormDatabaseCleanup), m_cleaner(nullptr) { + m_ui->setupUi(this); + // Set flags and attributes. + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint | Qt::WindowTitleHint); + setWindowIcon(qApp->icons()->fromTheme(QSL("edit-clear"))); + connect(m_ui->m_spinDays, static_cast(&QSpinBox::valueChanged), this, &FormDatabaseCleanup::updateDaysSuffix); + m_ui->m_spinDays->setValue(DEFAULT_DAYS_TO_DELETE_MSG); + m_ui->m_lblResult->setStatus(WidgetWithStatus::Information, tr("I am ready."), tr("I am ready.")); + loadDatabaseInfo(); +} + +FormDatabaseCleanup::~FormDatabaseCleanup() { + qDebug("Destroying FormDatabaseCleanup instance."); +} + +void FormDatabaseCleanup::setCleaner(DatabaseCleaner* cleaner) { + if (m_cleaner != nullptr) { + disconnect(this, 0, m_cleaner, 0); + disconnect(m_cleaner, 0, this, 0); + } + + m_cleaner = cleaner; + connect(m_ui->m_btnBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &FormDatabaseCleanup::startPurging); + connect(this, &FormDatabaseCleanup::purgeRequested, m_cleaner, &DatabaseCleaner::purgeDatabaseData); + connect(m_cleaner, &DatabaseCleaner::purgeStarted, this, &FormDatabaseCleanup::onPurgeStarted); + connect(m_cleaner, &DatabaseCleaner::purgeProgress, this, &FormDatabaseCleanup::onPurgeProgress); + connect(m_cleaner, &DatabaseCleaner::purgeFinished, this, &FormDatabaseCleanup::onPurgeFinished); +} + +void FormDatabaseCleanup::closeEvent(QCloseEvent* event) { + if (m_ui->m_progressBar->isEnabled()) { + event->ignore(); + } + + else { + QDialog::closeEvent(event); + } +} + +void FormDatabaseCleanup::keyPressEvent(QKeyEvent* event) { + if (m_ui->m_progressBar->isEnabled()) { + event->ignore(); + } + + else { + QDialog::keyPressEvent(event); + } +} + +void FormDatabaseCleanup::updateDaysSuffix(int number) { + m_ui->m_spinDays->setSuffix(tr(" day(s)", 0, number)); +} + +void FormDatabaseCleanup::startPurging() { + CleanerOrders orders; + orders.m_removeRecycleBin = m_ui->m_checkRemoveRecycleBin->isChecked(); + orders.m_removeOldMessages = m_ui->m_checkRemoveOldMessages->isChecked(); + orders.m_barrierForRemovingOldMessagesInDays = m_ui->m_spinDays->value(); + orders.m_removeReadMessages = m_ui->m_checkRemoveReadMessages->isChecked(); + orders.m_shrinkDatabase = m_ui->m_checkShrink->isEnabled() && m_ui->m_checkShrink->isChecked(); + orders.m_removeStarredMessages = m_ui->m_checkRemoveStarredMessages->isChecked(); + emit purgeRequested(orders); +} + +void FormDatabaseCleanup::onPurgeStarted() { + m_ui->m_progressBar->setValue(0); + m_ui->m_progressBar->setEnabled(true); + m_ui->m_btnBox->setEnabled(false); + m_ui->m_lblResult->setStatus(WidgetWithStatus::Information, tr("Database cleanup is running."), tr("Database cleanup is running.")); +} + +void FormDatabaseCleanup::onPurgeProgress(int progress, const QString& description) { + m_ui->m_progressBar->setValue(progress); + m_ui->m_lblResult->setStatus(WidgetWithStatus::Information, description, description); +} + +void FormDatabaseCleanup::onPurgeFinished(bool finished) { + m_ui->m_progressBar->setEnabled(false); + m_ui->m_progressBar->setValue(0); + m_ui->m_btnBox->setEnabled(true); + + if (finished) { + m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, tr("Database cleanup is completed."), tr("Database cleanup is completed.")); + } + + else { + m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Database cleanup failed."), tr("Database cleanup failed.")); + } + + loadDatabaseInfo(); +} + +void FormDatabaseCleanup::loadDatabaseInfo() { + qint64 file_size = qApp->database()->getDatabaseFileSize(); + qint64 data_size = qApp->database()->getDatabaseDataSize(); + QString file_size_str = file_size > 0 ? QString::number(file_size / 1000000.0) + QL1S(" MB") : tr("unknown"); + QString data_size_str = data_size > 0 ? QString::number(data_size / 1000000.0) + QL1S(" MB") : tr("unknown"); + m_ui->m_txtFileSize->setText(tr("file: %1, data: %2").arg(file_size_str, data_size_str)); + m_ui->m_txtDatabaseType->setText(qApp->database()->humanDriverName(qApp->database()->activeDatabaseDriver())); + m_ui->m_checkShrink->setEnabled(qApp->database()->activeDatabaseDriver() == DatabaseFactory::SQLITE || + qApp->database()->activeDatabaseDriver() == DatabaseFactory::SQLITE_MEMORY); + m_ui->m_checkShrink->setChecked(m_ui->m_checkShrink->isEnabled()); +} diff --git a/src/gui/dialogs/formdatabasecleanup.h b/src/gui/dialogs/formdatabasecleanup.h index 351e23381..8bacda633 100755 --- a/src/gui/dialogs/formdatabasecleanup.h +++ b/src/gui/dialogs/formdatabasecleanup.h @@ -1,60 +1,60 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FORMDATABASECLEANUP_H -#define FORMDATABASECLEANUP_H - -#include - -#include "ui_formdatabasecleanup.h" - -#include "miscellaneous/databasecleaner.h" - - -class FormDatabaseCleanup : public QDialog { - Q_OBJECT - - public: - // Constructors. - explicit FormDatabaseCleanup(QWidget *parent = 0); - virtual ~FormDatabaseCleanup(); - - void setCleaner(DatabaseCleaner *cleaner); - - protected: - void closeEvent(QCloseEvent *event); - void keyPressEvent(QKeyEvent *event); - - private slots: - void updateDaysSuffix(int number); - void startPurging(); - void onPurgeStarted(); - void onPurgeProgress(int progress, const QString &description); - void onPurgeFinished(bool finished); - - signals: - void purgeRequested(const CleanerOrders &which_data); - - private: - void loadDatabaseInfo(); - - private: - QScopedPointer m_ui; - DatabaseCleaner *m_cleaner; -}; - -#endif // FORMDATABASECLEANUP_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FORMDATABASECLEANUP_H +#define FORMDATABASECLEANUP_H + +#include + +#include "ui_formdatabasecleanup.h" + +#include "miscellaneous/databasecleaner.h" + + +class FormDatabaseCleanup : public QDialog { + Q_OBJECT + + public: + // Constructors. + explicit FormDatabaseCleanup(QWidget* parent = 0); + virtual ~FormDatabaseCleanup(); + + void setCleaner(DatabaseCleaner* cleaner); + + protected: + void closeEvent(QCloseEvent* event); + void keyPressEvent(QKeyEvent* event); + + private slots: + void updateDaysSuffix(int number); + void startPurging(); + void onPurgeStarted(); + void onPurgeProgress(int progress, const QString& description); + void onPurgeFinished(bool finished); + + signals: + void purgeRequested(const CleanerOrders& which_data); + + private: + void loadDatabaseInfo(); + + private: + QScopedPointer m_ui; + DatabaseCleaner* m_cleaner; +}; + +#endif // FORMDATABASECLEANUP_H diff --git a/src/gui/dialogs/formmain.cpp b/src/gui/dialogs/formmain.cpp index 9ebfb63e4..ae569ed98 100755 --- a/src/gui/dialogs/formmain.cpp +++ b/src/gui/dialogs/formmain.cpp @@ -1,814 +1,769 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/dialogs/formmain.h" - -#include "definitions/definitions.h" -#include "miscellaneous/settings.h" -#include "miscellaneous/application.h" -#include "miscellaneous/systemfactory.h" -#include "miscellaneous/mutex.h" -#include "miscellaneous/databasefactory.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/feedreader.h" -#include "network-web/webfactory.h" -#include "gui/feedsview.h" -#include "gui/messagebox.h" -#include "gui/systemtrayicon.h" -#include "gui/tabbar.h" -#include "gui/statusbar.h" -#include "gui/messagesview.h" -#include "gui/feedmessageviewer.h" -#include "gui/plaintoolbutton.h" -#include "gui/feedstoolbar.h" -#include "gui/messagestoolbar.h" -#include "gui/dialogs/formabout.h" -#include "gui/dialogs/formsettings.h" -#include "gui/dialogs/formupdate.h" -#include "gui/dialogs/formdatabasecleanup.h" -#include "gui/dialogs/formbackupdatabasesettings.h" -#include "gui/dialogs/formrestoredatabasesettings.h" -#include "gui/dialogs/formaddaccount.h" -#include "services/abstract/serviceroot.h" -#include "services/abstract/recyclebin.h" -#include "services/standard/gui/formstandardimportexport.h" -#include "services/owncloud/network/owncloudnetworkfactory.h" - -#include -#include -#include -#include -#include -#include - -#if defined(USE_WEBENGINE) -#include "network-web/adblock/adblockicon.h" -#endif - - -FormMain::FormMain(QWidget *parent, Qt::WindowFlags f) - : QMainWindow(parent, f), m_ui(new Ui::FormMain) { - m_ui->setupUi(this); - -#if defined(USE_WEBENGINE) - m_adblockIcon = new AdBlockIcon(this); - m_adblockIconAction = m_adblockIcon->menuAction(); - m_adblockIconAction->setObjectName(QSL("m_adblockIconAction")); - - m_ui->m_menuTools->addAction(m_adblockIconAction); -#endif - - qApp->setMainForm(this); - - // Add these actions to the list of actions of the main window. - // This allows to use actions via shortcuts - // even if main menu is not visible. - addActions(allActions()); - -#if defined(USE_WEBENGINE) - addAction(m_adblockIconAction); -#endif - - m_statusBar = new StatusBar(this); - setStatusBar(m_statusBar); - - // Prepare main window and tabs. - prepareMenus(); - - // Prepare tabs. - //m_ui->m_tabWidget->initializeTabs(); - tabWidget()->feedMessageViewer()->feedsToolBar()->loadSavedActions(); - tabWidget()->feedMessageViewer()->messagesToolBar()->loadSavedActions(); - - // Establish connections. - createConnections(); - - updateMessageButtonsAvailability(); - updateFeedButtonsAvailability(); - - // Setup some appearance of the window. - setupIcons(); - loadSize(); - - m_statusBar->loadSavedActions(); -} - -FormMain::~FormMain() { - qDebug("Destroying FormMain instance."); -} - -QMenu *FormMain::trayMenu() const { - return m_trayMenu; -} - -TabWidget *FormMain::tabWidget() const { - return m_ui->m_tabWidget; -} - -StatusBar *FormMain::statusBar() const { - return m_statusBar; -} - -void FormMain::showDbCleanupAssistant() { - if (qApp->feedUpdateLock()->tryLock()) { - QScopedPointer form_pointer(new FormDatabaseCleanup(this)); - form_pointer.data()->setCleaner(qApp->feedReader()->databaseCleaner()); - form_pointer.data()->exec(); - - qApp->feedUpdateLock()->unlock(); - - tabWidget()->feedMessageViewer()->messagesView()->reloadSelections(); - qApp->feedReader()->feedsModel()->reloadCountsOfWholeModel(); - } - else { - qApp->showGuiMessage(tr("Cannot cleanup database"), - tr("Cannot cleanup database, because another critical action is running."), - QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); - } -} - -QList FormMain::allActions() const { - QList actions; - - // Add basic actions. - actions << m_ui->m_actionSettings; - actions << m_ui->m_actionDownloadManager; - actions << m_ui->m_actionRestoreDatabaseSettings; - actions << m_ui->m_actionBackupDatabaseSettings; - actions << m_ui->m_actionRestart; - actions << m_ui->m_actionQuit; -#if !defined(Q_OS_MAC) - actions << m_ui->m_actionFullscreen; -#endif - actions << m_ui->m_actionAboutGuard; - actions << m_ui->m_actionSwitchFeedsList; - actions << m_ui->m_actionSwitchMainWindow; -#if !defined(Q_OS_MAC) - actions << m_ui->m_actionSwitchMainMenu; -#endif - actions << m_ui->m_actionSwitchToolBars; - actions << m_ui->m_actionSwitchListHeaders; - actions << m_ui->m_actionSwitchStatusBar; - actions << m_ui->m_actionSwitchMessageListOrientation; - - // Add feeds/messages actions. - actions << m_ui->m_actionOpenSelectedSourceArticlesExternally; - actions << m_ui->m_actionOpenSelectedMessagesInternally; - actions << m_ui->m_actionMarkAllItemsRead; - actions << m_ui->m_actionMarkSelectedItemsAsRead; - actions << m_ui->m_actionMarkSelectedItemsAsUnread; - actions << m_ui->m_actionClearSelectedItems; - actions << m_ui->m_actionClearAllItems; - actions << m_ui->m_actionShowOnlyUnreadItems; - actions << m_ui->m_actionMarkSelectedMessagesAsRead; - actions << m_ui->m_actionMarkSelectedMessagesAsUnread; - actions << m_ui->m_actionSwitchImportanceOfSelectedMessages; - actions << m_ui->m_actionDeleteSelectedMessages; - actions << m_ui->m_actionUpdateAllItems; - actions << m_ui->m_actionUpdateSelectedItems; - actions << m_ui->m_actionStopRunningItemsUpdate; - actions << m_ui->m_actionEditSelectedItem; - actions << m_ui->m_actionDeleteSelectedItem; - actions << m_ui->m_actionServiceAdd; - actions << m_ui->m_actionServiceEdit; - actions << m_ui->m_actionServiceDelete; - actions << m_ui->m_actionCleanupDatabase; - actions << m_ui->m_actionAddFeedIntoSelectedAccount; - actions << m_ui->m_actionAddCategoryIntoSelectedAccount; - actions << m_ui->m_actionViewSelectedItemsNewspaperMode; - actions << m_ui->m_actionSelectNextItem; - actions << m_ui->m_actionSelectPreviousItem; - actions << m_ui->m_actionSelectNextMessage; - actions << m_ui->m_actionSelectPreviousMessage; - actions << m_ui->m_actionSelectNextUnreadMessage; - actions << m_ui->m_actionExpandCollapseItem; - -#if defined(USE_WEBENGINE) - actions << m_ui->m_actionTabNewWebBrowser; - actions << m_adblockIconAction; -#endif - - actions << m_ui->m_actionTabsCloseAll; - actions << m_ui->m_actionTabsCloseAllExceptCurrent; - - return actions; -} - -void FormMain::prepareMenus() { - // Setup menu for tray icon. - if (SystemTrayIcon::isSystemTrayAvailable()) { -#if defined(Q_OS_WIN) - m_trayMenu = new TrayIconMenu(APP_NAME, this); -#else - m_trayMenu = new QMenu(QSL(APP_NAME), this); -#endif - - // Add needed items to the menu. - m_trayMenu->addAction(m_ui->m_actionSwitchMainWindow); - m_trayMenu->addSeparator(); - m_trayMenu->addAction(m_ui->m_actionUpdateAllItems); - m_trayMenu->addAction(m_ui->m_actionMarkAllItemsRead); - m_trayMenu->addSeparator(); - m_trayMenu->addAction(m_ui->m_actionSettings); - m_trayMenu->addAction(m_ui->m_actionQuit); - - qDebug("Creating tray icon menu."); - } - -#if !defined(USE_WEBENGINE) - m_ui->m_menuWebBrowserTabs->removeAction(m_ui->m_actionTabNewWebBrowser); - m_ui->m_menuWebBrowserTabs->setTitle(tr("Tabs")); -#endif - -#if defined(Q_OS_MAC) - m_ui->m_actionSwitchMainMenu->setVisible(false); - m_ui->m_actionFullscreen->setVisible(false); -#endif -} - -void FormMain::switchFullscreenMode() { - if (!isFullScreen()) { - qApp->settings()->setValue(GROUP(GUI), GUI::IsMainWindowMaximizedBeforeFullscreen, isMaximized()); - showFullScreen(); - } - else { - if (qApp->settings()->value(GROUP(GUI), SETTING(GUI::IsMainWindowMaximizedBeforeFullscreen)).toBool()) { - setWindowState((windowState() & ~Qt::WindowFullScreen) | Qt::WindowMaximized); - } - else { - showNormal(); - } - } -} - -void FormMain::updateAddItemMenu() { - // NOTE: Clear here deletes items from memory but only those OWNED by the menu. - m_ui->m_menuAddItem->clear(); - - foreach (ServiceRoot *activated_root, qApp->feedReader()->feedsModel()->serviceRoots()) { - QMenu *root_menu = new QMenu(activated_root->title(), m_ui->m_menuAddItem); - root_menu->setIcon(activated_root->icon()); - root_menu->setToolTip(activated_root->description()); - - QList specific_root_actions = activated_root->addItemMenu(); - - if (activated_root->supportsCategoryAdding()) { - QAction *action_new_category = new QAction(qApp->icons()->fromTheme(QSL("folder")), - tr("Add new category"), - m_ui->m_menuAddItem); - root_menu->addAction(action_new_category); - connect(action_new_category, &QAction::triggered, activated_root, &ServiceRoot::addNewCategory); - } - - if (activated_root->supportsFeedAdding()) { - QAction *action_new_feed = new QAction(qApp->icons()->fromTheme(QSL("application-rss+xml")), - tr("Add new feed"), - m_ui->m_menuAddItem); - root_menu->addAction(action_new_feed); - // NOTE: Because of default arguments. - connect(action_new_feed, SIGNAL(triggered(bool)), activated_root, SLOT(addNewFeed())); - } - - if (!specific_root_actions.isEmpty()) { - if (!root_menu->isEmpty()) { - root_menu->addSeparator(); - } - - root_menu->addActions(specific_root_actions); - } - - m_ui->m_menuAddItem->addMenu(root_menu); - } - - if (!m_ui->m_menuAddItem->isEmpty()) { - m_ui->m_menuAddItem->addSeparator(); - m_ui->m_menuAddItem->addAction(m_ui->m_actionAddCategoryIntoSelectedAccount); - m_ui->m_menuAddItem->addAction(m_ui->m_actionAddFeedIntoSelectedAccount); - } - else { - m_ui->m_menuAddItem->addAction(m_ui->m_actionNoActions); - } -} - -void FormMain::updateRecycleBinMenu() { - m_ui->m_menuRecycleBin->clear(); - - foreach (const ServiceRoot *activated_root, qApp->feedReader()->feedsModel()->serviceRoots()) { - QMenu *root_menu = new QMenu(activated_root->title(), m_ui->m_menuRecycleBin); - root_menu->setIcon(activated_root->icon()); - root_menu->setToolTip(activated_root->description()); - - RecycleBin *bin = activated_root->recycleBin(); - QList context_menu; - - if (bin == nullptr) { - QAction *no_action = new QAction(qApp->icons()->fromTheme(QSL("dialog-error")), - tr("No recycle bin"), - m_ui->m_menuRecycleBin); - no_action->setEnabled(false); - root_menu->addAction(no_action); - } - else if ((context_menu = bin->contextMenu()).isEmpty()) { - QAction *no_action = new QAction(qApp->icons()->fromTheme(QSL("dialog-error")), - tr("No actions possible"), - m_ui->m_menuRecycleBin); - no_action->setEnabled(false); - root_menu->addAction(no_action); - } - else { - root_menu->addActions(context_menu); - } - - m_ui->m_menuRecycleBin->addMenu(root_menu); - } - - if (!m_ui->m_menuRecycleBin->isEmpty()) { - m_ui->m_menuRecycleBin->addSeparator(); - } - - m_ui->m_menuRecycleBin->addAction(m_ui->m_actionRestoreAllRecycleBins); - m_ui->m_menuRecycleBin->addAction(m_ui->m_actionEmptyAllRecycleBins); -} - -void FormMain::updateAccountsMenu() { - m_ui->m_menuAccounts->clear(); - - foreach (ServiceRoot *activated_root, qApp->feedReader()->feedsModel()->serviceRoots()) { - QMenu *root_menu = new QMenu(activated_root->title(), m_ui->m_menuAccounts); - root_menu->setIcon(activated_root->icon()); - root_menu->setToolTip(activated_root->description()); - - QList root_actions = activated_root->serviceMenu(); - - if (root_actions.isEmpty()) { - QAction *no_action = new QAction(qApp->icons()->fromTheme(QSL("dialog-error")), - tr("No possible actions"), - m_ui->m_menuAccounts); - no_action->setEnabled(false); - root_menu->addAction(no_action); - } - else { - root_menu->addActions(root_actions); - } - - m_ui->m_menuAccounts->addMenu(root_menu); - } - - if (m_ui->m_menuAccounts->actions().size() > 0) { - m_ui->m_menuAccounts->addSeparator(); - } - - m_ui->m_menuAccounts->addAction(m_ui->m_actionServiceAdd); - m_ui->m_menuAccounts->addAction(m_ui->m_actionServiceEdit); - m_ui->m_menuAccounts->addAction(m_ui->m_actionServiceDelete); -} - -void FormMain::onFeedUpdatesFinished(const FeedDownloadResults &results) { - Q_UNUSED(results) - - statusBar()->clearProgressFeeds(); - tabWidget()->feedMessageViewer()->messagesView()->reloadSelections(); -} - -void FormMain::onFeedUpdatesStarted() { - m_ui->m_actionStopRunningItemsUpdate->setEnabled(true); - statusBar()->showProgressFeeds(0, tr("Feed update started")); -} - -void FormMain::onFeedUpdatesProgress(const Feed *feed, int current, int total) { - statusBar()->showProgressFeeds((current * 100.0) / total, - //: Text display in status bar when particular feed is updated. - tr("Updated feed '%1'").arg(feed->title())); -} - -void FormMain::updateMessageButtonsAvailability() { - const bool one_message_selected = tabWidget()->feedMessageViewer()->messagesView()->selectionModel()->selectedRows().size() == 1; - const bool atleast_one_message_selected = !tabWidget()->feedMessageViewer()->messagesView()->selectionModel()->selectedRows().isEmpty(); - const bool bin_loaded = tabWidget()->feedMessageViewer()->messagesView()->sourceModel()->loadedItem() != nullptr && tabWidget()->feedMessageViewer()->messagesView()->sourceModel()->loadedItem()->kind() == RootItemKind::Bin; - - m_ui->m_actionDeleteSelectedMessages->setEnabled(atleast_one_message_selected); - m_ui->m_actionRestoreSelectedMessages->setEnabled(atleast_one_message_selected && bin_loaded); - m_ui->m_actionMarkSelectedMessagesAsRead->setEnabled(atleast_one_message_selected); - m_ui->m_actionMarkSelectedMessagesAsUnread->setEnabled(atleast_one_message_selected); - m_ui->m_actionOpenSelectedMessagesInternally->setEnabled(atleast_one_message_selected); - m_ui->m_actionOpenSelectedSourceArticlesExternally->setEnabled(atleast_one_message_selected); - m_ui->m_actionSendMessageViaEmail->setEnabled(one_message_selected); - m_ui->m_actionSwitchImportanceOfSelectedMessages->setEnabled(atleast_one_message_selected); -} - -void FormMain::updateFeedButtonsAvailability() { - const bool is_update_running = qApp->feedReader()->isFeedUpdateRunning(); - const bool critical_action_running = qApp->feedUpdateLock()->isLocked(); - const RootItem *selected_item = tabWidget()->feedMessageViewer()->feedsView()->selectedItem(); - const bool anything_selected = selected_item != nullptr; - const bool feed_selected = anything_selected && selected_item->kind() == RootItemKind::Feed; - const bool category_selected = anything_selected && selected_item->kind() == RootItemKind::Category; - const bool service_selected = anything_selected && selected_item->kind() == RootItemKind::ServiceRoot; - - m_ui->m_actionStopRunningItemsUpdate->setEnabled(is_update_running); - m_ui->m_actionBackupDatabaseSettings->setEnabled(!critical_action_running); - m_ui->m_actionCleanupDatabase->setEnabled(!critical_action_running); - m_ui->m_actionClearSelectedItems->setEnabled(anything_selected); - m_ui->m_actionDeleteSelectedItem->setEnabled(!critical_action_running && anything_selected); - m_ui->m_actionEditSelectedItem->setEnabled(!critical_action_running && anything_selected); - m_ui->m_actionMarkSelectedItemsAsRead->setEnabled(anything_selected); - m_ui->m_actionMarkSelectedItemsAsUnread->setEnabled(anything_selected); - m_ui->m_actionUpdateAllItems->setEnabled(!critical_action_running); - m_ui->m_actionUpdateSelectedItems->setEnabled(!critical_action_running && (feed_selected || category_selected || service_selected)); - m_ui->m_actionViewSelectedItemsNewspaperMode->setEnabled(anything_selected); - m_ui->m_actionExpandCollapseItem->setEnabled(anything_selected); - - m_ui->m_actionServiceDelete->setEnabled(service_selected); - m_ui->m_actionServiceEdit->setEnabled(service_selected); - m_ui->m_actionAddFeedIntoSelectedAccount->setEnabled(anything_selected); - m_ui->m_actionAddCategoryIntoSelectedAccount->setEnabled(anything_selected); - - m_ui->m_menuAddItem->setEnabled(!critical_action_running); - m_ui->m_menuAccounts->setEnabled(!critical_action_running); - m_ui->m_menuRecycleBin->setEnabled(!critical_action_running); -} - -void FormMain::switchVisibility(bool force_hide) { - if (force_hide || isVisible()) { - if (SystemTrayIcon::isSystemTrayActivated()) { - hide(); - } - else { - // Window gets minimized in single-window mode. - showMinimized(); - } - } - else { - display(); - } -} - -void FormMain::display() { - // Make sure window is not minimized. - setWindowState(windowState() & ~Qt::WindowMinimized); - - // Display the window and make sure it is raised on top. - show(); - activateWindow(); - raise(); - - // Raise alert event. Check the documentation for more info on this. - Application::alert(this); -} - -void FormMain::setupIcons() { - IconFactory *icon_theme_factory = qApp->icons(); - - // Setup icons of this main window. - m_ui->m_actionDownloadManager->setIcon(icon_theme_factory->fromTheme(QSL("emblem-downloads"))); - m_ui->m_actionSettings->setIcon(icon_theme_factory->fromTheme(QSL("document-properties"))); - m_ui->m_actionQuit->setIcon(icon_theme_factory->fromTheme(QSL("application-exit"))); - m_ui->m_actionRestart->setIcon(icon_theme_factory->fromTheme(QSL("view-refresh"))); - m_ui->m_actionAboutGuard->setIcon(icon_theme_factory->fromTheme(QSL("help-about"))); - m_ui->m_actionCheckForUpdates->setIcon(icon_theme_factory->fromTheme(QSL("system-upgrade"))); - m_ui->m_actionCleanupDatabase->setIcon(icon_theme_factory->fromTheme(QSL("edit-clear"))); - m_ui->m_actionReportBug->setIcon(icon_theme_factory->fromTheme(QSL("call-start"))); - m_ui->m_actionBackupDatabaseSettings->setIcon(icon_theme_factory->fromTheme(QSL("document-export"))); - m_ui->m_actionRestoreDatabaseSettings->setIcon(icon_theme_factory->fromTheme(QSL("document-import"))); - m_ui->m_actionDonate->setIcon(icon_theme_factory->fromTheme(QSL("applications-office"))); - m_ui->m_actionDisplayWiki->setIcon(icon_theme_factory->fromTheme(QSL("applications-science"))); - - // View. - m_ui->m_actionSwitchMainWindow->setIcon(icon_theme_factory->fromTheme(QSL("window-close"))); - m_ui->m_actionFullscreen->setIcon(icon_theme_factory->fromTheme(QSL("view-fullscreen"))); - m_ui->m_actionSwitchFeedsList->setIcon(icon_theme_factory->fromTheme(QSL("view-restore"))); - m_ui->m_actionSwitchMainMenu->setIcon(icon_theme_factory->fromTheme(QSL("view-restore"))); - m_ui->m_actionSwitchToolBars->setIcon(icon_theme_factory->fromTheme(QSL("view-restore"))); - m_ui->m_actionSwitchListHeaders->setIcon(icon_theme_factory->fromTheme(QSL("view-restore"))); - m_ui->m_actionSwitchStatusBar->setIcon(icon_theme_factory->fromTheme(QSL("dialog-information"))); - m_ui->m_actionSwitchMessageListOrientation->setIcon(icon_theme_factory->fromTheme(QSL("view-restore"))); - m_ui->m_menuShowHide->setIcon(icon_theme_factory->fromTheme(QSL("view-restore"))); - - // Feeds/messages. - m_ui->m_menuAddItem->setIcon(icon_theme_factory->fromTheme(QSL("list-add"))); - m_ui->m_actionStopRunningItemsUpdate->setIcon(icon_theme_factory->fromTheme(QSL("process-stop"))); - m_ui->m_actionUpdateAllItems->setIcon(icon_theme_factory->fromTheme(QSL("view-refresh"))); - m_ui->m_actionUpdateSelectedItems->setIcon(icon_theme_factory->fromTheme(QSL("view-refresh"))); - m_ui->m_actionClearSelectedItems->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-junk"))); - m_ui->m_actionClearAllItems->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-junk"))); - m_ui->m_actionDeleteSelectedItem->setIcon(icon_theme_factory->fromTheme(QSL("list-remove"))); - m_ui->m_actionDeleteSelectedMessages->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-junk"))); - m_ui->m_actionEditSelectedItem->setIcon(icon_theme_factory->fromTheme(QSL("document-edit"))); - m_ui->m_actionMarkAllItemsRead->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-read"))); - m_ui->m_actionMarkSelectedItemsAsRead->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-read"))); - m_ui->m_actionMarkSelectedItemsAsUnread->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread"))); - m_ui->m_actionMarkSelectedMessagesAsRead->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-read"))); - m_ui->m_actionMarkSelectedMessagesAsUnread->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread"))); - m_ui->m_actionSwitchImportanceOfSelectedMessages->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-important"))); - m_ui->m_actionOpenSelectedSourceArticlesExternally->setIcon(icon_theme_factory->fromTheme(QSL("document-open"))); - m_ui->m_actionOpenSelectedMessagesInternally->setIcon(icon_theme_factory->fromTheme(QSL("document-open"))); - m_ui->m_actionSendMessageViaEmail->setIcon(icon_theme_factory->fromTheme(QSL("mail-send"))); - m_ui->m_actionViewSelectedItemsNewspaperMode->setIcon(icon_theme_factory->fromTheme(QSL("format-justify-fill"))); - m_ui->m_actionSelectNextItem->setIcon(icon_theme_factory->fromTheme(QSL("go-down"))); - m_ui->m_actionSelectPreviousItem->setIcon(icon_theme_factory->fromTheme(QSL("go-up"))); - m_ui->m_actionSelectNextMessage->setIcon(icon_theme_factory->fromTheme(QSL("go-down"))); - m_ui->m_actionSelectPreviousMessage->setIcon(icon_theme_factory->fromTheme(QSL("go-up"))); - m_ui->m_actionSelectNextUnreadMessage->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread"))); - m_ui->m_actionShowOnlyUnreadItems->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread"))); - m_ui->m_actionExpandCollapseItem->setIcon(icon_theme_factory->fromTheme(QSL("format-indent-more"))); - m_ui->m_actionRestoreSelectedMessages->setIcon(icon_theme_factory->fromTheme(QSL("view-refresh"))); - m_ui->m_actionRestoreAllRecycleBins->setIcon(icon_theme_factory->fromTheme(QSL("view-refresh"))); - m_ui->m_actionEmptyAllRecycleBins->setIcon(icon_theme_factory->fromTheme(QSL("edit-clear"))); - m_ui->m_actionServiceAdd->setIcon(icon_theme_factory->fromTheme(QSL("list-add"))); - m_ui->m_actionServiceEdit->setIcon(icon_theme_factory->fromTheme(QSL("document-edit"))); - m_ui->m_actionServiceDelete->setIcon(icon_theme_factory->fromTheme(QSL("list-remove"))); - m_ui->m_actionAddFeedIntoSelectedAccount->setIcon(icon_theme_factory->fromTheme(QSL("application-rss+xml"))); - m_ui->m_actionAddCategoryIntoSelectedAccount->setIcon(icon_theme_factory->fromTheme(QSL("folder"))); - - // Tabs & web browser. - m_ui->m_actionTabNewWebBrowser->setIcon(icon_theme_factory->fromTheme(QSL("tab-new"))); - m_ui->m_actionTabsCloseAll->setIcon(icon_theme_factory->fromTheme(QSL("window-close"))); - m_ui->m_actionTabsCloseAllExceptCurrent->setIcon(icon_theme_factory->fromTheme(QSL("window-close"))); - - // Setup icons on TabWidget too. - m_ui->m_tabWidget->setupIcons(); -} - -void FormMain::loadSize() { - const QRect screen = qApp->desktop()->screenGeometry(); - const Settings *settings = qApp->settings(); - - // Reload main window size & position. - resize(settings->value(GROUP(GUI), GUI::MainWindowInitialSize, size()).toSize()); - move(settings->value(GROUP(GUI), GUI::MainWindowInitialPosition, screen.center() - rect().center()).toPoint()); - - if (settings->value(GROUP(GUI), SETTING(GUI::MainWindowStartsMaximized)).toBool()) { - setWindowState(windowState() | Qt::WindowMaximized); - - // We process events so that window is really maximized fast. - qApp->processEvents(); - } - - // If user exited the application while in fullsreen mode, - // then re-enable it now. - if (settings->value(GROUP(GUI), SETTING(GUI::MainWindowStartsFullscreen)).toBool()) { - m_ui->m_actionFullscreen->setChecked(true); - } - - // Hide the main menu if user wants it. - m_ui->m_actionSwitchMainMenu->setChecked(settings->value(GROUP(GUI), SETTING(GUI::MainMenuVisible)).toBool()); - - // Adjust dimensions of "feeds & messages" widget. - m_ui->m_tabWidget->feedMessageViewer()->loadSize(); - m_ui->m_actionSwitchToolBars->setChecked(settings->value(GROUP(GUI), SETTING(GUI::ToolbarsVisible)).toBool()); - m_ui->m_actionSwitchListHeaders->setChecked(settings->value(GROUP(GUI), SETTING(GUI::ListHeadersVisible)).toBool()); - m_ui->m_actionSwitchStatusBar->setChecked(settings->value(GROUP(GUI), SETTING(GUI::StatusBarVisible)).toBool()); - - // Make sure that only unread feeds are shown if user has that feature set on. - m_ui->m_actionShowOnlyUnreadItems->setChecked(settings->value(GROUP(Feeds), SETTING(Feeds::ShowOnlyUnreadFeeds)).toBool()); -} - -void FormMain::saveSize() { - Settings *settings = qApp->settings(); - bool is_fullscreen = isFullScreen(); - bool is_maximized = false; - - if (is_fullscreen) { - m_ui->m_actionFullscreen->setChecked(false); - - // We (process events to really) un-fullscreen, so that we can determine if window is really maximized. - qApp->processEvents(); - } - - if (isMaximized()) { - is_maximized = true; - - // Window is maximized, we store that fact to settings and unmaximize. - qApp->settings()->setValue(GROUP(GUI), GUI::IsMainWindowMaximizedBeforeFullscreen, isMaximized()); - setWindowState((windowState() & ~Qt::WindowMaximized) | Qt::WindowActive); - - // We process events to really have window un-maximized. - qApp->processEvents(); - } - - settings->setValue(GROUP(GUI), GUI::MainMenuVisible, m_ui->m_actionSwitchMainMenu->isChecked()); - settings->setValue(GROUP(GUI), GUI::MainWindowInitialPosition, pos()); - settings->setValue(GROUP(GUI), GUI::MainWindowInitialSize, size()); - settings->setValue(GROUP(GUI), GUI::MainWindowStartsMaximized, is_maximized); - settings->setValue(GROUP(GUI), GUI::MainWindowStartsFullscreen, is_fullscreen); - settings->setValue(GROUP(GUI), GUI::StatusBarVisible, m_ui->m_actionSwitchStatusBar->isChecked()); - - m_ui->m_tabWidget->feedMessageViewer()->saveSize(); -} - -void FormMain::createConnections() { - // Status bar connections. - connect(m_ui->m_menuAddItem, &QMenu::aboutToShow, this, &FormMain::updateAddItemMenu); - connect(m_ui->m_menuRecycleBin, &QMenu::aboutToShow, this, &FormMain::updateRecycleBinMenu); - connect(m_ui->m_menuAccounts, &QMenu::aboutToShow, this, &FormMain::updateAccountsMenu); - - connect(m_ui->m_actionServiceDelete, &QAction::triggered, m_ui->m_actionDeleteSelectedItem, &QAction::triggered); - connect(m_ui->m_actionServiceEdit, &QAction::triggered, m_ui->m_actionEditSelectedItem, &QAction::triggered); - - // Menu "File" connections. - connect(m_ui->m_actionBackupDatabaseSettings, &QAction::triggered, this, &FormMain::backupDatabaseSettings); - connect(m_ui->m_actionRestoreDatabaseSettings, &QAction::triggered, this, &FormMain::restoreDatabaseSettings); - connect(m_ui->m_actionQuit, &QAction::triggered, qApp, &Application::quit); - connect(m_ui->m_actionServiceAdd, &QAction::triggered, this, &FormMain::showAddAccountDialog); - connect(m_ui->m_actionRestart, &QAction::triggered, qApp, &Application::restart); - - // Menu "View" connections. - connect(m_ui->m_actionFullscreen, &QAction::toggled, this, &FormMain::switchFullscreenMode); - connect(m_ui->m_actionSwitchMainMenu, &QAction::toggled, m_ui->m_menuBar, &QMenuBar::setVisible); - connect(m_ui->m_actionSwitchMainWindow, &QAction::triggered, this, &FormMain::switchVisibility); - connect(m_ui->m_actionSwitchStatusBar, &QAction::toggled, statusBar(), &StatusBar::setVisible); - - // Menu "Tools" connections. - connect(m_ui->m_actionSettings, &QAction::triggered, this, &FormMain::showSettings); - connect(m_ui->m_actionDownloadManager, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::showDownloadManager); - connect(m_ui->m_actionCleanupDatabase, &QAction::triggered, this, &FormMain::showDbCleanupAssistant); - - // Menu "Help" connections. - connect(m_ui->m_actionAboutGuard, &QAction::triggered, this, &FormMain::showAbout); - connect(m_ui->m_actionCheckForUpdates, &QAction::triggered, this, &FormMain::showUpdates); - connect(m_ui->m_actionReportBug, &QAction::triggered, this, &FormMain::reportABug); - connect(m_ui->m_actionDonate, &QAction::triggered, this, &FormMain::donate); - connect(m_ui->m_actionDisplayWiki, &QAction::triggered, this, &FormMain::showWiki); - - // Tab widget connections. - connect(m_ui->m_actionTabsCloseAllExceptCurrent, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::closeAllTabsExceptCurrent); - connect(m_ui->m_actionTabsCloseAll, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::closeAllTabs); - connect(m_ui->m_actionTabNewWebBrowser, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::addEmptyBrowser); - - connect(tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::itemSelected, this, &FormMain::updateFeedButtonsAvailability); - connect(qApp->feedUpdateLock(), &Mutex::locked, this, &FormMain::updateFeedButtonsAvailability); - connect(qApp->feedUpdateLock(), &Mutex::unlocked, this, &FormMain::updateFeedButtonsAvailability); - - connect(tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::currentMessageRemoved, - this, &FormMain::updateMessageButtonsAvailability); - connect(tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::currentMessageChanged, - this, &FormMain::updateMessageButtonsAvailability); - - connect(qApp->feedReader(), &FeedReader::feedUpdatesStarted, this, &FormMain::onFeedUpdatesStarted); - connect(qApp->feedReader(), &FeedReader::feedUpdatesProgress, this, &FormMain::onFeedUpdatesProgress); - connect(qApp->feedReader(), &FeedReader::feedUpdatesFinished, this, &FormMain::onFeedUpdatesFinished); - - // Toolbar forwardings. - connect(m_ui->m_actionAddFeedIntoSelectedAccount, &QAction::triggered, - tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::addFeedIntoSelectedAccount); - connect(m_ui->m_actionAddCategoryIntoSelectedAccount, &QAction::triggered, - tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::addCategoryIntoSelectedAccount); - connect(m_ui->m_actionSwitchImportanceOfSelectedMessages, - &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::switchSelectedMessagesImportance); - connect(m_ui->m_actionDeleteSelectedMessages, - &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::deleteSelectedMessages); - connect(m_ui->m_actionMarkSelectedMessagesAsRead, - &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::markSelectedMessagesRead); - connect(m_ui->m_actionMarkSelectedMessagesAsUnread, &QAction::triggered, - tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::markSelectedMessagesUnread); - connect(m_ui->m_actionOpenSelectedSourceArticlesExternally, - &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::openSelectedSourceMessagesExternally); - connect(m_ui->m_actionOpenSelectedMessagesInternally, - &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::openSelectedMessagesInternally); - connect(m_ui->m_actionSendMessageViaEmail, - &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::sendSelectedMessageViaEmail); - connect(m_ui->m_actionMarkAllItemsRead, - &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::markAllItemsRead); - connect(m_ui->m_actionMarkSelectedItemsAsRead, - &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::markSelectedItemRead); - connect(m_ui->m_actionExpandCollapseItem, - &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::expandCollapseCurrentItem); - connect(m_ui->m_actionMarkSelectedItemsAsUnread, - &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::markSelectedItemUnread); - connect(m_ui->m_actionClearSelectedItems, - &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::clearSelectedFeeds); - connect(m_ui->m_actionClearAllItems, - &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::clearAllFeeds); - connect(m_ui->m_actionUpdateSelectedItems, - &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::updateSelectedItems); - connect(m_ui->m_actionUpdateAllItems, - &QAction::triggered, qApp->feedReader(), &FeedReader::updateAllFeeds); - connect(m_ui->m_actionStopRunningItemsUpdate, - &QAction::triggered, qApp->feedReader(), &FeedReader::stopRunningFeedUpdate); - connect(m_ui->m_actionEditSelectedItem, - &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::editSelectedItem); - connect(m_ui->m_actionViewSelectedItemsNewspaperMode, - &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::openSelectedItemsInNewspaperMode); - connect(m_ui->m_actionDeleteSelectedItem, - &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::deleteSelectedItem); - connect(m_ui->m_actionSwitchFeedsList, &QAction::triggered, - tabWidget()->feedMessageViewer(), &FeedMessageViewer::switchFeedComponentVisibility); - connect(m_ui->m_actionSelectNextItem, - &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::selectNextItem); - connect(m_ui->m_actionSwitchToolBars, &QAction::toggled, - tabWidget()->feedMessageViewer(), &FeedMessageViewer::setToolBarsEnabled); - connect(m_ui->m_actionSwitchListHeaders, &QAction::toggled, - tabWidget()->feedMessageViewer(), &FeedMessageViewer::setListHeadersEnabled); - connect(m_ui->m_actionSelectPreviousItem, - &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::selectPreviousItem); - connect(m_ui->m_actionSelectNextMessage, - &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::selectNextItem); - connect(m_ui->m_actionSelectNextUnreadMessage, - &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::selectNextUnreadItem); - connect(m_ui->m_actionSelectPreviousMessage, - &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::selectPreviousItem); - connect(m_ui->m_actionSwitchMessageListOrientation, &QAction::triggered, - tabWidget()->feedMessageViewer(), &FeedMessageViewer::switchMessageSplitterOrientation); - connect(m_ui->m_actionShowOnlyUnreadItems, &QAction::toggled, - tabWidget()->feedMessageViewer(), &FeedMessageViewer::toggleShowOnlyUnreadFeeds); - connect(m_ui->m_actionRestoreSelectedMessages, &QAction::triggered, - tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::restoreSelectedMessages); - connect(m_ui->m_actionRestoreAllRecycleBins, &QAction::triggered, - tabWidget()->feedMessageViewer()->feedsView()->sourceModel(), &FeedsModel::restoreAllBins); - connect(m_ui->m_actionEmptyAllRecycleBins, &QAction::triggered, - tabWidget()->feedMessageViewer()->feedsView()->sourceModel(), &FeedsModel::emptyAllBins); -} - -void FormMain::backupDatabaseSettings() { - QScopedPointer form(new FormBackupDatabaseSettings(this)); - form->exec(); -} - -void FormMain::restoreDatabaseSettings() { - QScopedPointer form(new FormRestoreDatabaseSettings(this)); - form->exec(); - - if (form->shouldRestart()) { - qApp->restart(); - } -} - -void FormMain::changeEvent(QEvent *event) { - switch (event->type()) { - case QEvent::WindowStateChange: { - if (windowState() & Qt::WindowMinimized && - SystemTrayIcon::isSystemTrayActivated() && - qApp->settings()->value(GROUP(GUI), SETTING(GUI::HideMainWindowWhenMinimized)).toBool()) { - event->ignore(); - QTimer::singleShot(CHANGE_EVENT_DELAY, this, SLOT(switchVisibility())); - } - - break; - } - - default: - break; - } - - QMainWindow::changeEvent(event); -} - -void FormMain::showAbout() { - QScopedPointer form_pointer(new FormAbout(this)); - form_pointer->exec(); -} - -void FormMain::showUpdates() { - QScopedPointer form_update(new FormUpdate(this)); - form_update->exec(); -} - -void FormMain::showWiki() { - if (!WebFactory::instance()->openUrlInExternalBrowser(APP_URL_WIKI)) { - qApp->showGuiMessage(tr("Cannot open external browser"), - tr("Cannot open external browser. Navigate to application website manually."), - QSystemTrayIcon::Warning, this, true); - } -} - -void FormMain::showAddAccountDialog() { - QScopedPointer form_update(new FormAddAccount(qApp->feedReader()->feedServices(), - qApp->feedReader()->feedsModel(), - this)); - form_update->exec(); -} - -void FormMain::reportABug() { - if (!WebFactory::instance()->openUrlInExternalBrowser(QSL(APP_URL_ISSUES_NEW))) { - qApp->showGuiMessage(tr("Cannot open external browser"), - tr("Cannot open external browser. Navigate to application website manually."), - QSystemTrayIcon::Warning, this, true); - } -} - -void FormMain::donate() { - if (!WebFactory::instance()->openUrlInExternalBrowser(QSL(APP_DONATE_URL))) { - qApp->showGuiMessage(tr("Cannot open external browser"), - tr("Cannot open external browser. Navigate to application website manually."), - QSystemTrayIcon::Warning, this, true); - } -} - -void FormMain::showSettings() { - QScopedPointer form_pointer(new FormSettings(this)); - form_pointer->exec(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/dialogs/formmain.h" + +#include "definitions/definitions.h" +#include "miscellaneous/settings.h" +#include "miscellaneous/application.h" +#include "miscellaneous/systemfactory.h" +#include "miscellaneous/mutex.h" +#include "miscellaneous/databasefactory.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/feedreader.h" +#include "network-web/webfactory.h" +#include "gui/feedsview.h" +#include "gui/messagebox.h" +#include "gui/systemtrayicon.h" +#include "gui/tabbar.h" +#include "gui/statusbar.h" +#include "gui/messagesview.h" +#include "gui/feedmessageviewer.h" +#include "gui/plaintoolbutton.h" +#include "gui/feedstoolbar.h" +#include "gui/messagestoolbar.h" +#include "gui/dialogs/formabout.h" +#include "gui/dialogs/formsettings.h" +#include "gui/dialogs/formupdate.h" +#include "gui/dialogs/formdatabasecleanup.h" +#include "gui/dialogs/formbackupdatabasesettings.h" +#include "gui/dialogs/formrestoredatabasesettings.h" +#include "gui/dialogs/formaddaccount.h" +#include "services/abstract/serviceroot.h" +#include "services/abstract/recyclebin.h" +#include "services/standard/gui/formstandardimportexport.h" +#include "services/owncloud/network/owncloudnetworkfactory.h" + +#include +#include +#include +#include +#include +#include + +#if defined(USE_WEBENGINE) +#include "network-web/adblock/adblockicon.h" +#endif + + +FormMain::FormMain(QWidget* parent, Qt::WindowFlags f) + : QMainWindow(parent, f), m_ui(new Ui::FormMain) { + m_ui->setupUi(this); +#if defined(USE_WEBENGINE) + m_adblockIcon = new AdBlockIcon(this); + m_adblockIconAction = m_adblockIcon->menuAction(); + m_adblockIconAction->setObjectName(QSL("m_adblockIconAction")); + m_ui->m_menuTools->addAction(m_adblockIconAction); +#endif + qApp->setMainForm(this); + // Add these actions to the list of actions of the main window. + // This allows to use actions via shortcuts + // even if main menu is not visible. + addActions(allActions()); +#if defined(USE_WEBENGINE) + addAction(m_adblockIconAction); +#endif + m_statusBar = new StatusBar(this); + setStatusBar(m_statusBar); + // Prepare main window and tabs. + prepareMenus(); + // Prepare tabs. + //m_ui->m_tabWidget->initializeTabs(); + tabWidget()->feedMessageViewer()->feedsToolBar()->loadSavedActions(); + tabWidget()->feedMessageViewer()->messagesToolBar()->loadSavedActions(); + // Establish connections. + createConnections(); + updateMessageButtonsAvailability(); + updateFeedButtonsAvailability(); + // Setup some appearance of the window. + setupIcons(); + loadSize(); + m_statusBar->loadSavedActions(); +} + +FormMain::~FormMain() { + qDebug("Destroying FormMain instance."); +} + +QMenu* FormMain::trayMenu() const { + return m_trayMenu; +} + +TabWidget* FormMain::tabWidget() const { + return m_ui->m_tabWidget; +} + +StatusBar* FormMain::statusBar() const { + return m_statusBar; +} + +void FormMain::showDbCleanupAssistant() { + if (qApp->feedUpdateLock()->tryLock()) { + QScopedPointer form_pointer(new FormDatabaseCleanup(this)); + form_pointer.data()->setCleaner(qApp->feedReader()->databaseCleaner()); + form_pointer.data()->exec(); + qApp->feedUpdateLock()->unlock(); + tabWidget()->feedMessageViewer()->messagesView()->reloadSelections(); + qApp->feedReader()->feedsModel()->reloadCountsOfWholeModel(); + } + + else { + qApp->showGuiMessage(tr("Cannot cleanup database"), + tr("Cannot cleanup database, because another critical action is running."), + QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); + } +} + +QList FormMain::allActions() const { + QList actions; + // Add basic actions. + actions << m_ui->m_actionSettings; + actions << m_ui->m_actionDownloadManager; + actions << m_ui->m_actionRestoreDatabaseSettings; + actions << m_ui->m_actionBackupDatabaseSettings; + actions << m_ui->m_actionRestart; + actions << m_ui->m_actionQuit; +#if !defined(Q_OS_MAC) + actions << m_ui->m_actionFullscreen; +#endif + actions << m_ui->m_actionAboutGuard; + actions << m_ui->m_actionSwitchFeedsList; + actions << m_ui->m_actionSwitchMainWindow; +#if !defined(Q_OS_MAC) + actions << m_ui->m_actionSwitchMainMenu; +#endif + actions << m_ui->m_actionSwitchToolBars; + actions << m_ui->m_actionSwitchListHeaders; + actions << m_ui->m_actionSwitchStatusBar; + actions << m_ui->m_actionSwitchMessageListOrientation; + // Add feeds/messages actions. + actions << m_ui->m_actionOpenSelectedSourceArticlesExternally; + actions << m_ui->m_actionOpenSelectedMessagesInternally; + actions << m_ui->m_actionMarkAllItemsRead; + actions << m_ui->m_actionMarkSelectedItemsAsRead; + actions << m_ui->m_actionMarkSelectedItemsAsUnread; + actions << m_ui->m_actionClearSelectedItems; + actions << m_ui->m_actionClearAllItems; + actions << m_ui->m_actionShowOnlyUnreadItems; + actions << m_ui->m_actionMarkSelectedMessagesAsRead; + actions << m_ui->m_actionMarkSelectedMessagesAsUnread; + actions << m_ui->m_actionSwitchImportanceOfSelectedMessages; + actions << m_ui->m_actionDeleteSelectedMessages; + actions << m_ui->m_actionUpdateAllItems; + actions << m_ui->m_actionUpdateSelectedItems; + actions << m_ui->m_actionStopRunningItemsUpdate; + actions << m_ui->m_actionEditSelectedItem; + actions << m_ui->m_actionDeleteSelectedItem; + actions << m_ui->m_actionServiceAdd; + actions << m_ui->m_actionServiceEdit; + actions << m_ui->m_actionServiceDelete; + actions << m_ui->m_actionCleanupDatabase; + actions << m_ui->m_actionAddFeedIntoSelectedAccount; + actions << m_ui->m_actionAddCategoryIntoSelectedAccount; + actions << m_ui->m_actionViewSelectedItemsNewspaperMode; + actions << m_ui->m_actionSelectNextItem; + actions << m_ui->m_actionSelectPreviousItem; + actions << m_ui->m_actionSelectNextMessage; + actions << m_ui->m_actionSelectPreviousMessage; + actions << m_ui->m_actionSelectNextUnreadMessage; + actions << m_ui->m_actionExpandCollapseItem; +#if defined(USE_WEBENGINE) + actions << m_ui->m_actionTabNewWebBrowser; + actions << m_adblockIconAction; +#endif + actions << m_ui->m_actionTabsCloseAll; + actions << m_ui->m_actionTabsCloseAllExceptCurrent; + return actions; +} + +void FormMain::prepareMenus() { + // Setup menu for tray icon. + if (SystemTrayIcon::isSystemTrayAvailable()) { +#if defined(Q_OS_WIN) + m_trayMenu = new TrayIconMenu(APP_NAME, this); +#else + m_trayMenu = new QMenu(QSL(APP_NAME), this); +#endif + // Add needed items to the menu. + m_trayMenu->addAction(m_ui->m_actionSwitchMainWindow); + m_trayMenu->addSeparator(); + m_trayMenu->addAction(m_ui->m_actionUpdateAllItems); + m_trayMenu->addAction(m_ui->m_actionMarkAllItemsRead); + m_trayMenu->addSeparator(); + m_trayMenu->addAction(m_ui->m_actionSettings); + m_trayMenu->addAction(m_ui->m_actionQuit); + qDebug("Creating tray icon menu."); + } + +#if !defined(USE_WEBENGINE) + m_ui->m_menuWebBrowserTabs->removeAction(m_ui->m_actionTabNewWebBrowser); + m_ui->m_menuWebBrowserTabs->setTitle(tr("Tabs")); +#endif +#if defined(Q_OS_MAC) + m_ui->m_actionSwitchMainMenu->setVisible(false); + m_ui->m_actionFullscreen->setVisible(false); +#endif +} + +void FormMain::switchFullscreenMode() { + if (!isFullScreen()) { + qApp->settings()->setValue(GROUP(GUI), GUI::IsMainWindowMaximizedBeforeFullscreen, isMaximized()); + showFullScreen(); + } + + else { + if (qApp->settings()->value(GROUP(GUI), SETTING(GUI::IsMainWindowMaximizedBeforeFullscreen)).toBool()) { + setWindowState((windowState() & ~Qt::WindowFullScreen) | Qt::WindowMaximized); + } + + else { + showNormal(); + } + } +} + +void FormMain::updateAddItemMenu() { + // NOTE: Clear here deletes items from memory but only those OWNED by the menu. + m_ui->m_menuAddItem->clear(); + + foreach (ServiceRoot* activated_root, qApp->feedReader()->feedsModel()->serviceRoots()) { + QMenu* root_menu = new QMenu(activated_root->title(), m_ui->m_menuAddItem); + root_menu->setIcon(activated_root->icon()); + root_menu->setToolTip(activated_root->description()); + QList specific_root_actions = activated_root->addItemMenu(); + + if (activated_root->supportsCategoryAdding()) { + QAction* action_new_category = new QAction(qApp->icons()->fromTheme(QSL("folder")), + tr("Add new category"), + m_ui->m_menuAddItem); + root_menu->addAction(action_new_category); + connect(action_new_category, &QAction::triggered, activated_root, &ServiceRoot::addNewCategory); + } + + if (activated_root->supportsFeedAdding()) { + QAction* action_new_feed = new QAction(qApp->icons()->fromTheme(QSL("application-rss+xml")), + tr("Add new feed"), + m_ui->m_menuAddItem); + root_menu->addAction(action_new_feed); + // NOTE: Because of default arguments. + connect(action_new_feed, SIGNAL(triggered(bool)), activated_root, SLOT(addNewFeed())); + } + + if (!specific_root_actions.isEmpty()) { + if (!root_menu->isEmpty()) { + root_menu->addSeparator(); + } + + root_menu->addActions(specific_root_actions); + } + + m_ui->m_menuAddItem->addMenu(root_menu); + } + + if (!m_ui->m_menuAddItem->isEmpty()) { + m_ui->m_menuAddItem->addSeparator(); + m_ui->m_menuAddItem->addAction(m_ui->m_actionAddCategoryIntoSelectedAccount); + m_ui->m_menuAddItem->addAction(m_ui->m_actionAddFeedIntoSelectedAccount); + } + + else { + m_ui->m_menuAddItem->addAction(m_ui->m_actionNoActions); + } +} + +void FormMain::updateRecycleBinMenu() { + m_ui->m_menuRecycleBin->clear(); + + foreach (const ServiceRoot* activated_root, qApp->feedReader()->feedsModel()->serviceRoots()) { + QMenu* root_menu = new QMenu(activated_root->title(), m_ui->m_menuRecycleBin); + root_menu->setIcon(activated_root->icon()); + root_menu->setToolTip(activated_root->description()); + RecycleBin* bin = activated_root->recycleBin(); + QList context_menu; + + if (bin == nullptr) { + QAction* no_action = new QAction(qApp->icons()->fromTheme(QSL("dialog-error")), + tr("No recycle bin"), + m_ui->m_menuRecycleBin); + no_action->setEnabled(false); + root_menu->addAction(no_action); + } + + else if ((context_menu = bin->contextMenu()).isEmpty()) { + QAction* no_action = new QAction(qApp->icons()->fromTheme(QSL("dialog-error")), + tr("No actions possible"), + m_ui->m_menuRecycleBin); + no_action->setEnabled(false); + root_menu->addAction(no_action); + } + + else { + root_menu->addActions(context_menu); + } + + m_ui->m_menuRecycleBin->addMenu(root_menu); + } + + if (!m_ui->m_menuRecycleBin->isEmpty()) { + m_ui->m_menuRecycleBin->addSeparator(); + } + + m_ui->m_menuRecycleBin->addAction(m_ui->m_actionRestoreAllRecycleBins); + m_ui->m_menuRecycleBin->addAction(m_ui->m_actionEmptyAllRecycleBins); +} + +void FormMain::updateAccountsMenu() { + m_ui->m_menuAccounts->clear(); + + foreach (ServiceRoot* activated_root, qApp->feedReader()->feedsModel()->serviceRoots()) { + QMenu* root_menu = new QMenu(activated_root->title(), m_ui->m_menuAccounts); + root_menu->setIcon(activated_root->icon()); + root_menu->setToolTip(activated_root->description()); + QList root_actions = activated_root->serviceMenu(); + + if (root_actions.isEmpty()) { + QAction* no_action = new QAction(qApp->icons()->fromTheme(QSL("dialog-error")), + tr("No possible actions"), + m_ui->m_menuAccounts); + no_action->setEnabled(false); + root_menu->addAction(no_action); + } + + else { + root_menu->addActions(root_actions); + } + + m_ui->m_menuAccounts->addMenu(root_menu); + } + + if (m_ui->m_menuAccounts->actions().size() > 0) { + m_ui->m_menuAccounts->addSeparator(); + } + + m_ui->m_menuAccounts->addAction(m_ui->m_actionServiceAdd); + m_ui->m_menuAccounts->addAction(m_ui->m_actionServiceEdit); + m_ui->m_menuAccounts->addAction(m_ui->m_actionServiceDelete); +} + +void FormMain::onFeedUpdatesFinished(const FeedDownloadResults& results) { + Q_UNUSED(results) + statusBar()->clearProgressFeeds(); + tabWidget()->feedMessageViewer()->messagesView()->reloadSelections(); +} + +void FormMain::onFeedUpdatesStarted() { + m_ui->m_actionStopRunningItemsUpdate->setEnabled(true); + statusBar()->showProgressFeeds(0, tr("Feed update started")); +} + +void FormMain::onFeedUpdatesProgress(const Feed* feed, int current, int total) { + statusBar()->showProgressFeeds((current * 100.0) / total, + //: Text display in status bar when particular feed is updated. + tr("Updated feed '%1'").arg(feed->title())); +} + +void FormMain::updateMessageButtonsAvailability() { + const bool one_message_selected = tabWidget()->feedMessageViewer()->messagesView()->selectionModel()->selectedRows().size() == 1; + const bool atleast_one_message_selected = !tabWidget()->feedMessageViewer()->messagesView()->selectionModel()->selectedRows().isEmpty(); + const bool bin_loaded = tabWidget()->feedMessageViewer()->messagesView()->sourceModel()->loadedItem() != nullptr + && tabWidget()->feedMessageViewer()->messagesView()->sourceModel()->loadedItem()->kind() == RootItemKind::Bin; + m_ui->m_actionDeleteSelectedMessages->setEnabled(atleast_one_message_selected); + m_ui->m_actionRestoreSelectedMessages->setEnabled(atleast_one_message_selected && bin_loaded); + m_ui->m_actionMarkSelectedMessagesAsRead->setEnabled(atleast_one_message_selected); + m_ui->m_actionMarkSelectedMessagesAsUnread->setEnabled(atleast_one_message_selected); + m_ui->m_actionOpenSelectedMessagesInternally->setEnabled(atleast_one_message_selected); + m_ui->m_actionOpenSelectedSourceArticlesExternally->setEnabled(atleast_one_message_selected); + m_ui->m_actionSendMessageViaEmail->setEnabled(one_message_selected); + m_ui->m_actionSwitchImportanceOfSelectedMessages->setEnabled(atleast_one_message_selected); +} + +void FormMain::updateFeedButtonsAvailability() { + const bool is_update_running = qApp->feedReader()->isFeedUpdateRunning(); + const bool critical_action_running = qApp->feedUpdateLock()->isLocked(); + const RootItem* selected_item = tabWidget()->feedMessageViewer()->feedsView()->selectedItem(); + const bool anything_selected = selected_item != nullptr; + const bool feed_selected = anything_selected && selected_item->kind() == RootItemKind::Feed; + const bool category_selected = anything_selected && selected_item->kind() == RootItemKind::Category; + const bool service_selected = anything_selected && selected_item->kind() == RootItemKind::ServiceRoot; + m_ui->m_actionStopRunningItemsUpdate->setEnabled(is_update_running); + m_ui->m_actionBackupDatabaseSettings->setEnabled(!critical_action_running); + m_ui->m_actionCleanupDatabase->setEnabled(!critical_action_running); + m_ui->m_actionClearSelectedItems->setEnabled(anything_selected); + m_ui->m_actionDeleteSelectedItem->setEnabled(!critical_action_running && anything_selected); + m_ui->m_actionEditSelectedItem->setEnabled(!critical_action_running && anything_selected); + m_ui->m_actionMarkSelectedItemsAsRead->setEnabled(anything_selected); + m_ui->m_actionMarkSelectedItemsAsUnread->setEnabled(anything_selected); + m_ui->m_actionUpdateAllItems->setEnabled(!critical_action_running); + m_ui->m_actionUpdateSelectedItems->setEnabled(!critical_action_running && (feed_selected || category_selected || service_selected)); + m_ui->m_actionViewSelectedItemsNewspaperMode->setEnabled(anything_selected); + m_ui->m_actionExpandCollapseItem->setEnabled(anything_selected); + m_ui->m_actionServiceDelete->setEnabled(service_selected); + m_ui->m_actionServiceEdit->setEnabled(service_selected); + m_ui->m_actionAddFeedIntoSelectedAccount->setEnabled(anything_selected); + m_ui->m_actionAddCategoryIntoSelectedAccount->setEnabled(anything_selected); + m_ui->m_menuAddItem->setEnabled(!critical_action_running); + m_ui->m_menuAccounts->setEnabled(!critical_action_running); + m_ui->m_menuRecycleBin->setEnabled(!critical_action_running); +} + +void FormMain::switchVisibility(bool force_hide) { + if (force_hide || isVisible()) { + if (SystemTrayIcon::isSystemTrayActivated()) { + hide(); + } + + else { + // Window gets minimized in single-window mode. + showMinimized(); + } + } + + else { + display(); + } +} + +void FormMain::display() { + // Make sure window is not minimized. + setWindowState(windowState() & ~Qt::WindowMinimized); + // Display the window and make sure it is raised on top. + show(); + activateWindow(); + raise(); + // Raise alert event. Check the documentation for more info on this. + Application::alert(this); +} + +void FormMain::setupIcons() { + IconFactory* icon_theme_factory = qApp->icons(); + // Setup icons of this main window. + m_ui->m_actionDownloadManager->setIcon(icon_theme_factory->fromTheme(QSL("emblem-downloads"))); + m_ui->m_actionSettings->setIcon(icon_theme_factory->fromTheme(QSL("document-properties"))); + m_ui->m_actionQuit->setIcon(icon_theme_factory->fromTheme(QSL("application-exit"))); + m_ui->m_actionRestart->setIcon(icon_theme_factory->fromTheme(QSL("view-refresh"))); + m_ui->m_actionAboutGuard->setIcon(icon_theme_factory->fromTheme(QSL("help-about"))); + m_ui->m_actionCheckForUpdates->setIcon(icon_theme_factory->fromTheme(QSL("system-upgrade"))); + m_ui->m_actionCleanupDatabase->setIcon(icon_theme_factory->fromTheme(QSL("edit-clear"))); + m_ui->m_actionReportBug->setIcon(icon_theme_factory->fromTheme(QSL("call-start"))); + m_ui->m_actionBackupDatabaseSettings->setIcon(icon_theme_factory->fromTheme(QSL("document-export"))); + m_ui->m_actionRestoreDatabaseSettings->setIcon(icon_theme_factory->fromTheme(QSL("document-import"))); + m_ui->m_actionDonate->setIcon(icon_theme_factory->fromTheme(QSL("applications-office"))); + m_ui->m_actionDisplayWiki->setIcon(icon_theme_factory->fromTheme(QSL("applications-science"))); + // View. + m_ui->m_actionSwitchMainWindow->setIcon(icon_theme_factory->fromTheme(QSL("window-close"))); + m_ui->m_actionFullscreen->setIcon(icon_theme_factory->fromTheme(QSL("view-fullscreen"))); + m_ui->m_actionSwitchFeedsList->setIcon(icon_theme_factory->fromTheme(QSL("view-restore"))); + m_ui->m_actionSwitchMainMenu->setIcon(icon_theme_factory->fromTheme(QSL("view-restore"))); + m_ui->m_actionSwitchToolBars->setIcon(icon_theme_factory->fromTheme(QSL("view-restore"))); + m_ui->m_actionSwitchListHeaders->setIcon(icon_theme_factory->fromTheme(QSL("view-restore"))); + m_ui->m_actionSwitchStatusBar->setIcon(icon_theme_factory->fromTheme(QSL("dialog-information"))); + m_ui->m_actionSwitchMessageListOrientation->setIcon(icon_theme_factory->fromTheme(QSL("view-restore"))); + m_ui->m_menuShowHide->setIcon(icon_theme_factory->fromTheme(QSL("view-restore"))); + // Feeds/messages. + m_ui->m_menuAddItem->setIcon(icon_theme_factory->fromTheme(QSL("list-add"))); + m_ui->m_actionStopRunningItemsUpdate->setIcon(icon_theme_factory->fromTheme(QSL("process-stop"))); + m_ui->m_actionUpdateAllItems->setIcon(icon_theme_factory->fromTheme(QSL("view-refresh"))); + m_ui->m_actionUpdateSelectedItems->setIcon(icon_theme_factory->fromTheme(QSL("view-refresh"))); + m_ui->m_actionClearSelectedItems->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-junk"))); + m_ui->m_actionClearAllItems->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-junk"))); + m_ui->m_actionDeleteSelectedItem->setIcon(icon_theme_factory->fromTheme(QSL("list-remove"))); + m_ui->m_actionDeleteSelectedMessages->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-junk"))); + m_ui->m_actionEditSelectedItem->setIcon(icon_theme_factory->fromTheme(QSL("document-edit"))); + m_ui->m_actionMarkAllItemsRead->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-read"))); + m_ui->m_actionMarkSelectedItemsAsRead->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-read"))); + m_ui->m_actionMarkSelectedItemsAsUnread->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread"))); + m_ui->m_actionMarkSelectedMessagesAsRead->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-read"))); + m_ui->m_actionMarkSelectedMessagesAsUnread->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread"))); + m_ui->m_actionSwitchImportanceOfSelectedMessages->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-important"))); + m_ui->m_actionOpenSelectedSourceArticlesExternally->setIcon(icon_theme_factory->fromTheme(QSL("document-open"))); + m_ui->m_actionOpenSelectedMessagesInternally->setIcon(icon_theme_factory->fromTheme(QSL("document-open"))); + m_ui->m_actionSendMessageViaEmail->setIcon(icon_theme_factory->fromTheme(QSL("mail-send"))); + m_ui->m_actionViewSelectedItemsNewspaperMode->setIcon(icon_theme_factory->fromTheme(QSL("format-justify-fill"))); + m_ui->m_actionSelectNextItem->setIcon(icon_theme_factory->fromTheme(QSL("go-down"))); + m_ui->m_actionSelectPreviousItem->setIcon(icon_theme_factory->fromTheme(QSL("go-up"))); + m_ui->m_actionSelectNextMessage->setIcon(icon_theme_factory->fromTheme(QSL("go-down"))); + m_ui->m_actionSelectPreviousMessage->setIcon(icon_theme_factory->fromTheme(QSL("go-up"))); + m_ui->m_actionSelectNextUnreadMessage->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread"))); + m_ui->m_actionShowOnlyUnreadItems->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread"))); + m_ui->m_actionExpandCollapseItem->setIcon(icon_theme_factory->fromTheme(QSL("format-indent-more"))); + m_ui->m_actionRestoreSelectedMessages->setIcon(icon_theme_factory->fromTheme(QSL("view-refresh"))); + m_ui->m_actionRestoreAllRecycleBins->setIcon(icon_theme_factory->fromTheme(QSL("view-refresh"))); + m_ui->m_actionEmptyAllRecycleBins->setIcon(icon_theme_factory->fromTheme(QSL("edit-clear"))); + m_ui->m_actionServiceAdd->setIcon(icon_theme_factory->fromTheme(QSL("list-add"))); + m_ui->m_actionServiceEdit->setIcon(icon_theme_factory->fromTheme(QSL("document-edit"))); + m_ui->m_actionServiceDelete->setIcon(icon_theme_factory->fromTheme(QSL("list-remove"))); + m_ui->m_actionAddFeedIntoSelectedAccount->setIcon(icon_theme_factory->fromTheme(QSL("application-rss+xml"))); + m_ui->m_actionAddCategoryIntoSelectedAccount->setIcon(icon_theme_factory->fromTheme(QSL("folder"))); + // Tabs & web browser. + m_ui->m_actionTabNewWebBrowser->setIcon(icon_theme_factory->fromTheme(QSL("tab-new"))); + m_ui->m_actionTabsCloseAll->setIcon(icon_theme_factory->fromTheme(QSL("window-close"))); + m_ui->m_actionTabsCloseAllExceptCurrent->setIcon(icon_theme_factory->fromTheme(QSL("window-close"))); + // Setup icons on TabWidget too. + m_ui->m_tabWidget->setupIcons(); +} + +void FormMain::loadSize() { + const QRect screen = qApp->desktop()->screenGeometry(); + const Settings* settings = qApp->settings(); + // Reload main window size & position. + resize(settings->value(GROUP(GUI), GUI::MainWindowInitialSize, size()).toSize()); + move(settings->value(GROUP(GUI), GUI::MainWindowInitialPosition, screen.center() - rect().center()).toPoint()); + + if (settings->value(GROUP(GUI), SETTING(GUI::MainWindowStartsMaximized)).toBool()) { + setWindowState(windowState() | Qt::WindowMaximized); + // We process events so that window is really maximized fast. + qApp->processEvents(); + } + + // If user exited the application while in fullsreen mode, + // then re-enable it now. + if (settings->value(GROUP(GUI), SETTING(GUI::MainWindowStartsFullscreen)).toBool()) { + m_ui->m_actionFullscreen->setChecked(true); + } + + // Hide the main menu if user wants it. + m_ui->m_actionSwitchMainMenu->setChecked(settings->value(GROUP(GUI), SETTING(GUI::MainMenuVisible)).toBool()); + // Adjust dimensions of "feeds & messages" widget. + m_ui->m_tabWidget->feedMessageViewer()->loadSize(); + m_ui->m_actionSwitchToolBars->setChecked(settings->value(GROUP(GUI), SETTING(GUI::ToolbarsVisible)).toBool()); + m_ui->m_actionSwitchListHeaders->setChecked(settings->value(GROUP(GUI), SETTING(GUI::ListHeadersVisible)).toBool()); + m_ui->m_actionSwitchStatusBar->setChecked(settings->value(GROUP(GUI), SETTING(GUI::StatusBarVisible)).toBool()); + // Make sure that only unread feeds are shown if user has that feature set on. + m_ui->m_actionShowOnlyUnreadItems->setChecked(settings->value(GROUP(Feeds), SETTING(Feeds::ShowOnlyUnreadFeeds)).toBool()); +} + +void FormMain::saveSize() { + Settings* settings = qApp->settings(); + bool is_fullscreen = isFullScreen(); + bool is_maximized = false; + + if (is_fullscreen) { + m_ui->m_actionFullscreen->setChecked(false); + // We (process events to really) un-fullscreen, so that we can determine if window is really maximized. + qApp->processEvents(); + } + + if (isMaximized()) { + is_maximized = true; + // Window is maximized, we store that fact to settings and unmaximize. + qApp->settings()->setValue(GROUP(GUI), GUI::IsMainWindowMaximizedBeforeFullscreen, isMaximized()); + setWindowState((windowState() & ~Qt::WindowMaximized) | Qt::WindowActive); + // We process events to really have window un-maximized. + qApp->processEvents(); + } + + settings->setValue(GROUP(GUI), GUI::MainMenuVisible, m_ui->m_actionSwitchMainMenu->isChecked()); + settings->setValue(GROUP(GUI), GUI::MainWindowInitialPosition, pos()); + settings->setValue(GROUP(GUI), GUI::MainWindowInitialSize, size()); + settings->setValue(GROUP(GUI), GUI::MainWindowStartsMaximized, is_maximized); + settings->setValue(GROUP(GUI), GUI::MainWindowStartsFullscreen, is_fullscreen); + settings->setValue(GROUP(GUI), GUI::StatusBarVisible, m_ui->m_actionSwitchStatusBar->isChecked()); + m_ui->m_tabWidget->feedMessageViewer()->saveSize(); +} + +void FormMain::createConnections() { + // Status bar connections. + connect(m_ui->m_menuAddItem, &QMenu::aboutToShow, this, &FormMain::updateAddItemMenu); + connect(m_ui->m_menuRecycleBin, &QMenu::aboutToShow, this, &FormMain::updateRecycleBinMenu); + connect(m_ui->m_menuAccounts, &QMenu::aboutToShow, this, &FormMain::updateAccountsMenu); + connect(m_ui->m_actionServiceDelete, &QAction::triggered, m_ui->m_actionDeleteSelectedItem, &QAction::triggered); + connect(m_ui->m_actionServiceEdit, &QAction::triggered, m_ui->m_actionEditSelectedItem, &QAction::triggered); + // Menu "File" connections. + connect(m_ui->m_actionBackupDatabaseSettings, &QAction::triggered, this, &FormMain::backupDatabaseSettings); + connect(m_ui->m_actionRestoreDatabaseSettings, &QAction::triggered, this, &FormMain::restoreDatabaseSettings); + connect(m_ui->m_actionQuit, &QAction::triggered, qApp, &Application::quit); + connect(m_ui->m_actionServiceAdd, &QAction::triggered, this, &FormMain::showAddAccountDialog); + connect(m_ui->m_actionRestart, &QAction::triggered, qApp, &Application::restart); + // Menu "View" connections. + connect(m_ui->m_actionFullscreen, &QAction::toggled, this, &FormMain::switchFullscreenMode); + connect(m_ui->m_actionSwitchMainMenu, &QAction::toggled, m_ui->m_menuBar, &QMenuBar::setVisible); + connect(m_ui->m_actionSwitchMainWindow, &QAction::triggered, this, &FormMain::switchVisibility); + connect(m_ui->m_actionSwitchStatusBar, &QAction::toggled, statusBar(), &StatusBar::setVisible); + // Menu "Tools" connections. + connect(m_ui->m_actionSettings, &QAction::triggered, this, &FormMain::showSettings); + connect(m_ui->m_actionDownloadManager, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::showDownloadManager); + connect(m_ui->m_actionCleanupDatabase, &QAction::triggered, this, &FormMain::showDbCleanupAssistant); + // Menu "Help" connections. + connect(m_ui->m_actionAboutGuard, &QAction::triggered, this, &FormMain::showAbout); + connect(m_ui->m_actionCheckForUpdates, &QAction::triggered, this, &FormMain::showUpdates); + connect(m_ui->m_actionReportBug, &QAction::triggered, this, &FormMain::reportABug); + connect(m_ui->m_actionDonate, &QAction::triggered, this, &FormMain::donate); + connect(m_ui->m_actionDisplayWiki, &QAction::triggered, this, &FormMain::showWiki); + // Tab widget connections. + connect(m_ui->m_actionTabsCloseAllExceptCurrent, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::closeAllTabsExceptCurrent); + connect(m_ui->m_actionTabsCloseAll, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::closeAllTabs); + connect(m_ui->m_actionTabNewWebBrowser, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::addEmptyBrowser); + connect(tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::itemSelected, this, &FormMain::updateFeedButtonsAvailability); + connect(qApp->feedUpdateLock(), &Mutex::locked, this, &FormMain::updateFeedButtonsAvailability); + connect(qApp->feedUpdateLock(), &Mutex::unlocked, this, &FormMain::updateFeedButtonsAvailability); + connect(tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::currentMessageRemoved, + this, &FormMain::updateMessageButtonsAvailability); + connect(tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::currentMessageChanged, + this, &FormMain::updateMessageButtonsAvailability); + connect(qApp->feedReader(), &FeedReader::feedUpdatesStarted, this, &FormMain::onFeedUpdatesStarted); + connect(qApp->feedReader(), &FeedReader::feedUpdatesProgress, this, &FormMain::onFeedUpdatesProgress); + connect(qApp->feedReader(), &FeedReader::feedUpdatesFinished, this, &FormMain::onFeedUpdatesFinished); + // Toolbar forwardings. + connect(m_ui->m_actionAddFeedIntoSelectedAccount, &QAction::triggered, + tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::addFeedIntoSelectedAccount); + connect(m_ui->m_actionAddCategoryIntoSelectedAccount, &QAction::triggered, + tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::addCategoryIntoSelectedAccount); + connect(m_ui->m_actionSwitchImportanceOfSelectedMessages, + &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::switchSelectedMessagesImportance); + connect(m_ui->m_actionDeleteSelectedMessages, + &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::deleteSelectedMessages); + connect(m_ui->m_actionMarkSelectedMessagesAsRead, + &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::markSelectedMessagesRead); + connect(m_ui->m_actionMarkSelectedMessagesAsUnread, &QAction::triggered, + tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::markSelectedMessagesUnread); + connect(m_ui->m_actionOpenSelectedSourceArticlesExternally, + &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::openSelectedSourceMessagesExternally); + connect(m_ui->m_actionOpenSelectedMessagesInternally, + &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::openSelectedMessagesInternally); + connect(m_ui->m_actionSendMessageViaEmail, + &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::sendSelectedMessageViaEmail); + connect(m_ui->m_actionMarkAllItemsRead, + &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::markAllItemsRead); + connect(m_ui->m_actionMarkSelectedItemsAsRead, + &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::markSelectedItemRead); + connect(m_ui->m_actionExpandCollapseItem, + &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::expandCollapseCurrentItem); + connect(m_ui->m_actionMarkSelectedItemsAsUnread, + &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::markSelectedItemUnread); + connect(m_ui->m_actionClearSelectedItems, + &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::clearSelectedFeeds); + connect(m_ui->m_actionClearAllItems, + &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::clearAllFeeds); + connect(m_ui->m_actionUpdateSelectedItems, + &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::updateSelectedItems); + connect(m_ui->m_actionUpdateAllItems, + &QAction::triggered, qApp->feedReader(), &FeedReader::updateAllFeeds); + connect(m_ui->m_actionStopRunningItemsUpdate, + &QAction::triggered, qApp->feedReader(), &FeedReader::stopRunningFeedUpdate); + connect(m_ui->m_actionEditSelectedItem, + &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::editSelectedItem); + connect(m_ui->m_actionViewSelectedItemsNewspaperMode, + &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::openSelectedItemsInNewspaperMode); + connect(m_ui->m_actionDeleteSelectedItem, + &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::deleteSelectedItem); + connect(m_ui->m_actionSwitchFeedsList, &QAction::triggered, + tabWidget()->feedMessageViewer(), &FeedMessageViewer::switchFeedComponentVisibility); + connect(m_ui->m_actionSelectNextItem, + &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::selectNextItem); + connect(m_ui->m_actionSwitchToolBars, &QAction::toggled, + tabWidget()->feedMessageViewer(), &FeedMessageViewer::setToolBarsEnabled); + connect(m_ui->m_actionSwitchListHeaders, &QAction::toggled, + tabWidget()->feedMessageViewer(), &FeedMessageViewer::setListHeadersEnabled); + connect(m_ui->m_actionSelectPreviousItem, + &QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::selectPreviousItem); + connect(m_ui->m_actionSelectNextMessage, + &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::selectNextItem); + connect(m_ui->m_actionSelectNextUnreadMessage, + &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::selectNextUnreadItem); + connect(m_ui->m_actionSelectPreviousMessage, + &QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::selectPreviousItem); + connect(m_ui->m_actionSwitchMessageListOrientation, &QAction::triggered, + tabWidget()->feedMessageViewer(), &FeedMessageViewer::switchMessageSplitterOrientation); + connect(m_ui->m_actionShowOnlyUnreadItems, &QAction::toggled, + tabWidget()->feedMessageViewer(), &FeedMessageViewer::toggleShowOnlyUnreadFeeds); + connect(m_ui->m_actionRestoreSelectedMessages, &QAction::triggered, + tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::restoreSelectedMessages); + connect(m_ui->m_actionRestoreAllRecycleBins, &QAction::triggered, + tabWidget()->feedMessageViewer()->feedsView()->sourceModel(), &FeedsModel::restoreAllBins); + connect(m_ui->m_actionEmptyAllRecycleBins, &QAction::triggered, + tabWidget()->feedMessageViewer()->feedsView()->sourceModel(), &FeedsModel::emptyAllBins); +} + +void FormMain::backupDatabaseSettings() { + QScopedPointer form(new FormBackupDatabaseSettings(this)); + form->exec(); +} + +void FormMain::restoreDatabaseSettings() { + QScopedPointer form(new FormRestoreDatabaseSettings(this)); + form->exec(); + + if (form->shouldRestart()) { + qApp->restart(); + } +} + +void FormMain::changeEvent(QEvent* event) { + switch (event->type()) { + case QEvent::WindowStateChange: { + if (windowState() & Qt::WindowMinimized && + SystemTrayIcon::isSystemTrayActivated() && + qApp->settings()->value(GROUP(GUI), SETTING(GUI::HideMainWindowWhenMinimized)).toBool()) { + event->ignore(); + QTimer::singleShot(CHANGE_EVENT_DELAY, this, SLOT(switchVisibility())); + } + + break; + } + + default: + break; + } + + QMainWindow::changeEvent(event); +} + +void FormMain::showAbout() { + QScopedPointer form_pointer(new FormAbout(this)); + form_pointer->exec(); +} + +void FormMain::showUpdates() { + QScopedPointer form_update(new FormUpdate(this)); + form_update->exec(); +} + +void FormMain::showWiki() { + if (!WebFactory::instance()->openUrlInExternalBrowser(APP_URL_WIKI)) { + qApp->showGuiMessage(tr("Cannot open external browser"), + tr("Cannot open external browser. Navigate to application website manually."), + QSystemTrayIcon::Warning, this, true); + } +} + +void FormMain::showAddAccountDialog() { + QScopedPointer form_update(new FormAddAccount(qApp->feedReader()->feedServices(), + qApp->feedReader()->feedsModel(), + this)); + form_update->exec(); +} + +void FormMain::reportABug() { + if (!WebFactory::instance()->openUrlInExternalBrowser(QSL(APP_URL_ISSUES_NEW))) { + qApp->showGuiMessage(tr("Cannot open external browser"), + tr("Cannot open external browser. Navigate to application website manually."), + QSystemTrayIcon::Warning, this, true); + } +} + +void FormMain::donate() { + if (!WebFactory::instance()->openUrlInExternalBrowser(QSL(APP_DONATE_URL))) { + qApp->showGuiMessage(tr("Cannot open external browser"), + tr("Cannot open external browser. Navigate to application website manually."), + QSystemTrayIcon::Warning, this, true); + } +} + +void FormMain::showSettings() { + QScopedPointer form_pointer(new FormSettings(this)); + form_pointer->exec(); +} diff --git a/src/gui/dialogs/formmain.h b/src/gui/dialogs/formmain.h index 1998f3f96..4c9cc2565 100755 --- a/src/gui/dialogs/formmain.h +++ b/src/gui/dialogs/formmain.h @@ -1,125 +1,125 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FORMMAIN_H -#define FORMMAIN_H - -#include - -#include "ui_formmain.h" - - -#if defined(USE_WEBENGINE) -class AdBlockIcon; -#endif - -class StatusBar; - -class FormMain : public QMainWindow { - Q_OBJECT - - friend class TabWidget; - friend class MessagesView; - friend class FeedsView; - - public: - // Constructors and destructors. - explicit FormMain(QWidget *parent = 0, Qt::WindowFlags f = 0); - virtual ~FormMain(); - - // Returns menu for the tray icon. - QMenu *trayMenu() const; - - // Returns global tab widget. - TabWidget *tabWidget() const; - - // Access to statusbar. - StatusBar *statusBar() const; - - // Returns list of all globally available actions. - // NOTE: This is used for setting dynamic shortcuts - // for given actions. - QList allActions() const; - - // Loads/saves visual state of the application. - void loadSize(); - void saveSize(); - -#if defined(USE_WEBENGINE) - AdBlockIcon *adblockIcon() const { - return m_adblockIcon; - } -#endif - - public slots: - // Displays window on top or switches its visibility. - void display(); - - // Switches visibility of main window. - void switchVisibility(bool force_hide = false); - - // Turns on/off fullscreen mode - void switchFullscreenMode(); - - private slots: - void updateAddItemMenu(); - void updateRecycleBinMenu(); - void updateAccountsMenu(); - - void updateMessageButtonsAvailability(); - void updateFeedButtonsAvailability(); - - void onFeedUpdatesStarted(); - void onFeedUpdatesProgress(const Feed *feed, int current, int total); - void onFeedUpdatesFinished(const FeedDownloadResults &results); - - // Displays various dialogs. - void backupDatabaseSettings(); - void restoreDatabaseSettings(); - void showSettings(); - void showAbout(); - void showUpdates(); - void showWiki(); - void showAddAccountDialog(); - void showDbCleanupAssistant(); - void reportABug(); - void donate(); - - private: - // Event handler reimplementations. - void changeEvent(QEvent *event); - - // Creates all needed menus and sets them up. - void prepareMenus(); - - // Creates needed connections for this window. - void createConnections(); - - // Sets up proper icons for this widget. - void setupIcons(); - -#if defined(USE_WEBENGINE) - AdBlockIcon *m_adblockIcon; - QAction *m_adblockIconAction; -#endif - - QScopedPointer m_ui; - QMenu *m_trayMenu; - StatusBar *m_statusBar; -}; - -#endif // FORMMAIN_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FORMMAIN_H +#define FORMMAIN_H + +#include + +#include "ui_formmain.h" + + +#if defined(USE_WEBENGINE) +class AdBlockIcon; +#endif + +class StatusBar; + +class FormMain : public QMainWindow { + Q_OBJECT + + friend class TabWidget; + friend class MessagesView; + friend class FeedsView; + + public: + // Constructors and destructors. + explicit FormMain(QWidget* parent = 0, Qt::WindowFlags f = 0); + virtual ~FormMain(); + + // Returns menu for the tray icon. + QMenu* trayMenu() const; + + // Returns global tab widget. + TabWidget* tabWidget() const; + + // Access to statusbar. + StatusBar* statusBar() const; + + // Returns list of all globally available actions. + // NOTE: This is used for setting dynamic shortcuts + // for given actions. + QList allActions() const; + + // Loads/saves visual state of the application. + void loadSize(); + void saveSize(); + +#if defined(USE_WEBENGINE) + AdBlockIcon* adblockIcon() const { + return m_adblockIcon; + } +#endif + + public slots: + // Displays window on top or switches its visibility. + void display(); + + // Switches visibility of main window. + void switchVisibility(bool force_hide = false); + + // Turns on/off fullscreen mode + void switchFullscreenMode(); + + private slots: + void updateAddItemMenu(); + void updateRecycleBinMenu(); + void updateAccountsMenu(); + + void updateMessageButtonsAvailability(); + void updateFeedButtonsAvailability(); + + void onFeedUpdatesStarted(); + void onFeedUpdatesProgress(const Feed* feed, int current, int total); + void onFeedUpdatesFinished(const FeedDownloadResults& results); + + // Displays various dialogs. + void backupDatabaseSettings(); + void restoreDatabaseSettings(); + void showSettings(); + void showAbout(); + void showUpdates(); + void showWiki(); + void showAddAccountDialog(); + void showDbCleanupAssistant(); + void reportABug(); + void donate(); + + private: + // Event handler reimplementations. + void changeEvent(QEvent* event); + + // Creates all needed menus and sets them up. + void prepareMenus(); + + // Creates needed connections for this window. + void createConnections(); + + // Sets up proper icons for this widget. + void setupIcons(); + +#if defined(USE_WEBENGINE) + AdBlockIcon* m_adblockIcon; + QAction* m_adblockIconAction; +#endif + + QScopedPointer m_ui; + QMenu* m_trayMenu; + StatusBar* m_statusBar; +}; + +#endif // FORMMAIN_H diff --git a/src/gui/dialogs/formrestoredatabasesettings.cpp b/src/gui/dialogs/formrestoredatabasesettings.cpp index 1a7dd096d..8d04176b7 100755 --- a/src/gui/dialogs/formrestoredatabasesettings.cpp +++ b/src/gui/dialogs/formrestoredatabasesettings.cpp @@ -1,139 +1,136 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/dialogs/formrestoredatabasesettings.h" - -#include "gui/messagebox.h" -#include "gui/dialogs/formmain.h" -#include "miscellaneous/iconfactory.h" -#include "exceptions/applicationexception.h" - -#include "QFileDialog" - - -FormRestoreDatabaseSettings::FormRestoreDatabaseSettings(QWidget *parent) - : QDialog(parent), m_ui(new Ui::FormRestoreDatabaseSettings), m_shouldRestart(false) { - m_ui->setupUi(this); - - m_btnRestart = m_ui->m_buttonBox->addButton(tr("Restart"), QDialogButtonBox::ActionRole); - m_ui->m_lblResult->setStatus(WidgetWithStatus::Warning, tr("No operation executed yet."), tr("No operation executed yet.")); - - setWindowIcon(qApp->icons()->fromTheme(QSL("document-import"))); - setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); - - connect(m_btnRestart, &QPushButton::clicked, this, [=]() { - m_shouldRestart = true; - close(); - }); - connect(m_ui->m_btnSelectFolder, SIGNAL(clicked()), this, SLOT(selectFolder())); - connect(m_ui->m_groupDatabase, SIGNAL(toggled(bool)), this, SLOT(checkOkButton())); - connect(m_ui->m_groupSettings, SIGNAL(toggled(bool)), this, SLOT(checkOkButton())); - connect(m_ui->m_buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(performRestoration())); - - selectFolder(qApp->getDocumentsFolderPath()); -} - -FormRestoreDatabaseSettings::~FormRestoreDatabaseSettings() { - qDebug("Destroying FormRestoreDatabaseSettings instance."); -} - -void FormRestoreDatabaseSettings::performRestoration() { - m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - - try { - qApp->restoreDatabaseSettings(m_ui->m_groupDatabase->isChecked(), - m_ui->m_groupSettings->isChecked(), - m_ui->m_listDatabase->currentRow() >= 0 ? - m_ui->m_listDatabase->currentItem()->data(Qt::UserRole).toString() : - QString(), - m_ui->m_listSettings->currentRow() >= 0 ? - m_ui->m_listSettings->currentItem()->data(Qt::UserRole).toString() : - QString()); - m_btnRestart->setEnabled(true); - m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, tr("Restoration was initiated. Restart to proceed."), - tr("You need to restart application for restoration process to finish.")); - } - catch (const ApplicationException &ex) { - m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, ex.message(), - tr("Database and/or settings were not copied to restoration directory successully.")); - } -} - -void FormRestoreDatabaseSettings::checkOkButton() { - m_btnRestart->setEnabled(false); - m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!m_ui->m_lblSelectFolder->label()->text().isEmpty() && - ((m_ui->m_groupDatabase->isChecked() && - m_ui->m_listDatabase->currentRow() >= 0) || - (m_ui->m_groupSettings->isChecked() && - m_ui->m_listSettings->currentRow() >= 0))); -} - -void FormRestoreDatabaseSettings::selectFolderWithGui() { - selectFolder(); -} - -void FormRestoreDatabaseSettings::selectFolder(QString folder) { - if (folder.isEmpty()) { - folder = QFileDialog::getExistingDirectory(this, tr("Select source directory"), m_ui->m_lblSelectFolder->label()->text()); - } - - if (!folder.isEmpty()) { - m_ui->m_lblSelectFolder->setStatus(WidgetWithStatus::Ok, QDir::toNativeSeparators(folder), - tr("Good source directory is specified.")); - } - else { - return; - } - - const QDir selected_folder(folder); - const QFileInfoList available_databases = selected_folder.entryInfoList(QStringList() - << QString("*") + BACKUP_SUFFIX_DATABASE , - QDir::Files | QDir::NoDotAndDotDot | QDir::Readable | - QDir::CaseSensitive | QDir::NoSymLinks, - QDir::Name); - const QFileInfoList available_settings = selected_folder.entryInfoList(QStringList() - << QString("*") + BACKUP_SUFFIX_SETTINGS , - QDir::Files | QDir::NoDotAndDotDot | QDir::Readable | - QDir::CaseSensitive | QDir::NoSymLinks, - QDir::Name); - - m_ui->m_listDatabase->clear(); - m_ui->m_listSettings->clear(); - - foreach (const QFileInfo &database_file, available_databases) { - QListWidgetItem *database_item = new QListWidgetItem(database_file.fileName(), m_ui->m_listDatabase); - database_item->setData(Qt::UserRole, database_file.absoluteFilePath()); - database_item->setToolTip(QDir::toNativeSeparators(database_file.absoluteFilePath())); - } - - foreach (const QFileInfo &settings_file, available_settings) { - QListWidgetItem *settings_item = new QListWidgetItem(settings_file.fileName(), m_ui->m_listSettings); - settings_item->setData(Qt::UserRole, settings_file.absoluteFilePath()); - settings_item->setToolTip(QDir::toNativeSeparators(settings_file.absoluteFilePath())); - } - - if (!available_databases.isEmpty()) { - m_ui->m_listDatabase->setCurrentRow(0); - } - - if (!available_settings.isEmpty()) { - m_ui->m_listSettings->setCurrentRow(0); - } - - m_ui->m_groupDatabase->setChecked(!available_databases.isEmpty()); - m_ui->m_groupSettings->setChecked(!available_settings.isEmpty()); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/dialogs/formrestoredatabasesettings.h" + +#include "gui/messagebox.h" +#include "gui/dialogs/formmain.h" +#include "miscellaneous/iconfactory.h" +#include "exceptions/applicationexception.h" + +#include "QFileDialog" + + +FormRestoreDatabaseSettings::FormRestoreDatabaseSettings(QWidget* parent) + : QDialog(parent), m_ui(new Ui::FormRestoreDatabaseSettings), m_shouldRestart(false) { + m_ui->setupUi(this); + m_btnRestart = m_ui->m_buttonBox->addButton(tr("Restart"), QDialogButtonBox::ActionRole); + m_ui->m_lblResult->setStatus(WidgetWithStatus::Warning, tr("No operation executed yet."), tr("No operation executed yet.")); + setWindowIcon(qApp->icons()->fromTheme(QSL("document-import"))); + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); + connect(m_btnRestart, &QPushButton::clicked, this, [ = ]() { + m_shouldRestart = true; + close(); + }); + connect(m_ui->m_btnSelectFolder, SIGNAL(clicked()), this, SLOT(selectFolder())); + connect(m_ui->m_groupDatabase, SIGNAL(toggled(bool)), this, SLOT(checkOkButton())); + connect(m_ui->m_groupSettings, SIGNAL(toggled(bool)), this, SLOT(checkOkButton())); + connect(m_ui->m_buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(performRestoration())); + selectFolder(qApp->getDocumentsFolderPath()); +} + +FormRestoreDatabaseSettings::~FormRestoreDatabaseSettings() { + qDebug("Destroying FormRestoreDatabaseSettings instance."); +} + +void FormRestoreDatabaseSettings::performRestoration() { + m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + + try { + qApp->restoreDatabaseSettings(m_ui->m_groupDatabase->isChecked(), + m_ui->m_groupSettings->isChecked(), + m_ui->m_listDatabase->currentRow() >= 0 ? + m_ui->m_listDatabase->currentItem()->data(Qt::UserRole).toString() : + QString(), + m_ui->m_listSettings->currentRow() >= 0 ? + m_ui->m_listSettings->currentItem()->data(Qt::UserRole).toString() : + QString()); + m_btnRestart->setEnabled(true); + m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, tr("Restoration was initiated. Restart to proceed."), + tr("You need to restart application for restoration process to finish.")); + } + + catch (const ApplicationException& ex) { + m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, ex.message(), + tr("Database and/or settings were not copied to restoration directory successully.")); + } +} + +void FormRestoreDatabaseSettings::checkOkButton() { + m_btnRestart->setEnabled(false); + m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!m_ui->m_lblSelectFolder->label()->text().isEmpty() && + ((m_ui->m_groupDatabase->isChecked() && + m_ui->m_listDatabase->currentRow() >= 0) || + (m_ui->m_groupSettings->isChecked() && + m_ui->m_listSettings->currentRow() >= 0))); +} + +void FormRestoreDatabaseSettings::selectFolderWithGui() { + selectFolder(); +} + +void FormRestoreDatabaseSettings::selectFolder(QString folder) { + if (folder.isEmpty()) { + folder = QFileDialog::getExistingDirectory(this, tr("Select source directory"), m_ui->m_lblSelectFolder->label()->text()); + } + + if (!folder.isEmpty()) { + m_ui->m_lblSelectFolder->setStatus(WidgetWithStatus::Ok, QDir::toNativeSeparators(folder), + tr("Good source directory is specified.")); + } + + else { + return; + } + + const QDir selected_folder(folder); + const QFileInfoList available_databases = selected_folder.entryInfoList(QStringList() + << QString("*") + BACKUP_SUFFIX_DATABASE, + QDir::Files | QDir::NoDotAndDotDot | QDir::Readable | + QDir::CaseSensitive | QDir::NoSymLinks, + QDir::Name); + const QFileInfoList available_settings = selected_folder.entryInfoList(QStringList() + << QString("*") + BACKUP_SUFFIX_SETTINGS, + QDir::Files | QDir::NoDotAndDotDot | QDir::Readable | + QDir::CaseSensitive | QDir::NoSymLinks, + QDir::Name); + m_ui->m_listDatabase->clear(); + m_ui->m_listSettings->clear(); + + foreach (const QFileInfo& database_file, available_databases) { + QListWidgetItem* database_item = new QListWidgetItem(database_file.fileName(), m_ui->m_listDatabase); + database_item->setData(Qt::UserRole, database_file.absoluteFilePath()); + database_item->setToolTip(QDir::toNativeSeparators(database_file.absoluteFilePath())); + } + + foreach (const QFileInfo& settings_file, available_settings) { + QListWidgetItem* settings_item = new QListWidgetItem(settings_file.fileName(), m_ui->m_listSettings); + settings_item->setData(Qt::UserRole, settings_file.absoluteFilePath()); + settings_item->setToolTip(QDir::toNativeSeparators(settings_file.absoluteFilePath())); + } + + if (!available_databases.isEmpty()) { + m_ui->m_listDatabase->setCurrentRow(0); + } + + if (!available_settings.isEmpty()) { + m_ui->m_listSettings->setCurrentRow(0); + } + + m_ui->m_groupDatabase->setChecked(!available_databases.isEmpty()); + m_ui->m_groupSettings->setChecked(!available_settings.isEmpty()); +} diff --git a/src/gui/dialogs/formrestoredatabasesettings.h b/src/gui/dialogs/formrestoredatabasesettings.h index 4e511a3e7..c4a39fb59 100755 --- a/src/gui/dialogs/formrestoredatabasesettings.h +++ b/src/gui/dialogs/formrestoredatabasesettings.h @@ -1,51 +1,51 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FORMRESTOREDATABASESETTINGS_H -#define FORMRESTOREDATABASESETTINGS_H - -#include - -#include "ui_formrestoredatabasesettings.h" - - -class FormRestoreDatabaseSettings : public QDialog { - Q_OBJECT - - public: - // Constructors and destructors. - explicit FormRestoreDatabaseSettings(QWidget *parent = 0); - virtual ~FormRestoreDatabaseSettings(); - - bool shouldRestart() const { - return m_shouldRestart; - } - - private slots: - void performRestoration(); - void checkOkButton(); - void selectFolderWithGui(); - void selectFolder(QString folder = QString()); - - private: - QScopedPointer m_ui; - QPushButton *m_btnRestart; - - bool m_shouldRestart; -}; - -#endif // FORMRESTOREDATABASESETTINGS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FORMRESTOREDATABASESETTINGS_H +#define FORMRESTOREDATABASESETTINGS_H + +#include + +#include "ui_formrestoredatabasesettings.h" + + +class FormRestoreDatabaseSettings : public QDialog { + Q_OBJECT + + public: + // Constructors and destructors. + explicit FormRestoreDatabaseSettings(QWidget* parent = 0); + virtual ~FormRestoreDatabaseSettings(); + + bool shouldRestart() const { + return m_shouldRestart; + } + + private slots: + void performRestoration(); + void checkOkButton(); + void selectFolderWithGui(); + void selectFolder(QString folder = QString()); + + private: + QScopedPointer m_ui; + QPushButton* m_btnRestart; + + bool m_shouldRestart; +}; + +#endif // FORMRESTOREDATABASESETTINGS_H diff --git a/src/gui/dialogs/formsettings.cpp b/src/gui/dialogs/formsettings.cpp index c98892706..8d8428d84 100755 --- a/src/gui/dialogs/formsettings.cpp +++ b/src/gui/dialogs/formsettings.cpp @@ -1,146 +1,139 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/dialogs/formsettings.h" - -#include "definitions/definitions.h" -#include "miscellaneous/application.h" -#include "miscellaneous/settings.h" -#include "miscellaneous/iconfactory.h" -#include "gui/messagebox.h" - -#include "gui/settings/settingsbrowsermail.h" -#include "gui/settings/settingsdatabase.h" -#include "gui/settings/settingsdownloads.h" -#include "gui/settings/settingsfeedsmessages.h" -#include "gui/settings/settingsgeneral.h" -#include "gui/settings/settingsgui.h" -#include "gui/settings/settingslocalization.h" -#include "gui/settings/settingsshortcuts.h" - - -FormSettings::FormSettings(QWidget *parent) : QDialog(parent), m_panels(QList()), m_ui(new Ui::FormSettings), m_settings(qApp->settings()) { - m_ui->setupUi(this); - - // Set flags and attributes. - setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint | Qt::WindowTitleHint); - setWindowIcon(qApp->icons()->fromTheme(QSL("emblem-system"))); - - m_btnApply = m_ui->m_buttonBox->button(QDialogButtonBox::Apply); - m_btnApply->setEnabled(false); - - // Establish needed connections. - connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &FormSettings::saveSettings); - connect(m_ui->m_buttonBox, &QDialogButtonBox::rejected, this, &FormSettings::cancelSettings); - connect(m_btnApply, &QPushButton::clicked, this, &FormSettings::applySettings); - - addSettingsPanel(new SettingsGeneral(m_settings, this)); - addSettingsPanel(new SettingsDatabase(m_settings, this)); - addSettingsPanel(new SettingsGui(m_settings, this)); - addSettingsPanel(new SettingsLocalization(m_settings, this)); - addSettingsPanel(new SettingsShortcuts(m_settings, this)); - addSettingsPanel(new SettingsBrowserMail(m_settings, this)); - addSettingsPanel(new SettingsDownloads(m_settings, this)); - addSettingsPanel(new SettingsFeedsMessages(m_settings, this)); - - m_ui->m_listSettings->setCurrentRow(0); -} - -FormSettings::~FormSettings() { - qDebug("Destroying FormSettings distance."); -} - -void FormSettings::saveSettings() { - applySettings(); - accept(); -} - -void FormSettings::applySettings() { - // Save all settings. - m_settings->checkSettings(); - - QStringList panels_for_restart; - - foreach (SettingsPanel *panel, m_panels) { - if (panel->isDirty()) { - panel->saveSettings(); - } - - if (panel->requiresRestart()) { - panels_for_restart.append(panel->title().toLower()); - panel->setRequiresRestart(false); - } - } - - if (!panels_for_restart.isEmpty()) { - const QStringList changed_settings_description = panels_for_restart.replaceInStrings(QRegExp(QSL("^")), QString::fromUtf8(" • ")); - - const QMessageBox::StandardButton clicked_button = MessageBox::show(this, - QMessageBox::Question, - tr("Critical settings were changed"), - tr("Some critical settings were changed and will be applied after the application gets restarted. " - "\n\nYou have to restart manually."), - tr("Do you want to restart now?"), - tr("Changed categories of settings:\n%1.").arg(changed_settings_description .join(QSL(",\n"))), - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - - if (clicked_button == QMessageBox::Yes) { - qApp->restart(); - } - } - - m_btnApply->setEnabled(false); -} - -void FormSettings::cancelSettings() { - QStringList changed_panels; - - foreach (SettingsPanel *panel, m_panels) { - if (panel->isDirty()) { - changed_panels.append(panel->title().toLower()); - } - } - - if (changed_panels.isEmpty()) { - reject(); - } - else { - const QStringList changed_settings_description = changed_panels.replaceInStrings(QRegExp(QSL("^")), QString::fromUtf8(" • ")); - - if (MessageBox::show(this, - QMessageBox::Critical, - tr("Some settings are changed and will be lost"), - tr("Some settings were changed and by cancelling this dialog, you would lose these changes."), - tr("Do you really want to close this dialog without saving any settings?"), - tr("Changed categories of settings:\n%1.").arg(changed_settings_description .join(QSL(",\n"))), - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == - QMessageBox::Yes) { - reject(); - } - } -} - -void FormSettings::addSettingsPanel(SettingsPanel *panel) { - m_ui->m_listSettings->addItem(panel->title()); - m_panels.append(panel); - m_ui->m_stackedSettings->addWidget(panel); - panel->loadSettings(); - - connect(panel, &SettingsPanel::settingsChanged, [this]() { - m_btnApply->setEnabled(true); - }); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/dialogs/formsettings.h" + +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "miscellaneous/settings.h" +#include "miscellaneous/iconfactory.h" +#include "gui/messagebox.h" + +#include "gui/settings/settingsbrowsermail.h" +#include "gui/settings/settingsdatabase.h" +#include "gui/settings/settingsdownloads.h" +#include "gui/settings/settingsfeedsmessages.h" +#include "gui/settings/settingsgeneral.h" +#include "gui/settings/settingsgui.h" +#include "gui/settings/settingslocalization.h" +#include "gui/settings/settingsshortcuts.h" + + +FormSettings::FormSettings(QWidget* parent) : QDialog(parent), m_panels(QList()), m_ui(new Ui::FormSettings), m_settings(qApp->settings()) { + m_ui->setupUi(this); + // Set flags and attributes. + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint | Qt::WindowTitleHint); + setWindowIcon(qApp->icons()->fromTheme(QSL("emblem-system"))); + m_btnApply = m_ui->m_buttonBox->button(QDialogButtonBox::Apply); + m_btnApply->setEnabled(false); + // Establish needed connections. + connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &FormSettings::saveSettings); + connect(m_ui->m_buttonBox, &QDialogButtonBox::rejected, this, &FormSettings::cancelSettings); + connect(m_btnApply, &QPushButton::clicked, this, &FormSettings::applySettings); + addSettingsPanel(new SettingsGeneral(m_settings, this)); + addSettingsPanel(new SettingsDatabase(m_settings, this)); + addSettingsPanel(new SettingsGui(m_settings, this)); + addSettingsPanel(new SettingsLocalization(m_settings, this)); + addSettingsPanel(new SettingsShortcuts(m_settings, this)); + addSettingsPanel(new SettingsBrowserMail(m_settings, this)); + addSettingsPanel(new SettingsDownloads(m_settings, this)); + addSettingsPanel(new SettingsFeedsMessages(m_settings, this)); + m_ui->m_listSettings->setCurrentRow(0); +} + +FormSettings::~FormSettings() { + qDebug("Destroying FormSettings distance."); +} + +void FormSettings::saveSettings() { + applySettings(); + accept(); +} + +void FormSettings::applySettings() { + // Save all settings. + m_settings->checkSettings(); + QStringList panels_for_restart; + + foreach (SettingsPanel* panel, m_panels) { + if (panel->isDirty()) { + panel->saveSettings(); + } + + if (panel->requiresRestart()) { + panels_for_restart.append(panel->title().toLower()); + panel->setRequiresRestart(false); + } + } + + if (!panels_for_restart.isEmpty()) { + const QStringList changed_settings_description = panels_for_restart.replaceInStrings(QRegExp(QSL("^")), QString::fromUtf8(" • ")); + const QMessageBox::StandardButton clicked_button = MessageBox::show(this, + QMessageBox::Question, + tr("Critical settings were changed"), + tr("Some critical settings were changed and will be applied after the application gets restarted. " + "\n\nYou have to restart manually."), + tr("Do you want to restart now?"), + tr("Changed categories of settings:\n%1.").arg(changed_settings_description .join(QSL(",\n"))), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (clicked_button == QMessageBox::Yes) { + qApp->restart(); + } + } + + m_btnApply->setEnabled(false); +} + +void FormSettings::cancelSettings() { + QStringList changed_panels; + + foreach (SettingsPanel* panel, m_panels) { + if (panel->isDirty()) { + changed_panels.append(panel->title().toLower()); + } + } + + if (changed_panels.isEmpty()) { + reject(); + } + + else { + const QStringList changed_settings_description = changed_panels.replaceInStrings(QRegExp(QSL("^")), QString::fromUtf8(" • ")); + + if (MessageBox::show(this, + QMessageBox::Critical, + tr("Some settings are changed and will be lost"), + tr("Some settings were changed and by cancelling this dialog, you would lose these changes."), + tr("Do you really want to close this dialog without saving any settings?"), + tr("Changed categories of settings:\n%1.").arg(changed_settings_description .join(QSL(",\n"))), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == + QMessageBox::Yes) { + reject(); + } + } +} + +void FormSettings::addSettingsPanel(SettingsPanel* panel) { + m_ui->m_listSettings->addItem(panel->title()); + m_panels.append(panel); + m_ui->m_stackedSettings->addWidget(panel); + panel->loadSettings(); + connect(panel, &SettingsPanel::settingsChanged, [this]() { + m_btnApply->setEnabled(true); + }); +} diff --git a/src/gui/dialogs/formsettings.h b/src/gui/dialogs/formsettings.h index ace45e570..097834753 100755 --- a/src/gui/dialogs/formsettings.h +++ b/src/gui/dialogs/formsettings.h @@ -1,52 +1,52 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FORMSETTINGS_H -#define FORMSETTINGS_H - -#include - -#include "ui_formsettings.h" - - -class Settings; -class SettingsPanel; - -class FormSettings : public QDialog { - Q_OBJECT - - public: - // Constructors and destructors. - explicit FormSettings(QWidget *parent = 0); - virtual ~FormSettings(); - - private slots: - // Saves settings into global configuration. - void saveSettings(); - void applySettings(); - void cancelSettings(); - - private: - void addSettingsPanel(SettingsPanel *panel); - - QList m_panels; - QScopedPointer m_ui; - QPushButton *m_btnApply; - Settings *m_settings; -}; - -#endif // FORMSETTINGS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FORMSETTINGS_H +#define FORMSETTINGS_H + +#include + +#include "ui_formsettings.h" + + +class Settings; +class SettingsPanel; + +class FormSettings : public QDialog { + Q_OBJECT + + public: + // Constructors and destructors. + explicit FormSettings(QWidget* parent = 0); + virtual ~FormSettings(); + + private slots: + // Saves settings into global configuration. + void saveSettings(); + void applySettings(); + void cancelSettings(); + + private: + void addSettingsPanel(SettingsPanel* panel); + + QList m_panels; + QScopedPointer m_ui; + QPushButton* m_btnApply; + Settings* m_settings; +}; + +#endif // FORMSETTINGS_H diff --git a/src/gui/dialogs/formupdate.cpp b/src/gui/dialogs/formupdate.cpp index 5d1dc8456..386d5fc43 100755 --- a/src/gui/dialogs/formupdate.cpp +++ b/src/gui/dialogs/formupdate.cpp @@ -1,259 +1,259 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/dialogs/formupdate.h" - -#include "definitions/definitions.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/iofactory.h" -#include "network-web/networkfactory.h" -#include "network-web/webfactory.h" -#include "network-web/downloader.h" -#include "gui/messagebox.h" - -#include -#include - -#if defined(Q_OS_WIN) -#include -#endif - - -FormUpdate::FormUpdate(QWidget *parent) - : QDialog(parent), m_downloader(nullptr), m_readyToInstall(false), m_ui(new Ui::FormUpdate), m_lastDownloadedBytes(0) { - m_ui->setupUi(this); - m_ui->m_lblCurrentRelease->setText(APP_VERSION); - m_ui->m_tabInfo->removeTab(1); - - // Set flags and attributes. - setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint | Qt::WindowTitleHint); - setWindowIcon(qApp->icons()->fromTheme(QSL("help-about"))); - - if (isSelfUpdateSupported()) { - m_btnUpdate = m_ui->m_buttonBox->addButton(tr("Download selected update"), QDialogButtonBox::ActionRole); - m_btnUpdate->setToolTip(tr("Download new installation files.")); - } - else { - m_btnUpdate = m_ui->m_buttonBox->addButton(tr("Go to application website"), QDialogButtonBox::ActionRole); - m_btnUpdate->setToolTip(tr("Go to application website to get update packages manually.")); - } - - m_btnUpdate->setVisible(false); - connect(m_btnUpdate, &QPushButton::clicked, this, &FormUpdate::startUpdate); - checkForUpdates(); -} - -FormUpdate::~FormUpdate() { -} - -bool FormUpdate::isSelfUpdateSupported() const { -#if defined(Q_OS_WIN) || defined(Q_OS_MAC) - return true; -#else - return false; -#endif -} - -void FormUpdate::checkForUpdates() { - const QPair, QNetworkReply::NetworkError> update = qApp->system()->checkForUpdates(); - - if (update.second != QNetworkReply::NoError) { - m_updateInfo = UpdateInfo(); - m_ui->m_tabInfo->setEnabled(false); - - //: Unknown release. - m_ui->m_lblAvailableRelease->setText(tr("unknown")); - m_ui->m_txtChanges->clear(); - m_ui->m_lblStatus->setStatus(WidgetWithStatus::Error, - tr("Error: '%1'.").arg(NetworkFactory::networkErrorText(update.second)), - tr("List with updates was not\ndownloaded successfully.")); - } - else { - const bool self_update_supported = isSelfUpdateSupported(); - - m_updateInfo = update.first.at(0); - m_ui->m_tabInfo->setEnabled(true); - m_ui->m_lblAvailableRelease->setText(m_updateInfo.m_availableVersion); - m_ui->m_txtChanges->setText(m_updateInfo.m_changes); - - if (SystemFactory::isVersionNewer(m_updateInfo.m_availableVersion, APP_VERSION)) { - m_btnUpdate->setVisible(true); - m_ui->m_lblStatus->setStatus(WidgetWithStatus::Ok, - tr("New release available."), - tr("This is new version which can be\ndownloaded.")); - - if (self_update_supported) { - loadAvailableFiles(); - } - } - else { - m_ui->m_lblStatus->setStatus(WidgetWithStatus::Warning, - tr("No new release available."), - tr("This release is not newer than\ncurrently installed one.")); - } - } -} - -void FormUpdate::updateProgress(qint64 bytes_received, qint64 bytes_total) { - if (bytes_received - m_lastDownloadedBytes > 500000 || m_lastDownloadedBytes == 0) { - m_ui->m_lblStatus->setStatus(WidgetWithStatus::Information, - tr("Downloaded %1% (update size is %2 kB).").arg(QString::number(bytes_total == 0 ? 0 : (bytes_received * 100.0) / bytes_total, - 'f', - 2), - QString::number(bytes_total / 1000, - 'f', - 2)), - tr("Downloading update...")); - m_ui->m_lblStatus->repaint(); - - m_lastDownloadedBytes = bytes_received; - } -} - -void FormUpdate::saveUpdateFile(const QByteArray &file_contents) { - const QString url_file = m_ui->m_listFiles->currentItem()->data(Qt::UserRole).toString(); - const QString temp_directory = qApp->getTempFolderPath(); - - if (!temp_directory.isEmpty()) { - const QString output_file_name = url_file.mid(url_file.lastIndexOf('/') + 1); - QFile output_file(temp_directory + QDir::separator() + output_file_name); - - if (output_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - qDebug("Storing update file to temporary location '%s'.", - qPrintable(QDir::toNativeSeparators(output_file.fileName()))); - - output_file.write(file_contents); - output_file.flush(); - output_file.close(); - - qDebug("Update file contents was successfuly saved."); - - m_updateFilePath = output_file.fileName(); - m_readyToInstall = true; - } - else { - qDebug("Cannot save downloaded update file because target temporary file '%s' cannot be " - "opened for writing.", qPrintable(output_file_name)); - } - } - else { - qDebug("Cannot save downloaded update file because no TEMP directory is available."); - } -} - -void FormUpdate::loadAvailableFiles() { - m_ui->m_listFiles->clear(); - - foreach (const UpdateUrl &url, m_updateInfo.m_urls) { - QListWidgetItem *item = new QListWidgetItem(url.m_name + tr(" (size ") + url.m_size + QSL(")")); - item->setData(Qt::UserRole, url.m_fileUrl); - item->setToolTip(url.m_fileUrl); - m_ui->m_listFiles->addItem(item); - } - - if (m_ui->m_listFiles->count() > 0) { - m_ui->m_listFiles->setCurrentRow(0); - } - else { - m_btnUpdate->setEnabled(false); - } - - m_ui->m_tabInfo->addTab(m_ui->tabFiles, tr("Available update files")); - m_ui->m_tabInfo->setCurrentIndex(1); -} - -void FormUpdate::updateCompleted(QNetworkReply::NetworkError status, QByteArray contents) { - qDebug("Download of application update file was completed with code '%d'.", status); - - switch (status) { - case QNetworkReply::NoError: - saveUpdateFile(contents); - m_ui->m_lblStatus->setStatus(WidgetWithStatus::Ok, tr("Downloaded successfully"), tr("Package was downloaded successfully.\nYou can install it now.")); - m_btnUpdate->setText(tr("Install")); - m_btnUpdate->setEnabled(true); - break; - - default: - m_ui->m_lblStatus->setStatus(WidgetWithStatus::Error, tr("Error occured"), tr("Error occured during downloading of the package.")); - m_btnUpdate->setText(tr("Error occured")); - break; - } -} - -void FormUpdate::startUpdate() { - QString url_file; - const bool update_for_this_system = isSelfUpdateSupported(); - - if (update_for_this_system && m_ui->m_listFiles->currentItem() != nullptr) { - url_file = m_ui->m_listFiles->currentItem()->data(Qt::UserRole).toString(); - m_ui->m_listFiles->setEnabled(false); - } - else { - url_file = APP_URL; - } - - if (m_readyToInstall) { - close(); - qDebug("Preparing to launch external installer '%s'.", qPrintable(QDir::toNativeSeparators(m_updateFilePath))); - -#if defined(Q_OS_WIN) - HINSTANCE exec_result = ShellExecute(nullptr, - nullptr, - reinterpret_cast(QDir::toNativeSeparators(m_updateFilePath).utf16()), - nullptr, - nullptr, - SW_NORMAL); - - if (((int)exec_result) <= 32) { - qDebug("External updater was not launched due to error."); - - qApp->showGuiMessage(tr("Cannot update application"), - tr("Cannot launch external updater. Update application manually."), - QSystemTrayIcon::Warning, this); - } - else { - qApp->quit(); - } -#endif - } - else if (update_for_this_system) { - // Nothing is downloaded yet, but update for this system - // is available and self-update feature is present. - - if (m_downloader == nullptr) { - // Initialie downloader. - m_downloader = new Downloader(this); - - connect(m_downloader, &Downloader::progress, this, &FormUpdate::updateProgress); - connect(m_downloader, &Downloader::completed, this, &FormUpdate::updateCompleted); - updateProgress(0, 100); - } - - m_btnUpdate->setText(tr("Downloading update...")); - m_btnUpdate->setEnabled(false); - m_downloader->downloadFile(url_file); - } - else { - // Self-update and package are not available. - if (!WebFactory::instance()->openUrlInExternalBrowser(url_file)) { - qApp->showGuiMessage(tr("Cannot update application"), - tr("Cannot navigate to installation file. Check new installation downloads manually on project website."), - QSystemTrayIcon::Warning, - this, true); - } - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/dialogs/formupdate.h" + +#include "definitions/definitions.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/iofactory.h" +#include "network-web/networkfactory.h" +#include "network-web/webfactory.h" +#include "network-web/downloader.h" +#include "gui/messagebox.h" + +#include +#include + +#if defined(Q_OS_WIN) +#include +#endif + + +FormUpdate::FormUpdate(QWidget* parent) + : QDialog(parent), m_downloader(nullptr), m_readyToInstall(false), m_ui(new Ui::FormUpdate), m_lastDownloadedBytes(0) { + m_ui->setupUi(this); + m_ui->m_lblCurrentRelease->setText(APP_VERSION); + m_ui->m_tabInfo->removeTab(1); + // Set flags and attributes. + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint | Qt::WindowTitleHint); + setWindowIcon(qApp->icons()->fromTheme(QSL("help-about"))); + + if (isSelfUpdateSupported()) { + m_btnUpdate = m_ui->m_buttonBox->addButton(tr("Download selected update"), QDialogButtonBox::ActionRole); + m_btnUpdate->setToolTip(tr("Download new installation files.")); + } + + else { + m_btnUpdate = m_ui->m_buttonBox->addButton(tr("Go to application website"), QDialogButtonBox::ActionRole); + m_btnUpdate->setToolTip(tr("Go to application website to get update packages manually.")); + } + + m_btnUpdate->setVisible(false); + connect(m_btnUpdate, &QPushButton::clicked, this, &FormUpdate::startUpdate); + checkForUpdates(); +} + +FormUpdate::~FormUpdate() { +} + +bool FormUpdate::isSelfUpdateSupported() const { +#if defined(Q_OS_WIN) || defined(Q_OS_MAC) + return true; +#else + return false; +#endif +} + +void FormUpdate::checkForUpdates() { + const QPair, QNetworkReply::NetworkError> update = qApp->system()->checkForUpdates(); + + if (update.second != QNetworkReply::NoError) { + m_updateInfo = UpdateInfo(); + m_ui->m_tabInfo->setEnabled(false); + //: Unknown release. + m_ui->m_lblAvailableRelease->setText(tr("unknown")); + m_ui->m_txtChanges->clear(); + m_ui->m_lblStatus->setStatus(WidgetWithStatus::Error, + tr("Error: '%1'.").arg(NetworkFactory::networkErrorText(update.second)), + tr("List with updates was not\ndownloaded successfully.")); + } + + else { + const bool self_update_supported = isSelfUpdateSupported(); + m_updateInfo = update.first.at(0); + m_ui->m_tabInfo->setEnabled(true); + m_ui->m_lblAvailableRelease->setText(m_updateInfo.m_availableVersion); + m_ui->m_txtChanges->setText(m_updateInfo.m_changes); + + if (SystemFactory::isVersionNewer(m_updateInfo.m_availableVersion, APP_VERSION)) { + m_btnUpdate->setVisible(true); + m_ui->m_lblStatus->setStatus(WidgetWithStatus::Ok, + tr("New release available."), + tr("This is new version which can be\ndownloaded.")); + + if (self_update_supported) { + loadAvailableFiles(); + } + } + + else { + m_ui->m_lblStatus->setStatus(WidgetWithStatus::Warning, + tr("No new release available."), + tr("This release is not newer than\ncurrently installed one.")); + } + } +} + +void FormUpdate::updateProgress(qint64 bytes_received, qint64 bytes_total) { + if (bytes_received - m_lastDownloadedBytes > 500000 || m_lastDownloadedBytes == 0) { + m_ui->m_lblStatus->setStatus(WidgetWithStatus::Information, + tr("Downloaded %1% (update size is %2 kB).").arg(QString::number(bytes_total == 0 ? 0 : (bytes_received * 100.0) / bytes_total, + 'f', + 2), + QString::number(bytes_total / 1000, + 'f', + 2)), + tr("Downloading update...")); + m_ui->m_lblStatus->repaint(); + m_lastDownloadedBytes = bytes_received; + } +} + +void FormUpdate::saveUpdateFile(const QByteArray& file_contents) { + const QString url_file = m_ui->m_listFiles->currentItem()->data(Qt::UserRole).toString(); + const QString temp_directory = qApp->getTempFolderPath(); + + if (!temp_directory.isEmpty()) { + const QString output_file_name = url_file.mid(url_file.lastIndexOf('/') + 1); + QFile output_file(temp_directory + QDir::separator() + output_file_name); + + if (output_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + qDebug("Storing update file to temporary location '%s'.", + qPrintable(QDir::toNativeSeparators(output_file.fileName()))); + output_file.write(file_contents); + output_file.flush(); + output_file.close(); + qDebug("Update file contents was successfuly saved."); + m_updateFilePath = output_file.fileName(); + m_readyToInstall = true; + } + + else { + qDebug("Cannot save downloaded update file because target temporary file '%s' cannot be " + "opened for writing.", qPrintable(output_file_name)); + } + } + + else { + qDebug("Cannot save downloaded update file because no TEMP directory is available."); + } +} + +void FormUpdate::loadAvailableFiles() { + m_ui->m_listFiles->clear(); + + foreach (const UpdateUrl& url, m_updateInfo.m_urls) { + QListWidgetItem* item = new QListWidgetItem(url.m_name + tr(" (size ") + url.m_size + QSL(")")); + item->setData(Qt::UserRole, url.m_fileUrl); + item->setToolTip(url.m_fileUrl); + m_ui->m_listFiles->addItem(item); + } + + if (m_ui->m_listFiles->count() > 0) { + m_ui->m_listFiles->setCurrentRow(0); + } + + else { + m_btnUpdate->setEnabled(false); + } + + m_ui->m_tabInfo->addTab(m_ui->tabFiles, tr("Available update files")); + m_ui->m_tabInfo->setCurrentIndex(1); +} + +void FormUpdate::updateCompleted(QNetworkReply::NetworkError status, QByteArray contents) { + qDebug("Download of application update file was completed with code '%d'.", status); + + switch (status) { + case QNetworkReply::NoError: + saveUpdateFile(contents); + m_ui->m_lblStatus->setStatus(WidgetWithStatus::Ok, tr("Downloaded successfully"), tr("Package was downloaded successfully.\nYou can install it now.")); + m_btnUpdate->setText(tr("Install")); + m_btnUpdate->setEnabled(true); + break; + + default: + m_ui->m_lblStatus->setStatus(WidgetWithStatus::Error, tr("Error occured"), tr("Error occured during downloading of the package.")); + m_btnUpdate->setText(tr("Error occured")); + break; + } +} + +void FormUpdate::startUpdate() { + QString url_file; + const bool update_for_this_system = isSelfUpdateSupported(); + + if (update_for_this_system && m_ui->m_listFiles->currentItem() != nullptr) { + url_file = m_ui->m_listFiles->currentItem()->data(Qt::UserRole).toString(); + m_ui->m_listFiles->setEnabled(false); + } + + else { + url_file = APP_URL; + } + + if (m_readyToInstall) { + close(); + qDebug("Preparing to launch external installer '%s'.", qPrintable(QDir::toNativeSeparators(m_updateFilePath))); +#if defined(Q_OS_WIN) + HINSTANCE exec_result = ShellExecute(nullptr, + nullptr, + reinterpret_cast(QDir::toNativeSeparators(m_updateFilePath).utf16()), + nullptr, + nullptr, + SW_NORMAL); + + if (((int)exec_result) <= 32) { + qDebug("External updater was not launched due to error."); + qApp->showGuiMessage(tr("Cannot update application"), + tr("Cannot launch external updater. Update application manually."), + QSystemTrayIcon::Warning, this); + } + + else { + qApp->quit(); + } + +#endif + } + + else if (update_for_this_system) { + // Nothing is downloaded yet, but update for this system + // is available and self-update feature is present. + if (m_downloader == nullptr) { + // Initialie downloader. + m_downloader = new Downloader(this); + connect(m_downloader, &Downloader::progress, this, &FormUpdate::updateProgress); + connect(m_downloader, &Downloader::completed, this, &FormUpdate::updateCompleted); + updateProgress(0, 100); + } + + m_btnUpdate->setText(tr("Downloading update...")); + m_btnUpdate->setEnabled(false); + m_downloader->downloadFile(url_file); + } + + else { + // Self-update and package are not available. + if (!WebFactory::instance()->openUrlInExternalBrowser(url_file)) { + qApp->showGuiMessage(tr("Cannot update application"), + tr("Cannot navigate to installation file. Check new installation downloads manually on project website."), + QSystemTrayIcon::Warning, + this, true); + } + } +} diff --git a/src/gui/dialogs/formupdate.h b/src/gui/dialogs/formupdate.h index 58dc21451..48ed850d6 100755 --- a/src/gui/dialogs/formupdate.h +++ b/src/gui/dialogs/formupdate.h @@ -1,66 +1,66 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FORMUPDATE_H -#define FORMUPDATE_H - -#include - -#include "ui_formupdate.h" - -#include "miscellaneous/systemfactory.h" - -#include -#include - - -class Downloader; - -class FormUpdate : public QDialog { - Q_OBJECT - - public: - // Constructors and destructors. - explicit FormUpdate(QWidget *parent = 0); - virtual ~FormUpdate(); - - // Returns true if application can self-update - // on current platform. - bool isSelfUpdateSupported() const; - - private slots: - // Check for updates and interprets the results. - void checkForUpdates(); - void startUpdate(); - - void updateProgress(qint64 bytes_received, qint64 bytes_total); - void updateCompleted(QNetworkReply::NetworkError status, QByteArray contents); - void saveUpdateFile(const QByteArray &file_contents); - - private: - void loadAvailableFiles(); - - Downloader *m_downloader; - bool m_readyToInstall; - QString m_updateFilePath; - QScopedPointer m_ui; - UpdateInfo m_updateInfo; - QPushButton *m_btnUpdate; - qint64 m_lastDownloadedBytes; -}; - -#endif // FORMUPDATE_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FORMUPDATE_H +#define FORMUPDATE_H + +#include + +#include "ui_formupdate.h" + +#include "miscellaneous/systemfactory.h" + +#include +#include + + +class Downloader; + +class FormUpdate : public QDialog { + Q_OBJECT + + public: + // Constructors and destructors. + explicit FormUpdate(QWidget* parent = 0); + virtual ~FormUpdate(); + + // Returns true if application can self-update + // on current platform. + bool isSelfUpdateSupported() const; + + private slots: + // Check for updates and interprets the results. + void checkForUpdates(); + void startUpdate(); + + void updateProgress(qint64 bytes_received, qint64 bytes_total); + void updateCompleted(QNetworkReply::NetworkError status, QByteArray contents); + void saveUpdateFile(const QByteArray& file_contents); + + private: + void loadAvailableFiles(); + + Downloader* m_downloader; + bool m_readyToInstall; + QString m_updateFilePath; + QScopedPointer m_ui; + UpdateInfo m_updateInfo; + QPushButton* m_btnUpdate; + qint64 m_lastDownloadedBytes; +}; + +#endif // FORMUPDATE_H diff --git a/src/gui/discoverfeedsbutton.cpp b/src/gui/discoverfeedsbutton.cpp index d2307ab4b..86843263d 100755 --- a/src/gui/discoverfeedsbutton.cpp +++ b/src/gui/discoverfeedsbutton.cpp @@ -1,93 +1,93 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/discoverfeedsbutton.h" - -#include "miscellaneous/application.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/feedreader.h" -#include "gui/dialogs/formmain.h" -#include "gui/tabwidget.h" -#include "gui/feedmessageviewer.h" -#include "gui/feedsview.h" -#include "core/feedsmodel.h" -#include "services/abstract/serviceroot.h" - -#include - - -DiscoverFeedsButton::DiscoverFeedsButton(QWidget *parent) : QToolButton(parent), m_addresses(QStringList()) { - setEnabled(false); - setIcon(qApp->icons()->fromTheme(QSL("application-rss+xml"))); - setPopupMode(QToolButton::InstantPopup); -} - -DiscoverFeedsButton::~DiscoverFeedsButton() { -} - -void DiscoverFeedsButton::clearFeedAddresses() { - setFeedAddresses(QStringList()); -} - -void DiscoverFeedsButton::setFeedAddresses(const QStringList &addresses) { - setEnabled(!addresses.isEmpty()); - setToolTip(addresses.isEmpty() ? - tr("This website does not contain any feeds.") : - tr("Click me to add feeds from this website.\nThis website contains %n feed(s).", 0, addresses.size())); - - if (menu() == nullptr) { - // Initialize the menu. - setMenu(new QMenu(this)); - connect(menu(), &QMenu::triggered, this, &DiscoverFeedsButton::linkTriggered); - connect(menu(), &QMenu::aboutToShow, this, &DiscoverFeedsButton::fillMenu); - } - - menu()->hide(); - m_addresses = addresses; -} - -void DiscoverFeedsButton::linkTriggered(QAction *action) { - const QString url = action->property("url").toString(); - ServiceRoot *root = static_cast(action->property("root").value()); - - if (root->supportsFeedAdding()) { - root->addNewFeed(url); - } - else { - qApp->showGuiMessage(tr("Not supported"), - tr("Given account does not support adding feeds."), - QSystemTrayIcon::Warning, - qApp->mainFormWidget(), true); - } -} - -void DiscoverFeedsButton::fillMenu() { - menu()->clear(); - - foreach (const ServiceRoot *root, qApp->feedReader()->feedsModel()->serviceRoots()) { - QMenu *root_menu = menu()->addMenu(root->icon(), root->title()); - - foreach (const QString &url, m_addresses) { - if (root->supportsFeedAdding()) { - QAction *url_action = root_menu->addAction(root->icon(), url); - - url_action->setProperty("url", url); - url_action->setProperty("root", QVariant::fromValue((void*) root)); - } - } - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/discoverfeedsbutton.h" + +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/feedreader.h" +#include "gui/dialogs/formmain.h" +#include "gui/tabwidget.h" +#include "gui/feedmessageviewer.h" +#include "gui/feedsview.h" +#include "core/feedsmodel.h" +#include "services/abstract/serviceroot.h" + +#include + + +DiscoverFeedsButton::DiscoverFeedsButton(QWidget* parent) : QToolButton(parent), m_addresses(QStringList()) { + setEnabled(false); + setIcon(qApp->icons()->fromTheme(QSL("application-rss+xml"))); + setPopupMode(QToolButton::InstantPopup); +} + +DiscoverFeedsButton::~DiscoverFeedsButton() { +} + +void DiscoverFeedsButton::clearFeedAddresses() { + setFeedAddresses(QStringList()); +} + +void DiscoverFeedsButton::setFeedAddresses(const QStringList& addresses) { + setEnabled(!addresses.isEmpty()); + setToolTip(addresses.isEmpty() ? + tr("This website does not contain any feeds.") : + tr("Click me to add feeds from this website.\nThis website contains %n feed(s).", 0, addresses.size())); + + if (menu() == nullptr) { + // Initialize the menu. + setMenu(new QMenu(this)); + connect(menu(), &QMenu::triggered, this, &DiscoverFeedsButton::linkTriggered); + connect(menu(), &QMenu::aboutToShow, this, &DiscoverFeedsButton::fillMenu); + } + + menu()->hide(); + m_addresses = addresses; +} + +void DiscoverFeedsButton::linkTriggered(QAction* action) { + const QString url = action->property("url").toString(); + ServiceRoot* root = static_cast(action->property("root").value()); + + if (root->supportsFeedAdding()) { + root->addNewFeed(url); + } + + else { + qApp->showGuiMessage(tr("Not supported"), + tr("Given account does not support adding feeds."), + QSystemTrayIcon::Warning, + qApp->mainFormWidget(), true); + } +} + +void DiscoverFeedsButton::fillMenu() { + menu()->clear(); + + foreach (const ServiceRoot* root, qApp->feedReader()->feedsModel()->serviceRoots()) { + QMenu* root_menu = menu()->addMenu(root->icon(), root->title()); + + foreach (const QString& url, m_addresses) { + if (root->supportsFeedAdding()) { + QAction* url_action = root_menu->addAction(root->icon(), url); + url_action->setProperty("url", url); + url_action->setProperty("root", QVariant::fromValue((void*) root)); + } + } + } +} diff --git a/src/gui/discoverfeedsbutton.h b/src/gui/discoverfeedsbutton.h index abf347e52..ab510106c 100755 --- a/src/gui/discoverfeedsbutton.h +++ b/src/gui/discoverfeedsbutton.h @@ -1,45 +1,45 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef DISCOVERFEEDSBUTTON_H -#define DISCOVERFEEDSBUTTON_H - -#include - - -class DiscoverFeedsButton : public QToolButton { - Q_OBJECT - - public: - // Constructors. - explicit DiscoverFeedsButton(QWidget *parent = 0); - virtual ~DiscoverFeedsButton(); - - // Feed addresses manipulators. - void clearFeedAddresses(); - void setFeedAddresses(const QStringList &addresses); - - private slots: - // User chose any of addresses. - void linkTriggered(QAction *action); - void fillMenu(); - - private: - QStringList m_addresses; -}; - -#endif // DISCOVERFEEDSBUTTON_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef DISCOVERFEEDSBUTTON_H +#define DISCOVERFEEDSBUTTON_H + +#include + + +class DiscoverFeedsButton : public QToolButton { + Q_OBJECT + + public: + // Constructors. + explicit DiscoverFeedsButton(QWidget* parent = 0); + virtual ~DiscoverFeedsButton(); + + // Feed addresses manipulators. + void clearFeedAddresses(); + void setFeedAddresses(const QStringList& addresses); + + private slots: + // User chose any of addresses. + void linkTriggered(QAction* action); + void fillMenu(); + + private: + QStringList m_addresses; +}; + +#endif // DISCOVERFEEDSBUTTON_H diff --git a/src/gui/edittableview.cpp b/src/gui/edittableview.cpp index 85b9985e7..15663d8e3 100755 --- a/src/gui/edittableview.cpp +++ b/src/gui/edittableview.cpp @@ -20,49 +20,50 @@ #include -EditTableView::EditTableView(QWidget *parent) : QTableView(parent) { +EditTableView::EditTableView(QWidget* parent) : QTableView(parent) { } -void EditTableView::keyPressEvent(QKeyEvent *event) { - if (model() && event->key() == Qt::Key_Delete) { - removeSelected(); - event->accept(); - } - else { - QAbstractItemView::keyPressEvent(event); - } +void EditTableView::keyPressEvent(QKeyEvent* event) { + if (model() && event->key() == Qt::Key_Delete) { + removeSelected(); + event->accept(); + } + + else { + QAbstractItemView::keyPressEvent(event); + } } void EditTableView::removeSelected() { - if (!model() || !selectionModel() || !selectionModel()->hasSelection()) { - return; - } + if (!model() || !selectionModel() || !selectionModel()->hasSelection()) { + return; + } - const QModelIndexList selected_rows = selectionModel()->selectedRows(); + const QModelIndexList selected_rows = selectionModel()->selectedRows(); - if (selected_rows.isEmpty()) { - return; - } + if (selected_rows.isEmpty()) { + return; + } - const int new_selected_row = selected_rows.at(0).row(); + const int new_selected_row = selected_rows.at(0).row(); - for (int i = selected_rows.count() - 1; i >= 0; i--) { - QModelIndex idx = selected_rows.at(i); - model()->removeRow(idx.row(), rootIndex()); - } + for (int i = selected_rows.count() - 1; i >= 0; i--) { + QModelIndex idx = selected_rows.at(i); + model()->removeRow(idx.row(), rootIndex()); + } - QModelIndex new_selected_index = model()->index(new_selected_row, 0, rootIndex()); + QModelIndex new_selected_index = model()->index(new_selected_row, 0, rootIndex()); - if (!new_selected_index.isValid()) { - new_selected_index = model()->index(new_selected_row - 1, 0, rootIndex()); - } + if (!new_selected_index.isValid()) { + new_selected_index = model()->index(new_selected_row - 1, 0, rootIndex()); + } - selectionModel()->select(new_selected_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); - setCurrentIndex(new_selected_index); + selectionModel()->select(new_selected_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + setCurrentIndex(new_selected_index); } void EditTableView::removeAll() { - if (model() != nullptr) { - model()->removeRows(0, model()->rowCount(rootIndex()), rootIndex()); - } + if (model() != nullptr) { + model()->removeRows(0, model()->rowCount(rootIndex()), rootIndex()); + } } diff --git a/src/gui/edittableview.h b/src/gui/edittableview.h index de17ad06d..9619aeebd 100755 --- a/src/gui/edittableview.h +++ b/src/gui/edittableview.h @@ -22,17 +22,17 @@ class EditTableView : public QTableView { - Q_OBJECT + Q_OBJECT - public: - explicit EditTableView(QWidget *parent = 0); + public: + explicit EditTableView(QWidget* parent = 0); - public slots: - void removeSelected(); - void removeAll(); + public slots: + void removeSelected(); + void removeAll(); - private: - void keyPressEvent(QKeyEvent *event); + private: + void keyPressEvent(QKeyEvent* event); }; #endif // EDITTABLEVIEW_H diff --git a/src/gui/feedmessageviewer.cpp b/src/gui/feedmessageviewer.cpp index 780bfa439..0045dd0f3 100755 --- a/src/gui/feedmessageviewer.cpp +++ b/src/gui/feedmessageviewer.cpp @@ -63,240 +63,220 @@ #include -FeedMessageViewer::FeedMessageViewer(QWidget *parent) - : TabContent(parent), - m_toolBarsEnabled(true), - m_listHeadersEnabled(true), - m_toolBarFeeds(new FeedsToolBar(tr("Toolbar for feeds"), this)), - m_toolBarMessages(new MessagesToolBar(tr("Toolbar for messages"), this)), - m_messagesView(new MessagesView(this)), - m_feedsView(new FeedsView(this)), +FeedMessageViewer::FeedMessageViewer(QWidget* parent) + : TabContent(parent), + m_toolBarsEnabled(true), + m_listHeadersEnabled(true), + m_toolBarFeeds(new FeedsToolBar(tr("Toolbar for feeds"), this)), + m_toolBarMessages(new MessagesToolBar(tr("Toolbar for messages"), this)), + m_messagesView(new MessagesView(this)), + m_feedsView(new FeedsView(this)), #if defined(USE_WEBENGINE) - m_messagesBrowser(new WebBrowser(this)) { + m_messagesBrowser(new WebBrowser(this)) { #else - m_messagesBrowser(new MessagePreviewer(this)) { + m_messagesBrowser(new MessagePreviewer(this)) { #endif - initialize(); - initializeViews(); - loadMessageViewerFonts(); - createConnections(); + initialize(); + initializeViews(); + loadMessageViewerFonts(); + createConnections(); } FeedMessageViewer::~FeedMessageViewer() { - qDebug("Destroying FeedMessageViewer instance."); + qDebug("Destroying FeedMessageViewer instance."); } #if defined(USE_WEBENGINE) -WebBrowser *FeedMessageViewer::webBrowser() const { - return m_messagesBrowser; +WebBrowser* FeedMessageViewer::webBrowser() const { + return m_messagesBrowser; } #endif -FeedsView *FeedMessageViewer::feedsView() const { - return m_feedsView; +FeedsView* FeedMessageViewer::feedsView() const { + return m_feedsView; } -MessagesView *FeedMessageViewer::messagesView() const { - return m_messagesView; +MessagesView* FeedMessageViewer::messagesView() const { + return m_messagesView; } -MessagesToolBar *FeedMessageViewer::messagesToolBar() const { - return m_toolBarMessages; +MessagesToolBar* FeedMessageViewer::messagesToolBar() const { + return m_toolBarMessages; } -FeedsToolBar *FeedMessageViewer::feedsToolBar() const { - return m_toolBarFeeds; +FeedsToolBar* FeedMessageViewer::feedsToolBar() const { + return m_toolBarFeeds; } void FeedMessageViewer::saveSize() { - Settings *settings = qApp->settings(); - - m_feedsView->saveAllExpandStates(); - - - // Store offsets of splitters. - settings->setValue(GROUP(GUI), GUI::SplitterFeeds, QString(m_feedSplitter->saveState().toBase64())); - settings->setValue(GROUP(GUI), GUI::SplitterMessages, QString(m_messageSplitter->saveState().toBase64())); - settings->setValue(GROUP(GUI), GUI::MessageViewState, QString(m_messagesView->header()->saveState().toBase64())); - - // Store "visibility" of toolbars and list headers. - settings->setValue(GROUP(GUI), GUI::ToolbarsVisible, m_toolBarsEnabled); - settings->setValue(GROUP(GUI), GUI::ListHeadersVisible, m_listHeadersEnabled); + Settings* settings = qApp->settings(); + m_feedsView->saveAllExpandStates(); + // Store offsets of splitters. + settings->setValue(GROUP(GUI), GUI::SplitterFeeds, QString(m_feedSplitter->saveState().toBase64())); + settings->setValue(GROUP(GUI), GUI::SplitterMessages, QString(m_messageSplitter->saveState().toBase64())); + settings->setValue(GROUP(GUI), GUI::MessageViewState, QString(m_messagesView->header()->saveState().toBase64())); + // Store "visibility" of toolbars and list headers. + settings->setValue(GROUP(GUI), GUI::ToolbarsVisible, m_toolBarsEnabled); + settings->setValue(GROUP(GUI), GUI::ListHeadersVisible, m_listHeadersEnabled); } void FeedMessageViewer::loadSize() { - const Settings *settings = qApp->settings(); - - // Restore offsets of splitters. - m_feedSplitter->restoreState(QByteArray::fromBase64(settings->value(GROUP(GUI), SETTING(GUI::SplitterFeeds)).toString().toLocal8Bit())); - m_messageSplitter->restoreState(QByteArray::fromBase64(settings->value(GROUP(GUI), SETTING(GUI::SplitterMessages)).toString().toLocal8Bit())); - - m_messagesView->header()->restoreState(QByteArray::fromBase64(settings->value(GROUP(GUI), SETTING(GUI::MessageViewState)).toString().toLocal8Bit())); + const Settings* settings = qApp->settings(); + // Restore offsets of splitters. + m_feedSplitter->restoreState(QByteArray::fromBase64(settings->value(GROUP(GUI), SETTING(GUI::SplitterFeeds)).toString().toLocal8Bit())); + m_messageSplitter->restoreState(QByteArray::fromBase64(settings->value(GROUP(GUI), SETTING(GUI::SplitterMessages)).toString().toLocal8Bit())); + m_messagesView->header()->restoreState(QByteArray::fromBase64(settings->value(GROUP(GUI), SETTING(GUI::MessageViewState)).toString().toLocal8Bit())); } -void FeedMessageViewer::loadMessageViewerFonts() { - m_messagesBrowser->reloadFontSettings(); +void FeedMessageViewer::loadMessageViewerFonts() { + m_messagesBrowser->reloadFontSettings(); } bool FeedMessageViewer::areToolBarsEnabled() const { - return m_toolBarsEnabled; + return m_toolBarsEnabled; } bool FeedMessageViewer::areListHeadersEnabled() const { - return m_listHeadersEnabled; + return m_listHeadersEnabled; } void FeedMessageViewer::switchMessageSplitterOrientation() { - if (m_messageSplitter->orientation() == Qt::Vertical) { - m_messageSplitter->setOrientation(Qt::Horizontal); - } - else { - m_messageSplitter->setOrientation(Qt::Vertical); - } + if (m_messageSplitter->orientation() == Qt::Vertical) { + m_messageSplitter->setOrientation(Qt::Horizontal); + } + + else { + m_messageSplitter->setOrientation(Qt::Vertical); + } } void FeedMessageViewer::setToolBarsEnabled(bool enable) { - m_toolBarsEnabled = enable; - m_toolBarFeeds->setVisible(enable); - m_toolBarMessages->setVisible(enable); + m_toolBarsEnabled = enable; + m_toolBarFeeds->setVisible(enable); + m_toolBarMessages->setVisible(enable); } void FeedMessageViewer::setListHeadersEnabled(bool enable) { - m_listHeadersEnabled = enable; - m_feedsView->header()->setVisible(enable); - m_messagesView->header()->setVisible(enable); + m_listHeadersEnabled = enable; + m_feedsView->header()->setVisible(enable); + m_messagesView->header()->setVisible(enable); } void FeedMessageViewer::switchFeedComponentVisibility() { - QAction *sen = qobject_cast(sender()); + QAction* sen = qobject_cast(sender()); - if (sen != nullptr) { - m_feedsWidget->setVisible(sen->isChecked()); - } - else { - m_feedsWidget->setVisible(!m_feedsWidget->isVisible()); - } + if (sen != nullptr) { + m_feedsWidget->setVisible(sen->isChecked()); + } + + else { + m_feedsWidget->setVisible(!m_feedsWidget->isVisible()); + } } void FeedMessageViewer::toggleShowOnlyUnreadFeeds() { - const QAction *origin = qobject_cast(sender()); - - if (origin == nullptr) { - m_feedsView->model()->invalidateReadFeedsFilter(true, false); - } - else { - m_feedsView->model()->invalidateReadFeedsFilter(true, origin->isChecked()); - } + const QAction* origin = qobject_cast(sender()); + + if (origin == nullptr) { + m_feedsView->model()->invalidateReadFeedsFilter(true, false); + } + + else { + m_feedsView->model()->invalidateReadFeedsFilter(true, origin->isChecked()); + } } void FeedMessageViewer::createConnections() { - // Filtering & searching. - connect(m_toolBarMessages, &MessagesToolBar::messageSearchPatternChanged, m_messagesView, &MessagesView::searchMessages); - connect(m_toolBarMessages, &MessagesToolBar::messageFilterChanged, m_messagesView, &MessagesView::filterMessages); - + // Filtering & searching. + connect(m_toolBarMessages, &MessagesToolBar::messageSearchPatternChanged, m_messagesView, &MessagesView::searchMessages); + connect(m_toolBarMessages, &MessagesToolBar::messageFilterChanged, m_messagesView, &MessagesView::filterMessages); #if defined(USE_WEBENGINE) - connect(m_messagesView, &MessagesView::currentMessageRemoved, m_messagesBrowser, &WebBrowser::clear); - connect(m_messagesView, &MessagesView::currentMessageChanged, m_messagesBrowser, &WebBrowser::loadMessage); - connect(m_messagesBrowser, &WebBrowser::markMessageRead, - m_messagesView->sourceModel(), &MessagesModel::setMessageReadById); - connect(m_messagesBrowser, &WebBrowser::markMessageImportant, - m_messagesView->sourceModel(), &MessagesModel::setMessageImportantById); + connect(m_messagesView, &MessagesView::currentMessageRemoved, m_messagesBrowser, &WebBrowser::clear); + connect(m_messagesView, &MessagesView::currentMessageChanged, m_messagesBrowser, &WebBrowser::loadMessage); + connect(m_messagesBrowser, &WebBrowser::markMessageRead, + m_messagesView->sourceModel(), &MessagesModel::setMessageReadById); + connect(m_messagesBrowser, &WebBrowser::markMessageImportant, + m_messagesView->sourceModel(), &MessagesModel::setMessageImportantById); #else - connect(m_messagesView, &MessagesView::currentMessageRemoved, m_messagesBrowser, &MessagePreviewer::clear); - connect(m_messagesView, &MessagesView::currentMessageChanged, m_messagesBrowser, &MessagePreviewer::loadMessage); - connect(m_messagesBrowser, &MessagePreviewer::markMessageRead, - m_messagesView->sourceModel(), &MessagesModel::setMessageReadById); - connect(m_messagesBrowser, &MessagePreviewer::markMessageImportant, - m_messagesView->sourceModel(), &MessagesModel::setMessageImportantById); + connect(m_messagesView, &MessagesView::currentMessageRemoved, m_messagesBrowser, &MessagePreviewer::clear); + connect(m_messagesView, &MessagesView::currentMessageChanged, m_messagesBrowser, &MessagePreviewer::loadMessage); + connect(m_messagesBrowser, &MessagePreviewer::markMessageRead, + m_messagesView->sourceModel(), &MessagesModel::setMessageReadById); + connect(m_messagesBrowser, &MessagePreviewer::markMessageImportant, + m_messagesView->sourceModel(), &MessagesModel::setMessageImportantById); #endif - - // If user selects feeds, load their messages. - connect(m_feedsView, &FeedsView::itemSelected, m_messagesView, &MessagesView::loadItem); - - // State of many messages is changed, then we need - // to reload selections. - connect(m_feedsView->sourceModel(), &FeedsModel::reloadMessageListRequested, - m_messagesView, &MessagesView::reloadSelections); + // If user selects feeds, load their messages. + connect(m_feedsView, &FeedsView::itemSelected, m_messagesView, &MessagesView::loadItem); + // State of many messages is changed, then we need + // to reload selections. + connect(m_feedsView->sourceModel(), &FeedsModel::reloadMessageListRequested, + m_messagesView, &MessagesView::reloadSelections); } void FeedMessageViewer::initialize() { - // Initialize/populate toolbars. - m_toolBarFeeds->setFloatable(false); - m_toolBarFeeds->setMovable(false); - m_toolBarFeeds->setAllowedAreas(Qt::TopToolBarArea); - - m_toolBarMessages->setFloatable(false); - m_toolBarMessages->setMovable(false); - m_toolBarMessages->setAllowedAreas(Qt::TopToolBarArea); - - m_toolBarFeeds->loadSavedActions(); - m_toolBarMessages->loadSavedActions(); - - m_messagesBrowser->clear(); - - // Now refresh visual setup. - refreshVisualProperties(); + // Initialize/populate toolbars. + m_toolBarFeeds->setFloatable(false); + m_toolBarFeeds->setMovable(false); + m_toolBarFeeds->setAllowedAreas(Qt::TopToolBarArea); + m_toolBarMessages->setFloatable(false); + m_toolBarMessages->setMovable(false); + m_toolBarMessages->setAllowedAreas(Qt::TopToolBarArea); + m_toolBarFeeds->loadSavedActions(); + m_toolBarMessages->loadSavedActions(); + m_messagesBrowser->clear(); + // Now refresh visual setup. + refreshVisualProperties(); } void FeedMessageViewer::initializeViews() { - m_feedsWidget = new QWidget(this); - m_messagesWidget = new QWidget(this); - m_feedSplitter = new QSplitter(Qt::Horizontal, this); - m_messageSplitter = new QSplitter(Qt::Vertical, this); - - // Instantiate needed components. - QVBoxLayout *central_layout = new QVBoxLayout(this); - QVBoxLayout *feed_layout = new QVBoxLayout(m_feedsWidget); - QVBoxLayout *message_layout = new QVBoxLayout(m_messagesWidget); - - // Set layout properties. - central_layout->setMargin(0); - central_layout->setSpacing(0); - feed_layout->setMargin(0); - feed_layout->setSpacing(0); - message_layout->setMargin(0); - message_layout->setSpacing(0); - - // Set views. - m_feedsView->setFrameStyle(QFrame::NoFrame); - m_messagesView->setFrameStyle(QFrame::NoFrame); - - // Setup message splitter. - m_messageSplitter->setObjectName(QSL("MessageSplitter")); - m_messageSplitter->setHandleWidth(1); - m_messageSplitter->setOpaqueResize(false); - m_messageSplitter->setChildrenCollapsible(false); - m_messageSplitter->addWidget(m_messagesView); - m_messageSplitter->addWidget(m_messagesBrowser); - - // Assemble message-related components to single widget. - message_layout->addWidget(m_toolBarMessages); - message_layout->addWidget(m_messageSplitter); - - // Assemble feed-related components to another widget. - feed_layout->addWidget(m_toolBarFeeds); - feed_layout->addWidget(m_feedsView); - - // Assembler everything together. - m_feedSplitter->setHandleWidth(1); - m_feedSplitter->setOpaqueResize(false); - m_feedSplitter->setChildrenCollapsible(false); - m_feedSplitter->addWidget(m_feedsWidget); - m_feedSplitter->addWidget(m_messagesWidget); - - // Add toolbar and main feeds/messages widget to main layout. - central_layout->addWidget(m_feedSplitter); - - setTabOrder(m_feedsView, m_messagesView); - setTabOrder(m_messagesView, m_toolBarFeeds); - setTabOrder(m_toolBarFeeds, m_toolBarMessages); - setTabOrder(m_toolBarMessages, m_messagesBrowser); + m_feedsWidget = new QWidget(this); + m_messagesWidget = new QWidget(this); + m_feedSplitter = new QSplitter(Qt::Horizontal, this); + m_messageSplitter = new QSplitter(Qt::Vertical, this); + // Instantiate needed components. + QVBoxLayout* central_layout = new QVBoxLayout(this); + QVBoxLayout* feed_layout = new QVBoxLayout(m_feedsWidget); + QVBoxLayout* message_layout = new QVBoxLayout(m_messagesWidget); + // Set layout properties. + central_layout->setMargin(0); + central_layout->setSpacing(0); + feed_layout->setMargin(0); + feed_layout->setSpacing(0); + message_layout->setMargin(0); + message_layout->setSpacing(0); + // Set views. + m_feedsView->setFrameStyle(QFrame::NoFrame); + m_messagesView->setFrameStyle(QFrame::NoFrame); + // Setup message splitter. + m_messageSplitter->setObjectName(QSL("MessageSplitter")); + m_messageSplitter->setHandleWidth(1); + m_messageSplitter->setOpaqueResize(false); + m_messageSplitter->setChildrenCollapsible(false); + m_messageSplitter->addWidget(m_messagesView); + m_messageSplitter->addWidget(m_messagesBrowser); + // Assemble message-related components to single widget. + message_layout->addWidget(m_toolBarMessages); + message_layout->addWidget(m_messageSplitter); + // Assemble feed-related components to another widget. + feed_layout->addWidget(m_toolBarFeeds); + feed_layout->addWidget(m_feedsView); + // Assembler everything together. + m_feedSplitter->setHandleWidth(1); + m_feedSplitter->setOpaqueResize(false); + m_feedSplitter->setChildrenCollapsible(false); + m_feedSplitter->addWidget(m_feedsWidget); + m_feedSplitter->addWidget(m_messagesWidget); + // Add toolbar and main feeds/messages widget to main layout. + central_layout->addWidget(m_feedSplitter); + setTabOrder(m_feedsView, m_messagesView); + setTabOrder(m_messagesView, m_toolBarFeeds); + setTabOrder(m_toolBarFeeds, m_toolBarMessages); + setTabOrder(m_toolBarMessages, m_messagesBrowser); } void FeedMessageViewer::refreshVisualProperties() { - const Qt::ToolButtonStyle button_style = static_cast(qApp->settings()->value(GROUP(GUI), - SETTING(GUI::ToolbarStyle)).toInt()); - - m_toolBarFeeds->setToolButtonStyle(button_style); - m_toolBarMessages->setToolButtonStyle(button_style); + const Qt::ToolButtonStyle button_style = static_cast(qApp->settings()->value(GROUP(GUI), + SETTING(GUI::ToolbarStyle)).toInt()); + m_toolBarFeeds->setToolButtonStyle(button_style); + m_toolBarMessages->setToolButtonStyle(button_style); } diff --git a/src/gui/feedmessageviewer.h b/src/gui/feedmessageviewer.h index de72c371f..b00470c9c 100755 --- a/src/gui/feedmessageviewer.h +++ b/src/gui/feedmessageviewer.h @@ -41,79 +41,79 @@ class QSplitter; class QProgressBar; class FeedMessageViewer : public TabContent { - Q_OBJECT + Q_OBJECT - public: - // Constructors and destructors. - explicit FeedMessageViewer(QWidget *parent = 0); - virtual ~FeedMessageViewer(); + public: + // Constructors and destructors. + explicit FeedMessageViewer(QWidget* parent = 0); + virtual ~FeedMessageViewer(); #if defined(USE_WEBENGINE) - WebBrowser *webBrowser() const; + WebBrowser* webBrowser() const; #endif - FeedsView *feedsView() const; - MessagesView *messagesView() const; - MessagesToolBar *messagesToolBar() const; - FeedsToolBar *feedsToolBar() const; + FeedsView* feedsView() const; + MessagesView* messagesView() const; + MessagesToolBar* messagesToolBar() const; + FeedsToolBar* feedsToolBar() const; - bool areToolBarsEnabled() const; - bool areListHeadersEnabled() const; + bool areToolBarsEnabled() const; + bool areListHeadersEnabled() const; - public slots: - // Loads/saves sizes and states of ALL - // underlying widgets, this contains primarily - // splitters, toolbar and views. - void saveSize(); - void loadSize(); + public slots: + // Loads/saves sizes and states of ALL + // underlying widgets, this contains primarily + // splitters, toolbar and views. + void saveSize(); + void loadSize(); - void loadMessageViewerFonts(); + void loadMessageViewerFonts(); - // Switches orientation horizontal/vertical. - void switchMessageSplitterOrientation(); + // Switches orientation horizontal/vertical. + void switchMessageSplitterOrientation(); - // Enables/disables main toolbars or list headers. - void setToolBarsEnabled(bool enable); - void setListHeadersEnabled(bool enable); + // Enables/disables main toolbars or list headers. + void setToolBarsEnabled(bool enable); + void setListHeadersEnabled(bool enable); - // Reloads some changeable visual settings. - void refreshVisualProperties(); + // Reloads some changeable visual settings. + void refreshVisualProperties(); - // Switches visibility of feed list and related - // toolbar. - void switchFeedComponentVisibility(); + // Switches visibility of feed list and related + // toolbar. + void switchFeedComponentVisibility(); - // Toggles displayed feeds. - void toggleShowOnlyUnreadFeeds(); + // Toggles displayed feeds. + void toggleShowOnlyUnreadFeeds(); - protected: - // Initializes some properties of the widget. - void initialize(); + protected: + // Initializes some properties of the widget. + void initialize(); - // Initializes both messages/feeds views. - void initializeViews(); + // Initializes both messages/feeds views. + void initializeViews(); - // Sets up connections. - void createConnections(); + // Sets up connections. + void createConnections(); - private: - bool m_toolBarsEnabled; - bool m_listHeadersEnabled; - FeedsToolBar *m_toolBarFeeds; - MessagesToolBar *m_toolBarMessages; + private: + bool m_toolBarsEnabled; + bool m_listHeadersEnabled; + FeedsToolBar* m_toolBarFeeds; + MessagesToolBar* m_toolBarMessages; - QSplitter *m_feedSplitter; - QSplitter *m_messageSplitter; + QSplitter* m_feedSplitter; + QSplitter* m_messageSplitter; - MessagesView *m_messagesView; - FeedsView *m_feedsView; - QWidget *m_feedsWidget; - QWidget *m_messagesWidget; + MessagesView* m_messagesView; + FeedsView* m_feedsView; + QWidget* m_feedsWidget; + QWidget* m_messagesWidget; #if defined(USE_WEBENGINE) - WebBrowser *m_messagesBrowser; + WebBrowser* m_messagesBrowser; #else - MessagePreviewer *m_messagesBrowser; + MessagePreviewer* m_messagesBrowser; #endif }; diff --git a/src/gui/feedstoolbar.cpp b/src/gui/feedstoolbar.cpp index 7444bfa8c..8edd3755a 100755 --- a/src/gui/feedstoolbar.cpp +++ b/src/gui/feedstoolbar.cpp @@ -1,105 +1,102 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/feedstoolbar.h" - -#include "miscellaneous/application.h" -#include "miscellaneous/settings.h" -#include "miscellaneous/iconfactory.h" - -#include - - -FeedsToolBar::FeedsToolBar(const QString &title, QWidget *parent) : BaseToolBar(title, parent) { - // Update right margin of filter textbox. - QMargins margins = contentsMargins(); - margins.setRight(margins.right() + FILTER_RIGHT_MARGIN); - setContentsMargins(margins); -} - -FeedsToolBar::~FeedsToolBar() { -} - -QList FeedsToolBar::availableActions() const { - return qApp->userActions(); -} - -QList FeedsToolBar::changeableActions() const { - return actions(); -} - -void FeedsToolBar::saveChangeableActions(const QStringList &actions) { - qApp->settings()->setValue(GROUP(GUI), GUI::FeedsToolbarActions, actions.join(QSL(","))); - loadSpecificActions(getSpecificActions(actions)); -} - -QList FeedsToolBar::getSpecificActions(const QStringList &actions) { - QList available_actions = availableActions(); - QList spec_actions; - - // Iterate action names and add respectable actions into the toolbar. - foreach (const QString &action_name, actions) { - QAction *matching_action = findMatchingAction(action_name, available_actions); - - if (matching_action != nullptr) { - // Add existing standard action. - spec_actions.append(matching_action); - } - else if (action_name == SEPARATOR_ACTION_NAME) { - // Add new separator. - QAction *act = new QAction(this); - act->setSeparator(true); - - spec_actions.append(act); - } - else if (action_name == SPACER_ACTION_NAME) { - // Add new spacer. - QWidget *spacer = new QWidget(this); - spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - QWidgetAction *action = new QWidgetAction(this); - - action->setDefaultWidget(spacer); - action->setIcon(qApp->icons()->fromTheme(QSL("system-search"))); - action->setProperty("type", SPACER_ACTION_NAME); - action->setProperty("name", tr("Toolbar spacer")); - - spec_actions.append(action); - } - } - - return spec_actions; -} - -void FeedsToolBar::loadSpecificActions(const QList &actions) { - clear(); - - foreach (QAction *act, actions) { - addAction(act); - } -} - -QStringList FeedsToolBar::defaultActions() const { - return QString(GUI::FeedsToolbarActionsDef).split(',', - QString::SkipEmptyParts); -} - -QStringList FeedsToolBar::savedActions() const { - return qApp->settings()->value(GROUP(GUI), SETTING(GUI::FeedsToolbarActions)).toString().split(',', - QString::SkipEmptyParts); - -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/feedstoolbar.h" + +#include "miscellaneous/application.h" +#include "miscellaneous/settings.h" +#include "miscellaneous/iconfactory.h" + +#include + + +FeedsToolBar::FeedsToolBar(const QString& title, QWidget* parent) : BaseToolBar(title, parent) { + // Update right margin of filter textbox. + QMargins margins = contentsMargins(); + margins.setRight(margins.right() + FILTER_RIGHT_MARGIN); + setContentsMargins(margins); +} + +FeedsToolBar::~FeedsToolBar() { +} + +QList FeedsToolBar::availableActions() const { + return qApp->userActions(); +} + +QList FeedsToolBar::changeableActions() const { + return actions(); +} + +void FeedsToolBar::saveChangeableActions(const QStringList& actions) { + qApp->settings()->setValue(GROUP(GUI), GUI::FeedsToolbarActions, actions.join(QSL(","))); + loadSpecificActions(getSpecificActions(actions)); +} + +QList FeedsToolBar::getSpecificActions(const QStringList& actions) { + QList available_actions = availableActions(); + QList spec_actions; + + // Iterate action names and add respectable actions into the toolbar. + foreach (const QString& action_name, actions) { + QAction* matching_action = findMatchingAction(action_name, available_actions); + + if (matching_action != nullptr) { + // Add existing standard action. + spec_actions.append(matching_action); + } + + else if (action_name == SEPARATOR_ACTION_NAME) { + // Add new separator. + QAction* act = new QAction(this); + act->setSeparator(true); + spec_actions.append(act); + } + + else if (action_name == SPACER_ACTION_NAME) { + // Add new spacer. + QWidget* spacer = new QWidget(this); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QWidgetAction* action = new QWidgetAction(this); + action->setDefaultWidget(spacer); + action->setIcon(qApp->icons()->fromTheme(QSL("system-search"))); + action->setProperty("type", SPACER_ACTION_NAME); + action->setProperty("name", tr("Toolbar spacer")); + spec_actions.append(action); + } + } + + return spec_actions; +} + +void FeedsToolBar::loadSpecificActions(const QList& actions) { + clear(); + + foreach (QAction* act, actions) { + addAction(act); + } +} + +QStringList FeedsToolBar::defaultActions() const { + return QString(GUI::FeedsToolbarActionsDef).split(',', + QString::SkipEmptyParts); +} + +QStringList FeedsToolBar::savedActions() const { + return qApp->settings()->value(GROUP(GUI), SETTING(GUI::FeedsToolbarActions)).toString().split(',', + QString::SkipEmptyParts); +} diff --git a/src/gui/feedstoolbar.h b/src/gui/feedstoolbar.h index f754799eb..7fa2766ff 100755 --- a/src/gui/feedstoolbar.h +++ b/src/gui/feedstoolbar.h @@ -1,43 +1,43 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FEEDSTOOLBAR_H -#define FEEDSTOOLBAR_H - -#include "gui/basetoolbar.h" - - -class FeedsToolBar : public BaseToolBar { - Q_OBJECT - - public: - // Constructors and destructors. - explicit FeedsToolBar(const QString &title, QWidget *parent = 0); - virtual ~FeedsToolBar(); - - QList availableActions() const; - QList changeableActions() const; - void saveChangeableActions(const QStringList &actions); - - QList getSpecificActions(const QStringList &actions); - void loadSpecificActions(const QList &actions); - - QStringList defaultActions() const; - QStringList savedActions() const; -}; - -#endif // FEEDSTOOLBAR_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FEEDSTOOLBAR_H +#define FEEDSTOOLBAR_H + +#include "gui/basetoolbar.h" + + +class FeedsToolBar : public BaseToolBar { + Q_OBJECT + + public: + // Constructors and destructors. + explicit FeedsToolBar(const QString& title, QWidget* parent = 0); + virtual ~FeedsToolBar(); + + QList availableActions() const; + QList changeableActions() const; + void saveChangeableActions(const QStringList& actions); + + QList getSpecificActions(const QStringList& actions); + void loadSpecificActions(const QList& actions); + + QStringList defaultActions() const; + QStringList savedActions() const; +}; + +#endif // FEEDSTOOLBAR_H diff --git a/src/gui/feedsview.cpp b/src/gui/feedsview.cpp index 07b8542a4..25bcf4ed0 100755 --- a/src/gui/feedsview.cpp +++ b/src/gui/feedsview.cpp @@ -1,560 +1,561 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/feedsview.h" - -#include "definitions/definitions.h" -#include "core/feedsmodel.h" -#include "core/feedsproxymodel.h" -#include "services/abstract/rootitem.h" -#include "miscellaneous/systemfactory.h" -#include "miscellaneous/feedreader.h" -#include "miscellaneous/mutex.h" -#include "gui/systemtrayicon.h" -#include "gui/messagebox.h" -#include "gui/styleditemdelegatewithoutfocus.h" -#include "gui/dialogs/formmain.h" -#include "services/abstract/feed.h" -#include "services/abstract/serviceroot.h" -#include "services/standard/standardcategory.h" -#include "services/standard/standardfeed.h" -#include "services/standard/gui/formstandardcategorydetails.h" - -#include -#include -#include -#include -#include -#include - - -FeedsView::FeedsView(QWidget *parent) - : QTreeView(parent), - m_contextMenuCategories(nullptr), - m_contextMenuFeeds(nullptr), - m_contextMenuEmptySpace(nullptr), - m_contextMenuOtherItems(nullptr) { - setObjectName(QSL("FeedsView")); - - // Allocate models. - m_sourceModel = qApp->feedReader()->feedsModel(); - m_proxyModel = qApp->feedReader()->feedsProxyModel(); - - // Connections. - connect(m_sourceModel, &FeedsModel::requireItemValidationAfterDragDrop, this, &FeedsView::validateItemAfterDragDrop); - connect(m_sourceModel, &FeedsModel::itemExpandRequested, this, &FeedsView::onItemExpandRequested); - connect(m_sourceModel, &FeedsModel::itemExpandStateSaveRequested, this, &FeedsView::onItemExpandStateSaveRequested); - connect(header(), &QHeaderView::sortIndicatorChanged, this, &FeedsView::saveSortState); - connect(m_proxyModel, &FeedsProxyModel::expandAfterFilterIn, this, &FeedsView::expandItemDelayed); - - setModel(m_proxyModel); - setupAppearance(); -} - -FeedsView::~FeedsView() { - qDebug("Destroying FeedsView instance."); -} - -void FeedsView::setSortingEnabled(bool enable) { - disconnect(header(), &QHeaderView::sortIndicatorChanged, this, &FeedsView::saveSortState); - QTreeView::setSortingEnabled(enable); - connect(header(), &QHeaderView::sortIndicatorChanged, this, &FeedsView::saveSortState); -} - -QList FeedsView::selectedFeeds() const { - const QModelIndex current_index = currentIndex(); - - if (current_index.isValid()) { - return m_sourceModel->feedsForIndex(m_proxyModel->mapToSource(current_index)); - } - else { - return QList(); - } -} - -RootItem *FeedsView::selectedItem() const { - const QModelIndexList selected_rows = selectionModel()->selectedRows(); - - if (selected_rows.isEmpty()) { - return nullptr; - } - else { - RootItem *selected_item = m_sourceModel->itemForIndex(m_proxyModel->mapToSource(selected_rows.at(0))); - return selected_item == m_sourceModel->rootItem() ? nullptr : selected_item; - } -} - -void FeedsView::onItemExpandStateSaveRequested(RootItem *item) { - saveExpandStates(item); -} - -void FeedsView::saveAllExpandStates() { - saveExpandStates(sourceModel()->rootItem()); -} - -void FeedsView::saveExpandStates(RootItem *item) { - Settings *settings = qApp->settings(); - QList items = item->getSubTree(RootItemKind::Category | RootItemKind::ServiceRoot); - - // Iterate all categories and save their expand statuses. - foreach (const RootItem *item, items) { - const QString setting_name = item->hashCode(); - - QModelIndex source_index = sourceModel()->indexForItem(item); - QModelIndex visible_index = model()->mapFromSource(source_index); - - settings->setValue(GROUP(CategoriesExpandStates), - setting_name, - isExpanded(visible_index)); - } -} - -void FeedsView::loadAllExpandStates() { - const Settings *settings = qApp->settings(); - QList expandable_items; - - expandable_items.append(sourceModel()->rootItem()->getSubTree(RootItemKind::Category | RootItemKind::ServiceRoot)); - - // Iterate all categories and save their expand statuses. - foreach (const RootItem *item, expandable_items) { - const QString setting_name = item->hashCode(); - - setExpanded(model()->mapFromSource(sourceModel()->indexForItem(item)), - settings->value(GROUP(CategoriesExpandStates), setting_name, item->childCount() > 0).toBool()); - } - - sortByColumn(qApp->settings()->value(GROUP(GUI), SETTING(GUI::DefaultSortColumnFeeds)).toInt(), - static_cast(qApp->settings()->value(GROUP(GUI), SETTING(GUI::DefaultSortOrderFeeds)).toInt())); -} - -void FeedsView::sortByColumn(int column, Qt::SortOrder order) { - const int old_column = header()->sortIndicatorSection(); - const Qt::SortOrder old_order = header()->sortIndicatorOrder(); - - if (column == old_column && order == old_order) { - m_proxyModel->sort(column, order); - } - else { - QTreeView::sortByColumn(column, order); - } -} - -void FeedsView::addFeedIntoSelectedAccount() { - const RootItem *selected = selectedItem(); - - if (selected != nullptr) { - ServiceRoot *root = selected->getParentServiceRoot(); - - if (root->supportsFeedAdding()) { - root->addNewFeed(); - } - else { - qApp->showGuiMessage(tr("Not supported"), - tr("Selected account does not support adding of new feeds."), - QSystemTrayIcon::Warning, - qApp->mainFormWidget(), true); - } - } -} - -void FeedsView::addCategoryIntoSelectedAccount() { - const RootItem *selected = selectedItem(); - - if (selected != nullptr) { - ServiceRoot *root = selected->getParentServiceRoot(); - - if (root->supportsCategoryAdding()) { - root->addNewCategory(); - } - else { - qApp->showGuiMessage(tr("Not supported"), - tr("Selected account does not support adding of new categories."), - QSystemTrayIcon::Warning, - qApp->mainFormWidget(), true); - } - } -} - -void FeedsView::expandCollapseCurrentItem() { - if (selectionModel()->selectedRows().size() == 1) { - QModelIndex index = selectionModel()->selectedRows().at(0); - - if (!index.child(0, 0).isValid() && index.parent().isValid()) { - setCurrentIndex(index.parent()); - index = index.parent(); - } - - isExpanded(index) ? collapse(index) : expand(index); - } -} - -void FeedsView::updateSelectedItems() { - qApp->feedReader()->updateFeeds(selectedFeeds()); -} - -void FeedsView::clearSelectedFeeds() { - m_sourceModel->markItemCleared(selectedItem(), false); -} - -void FeedsView::clearAllFeeds() { - m_sourceModel->markItemCleared(m_sourceModel->rootItem(), false); -} - -void FeedsView::editSelectedItem() { - if (!qApp->feedUpdateLock()->tryLock()) { - // Lock was not obtained because - // it is used probably by feed updater or application - // is quitting. - qApp->showGuiMessage(tr("Cannot edit item"), - tr("Selected item cannot be edited because another critical operation is ongoing."), - QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); - // Thus, cannot delete and quit the method. - return; - } - - if (selectedItem()->canBeEdited()) { - selectedItem()->editViaGui(); - } - else { - qApp->showGuiMessage(tr("Cannot edit item"), - tr("Selected item cannot be edited, this is not (yet?) supported."), - QSystemTrayIcon::Warning, - qApp->mainFormWidget(), - true); - } - - // Changes are done, unlock the update master lock. - qApp->feedUpdateLock()->unlock(); -} - -void FeedsView::deleteSelectedItem() { - if (!qApp->feedUpdateLock()->tryLock()) { - // Lock was not obtained because - // it is used probably by feed updater or application - // is quitting. - qApp->showGuiMessage(tr("Cannot delete item"), - tr("Selected item cannot be deleted because another critical operation is ongoing."), - QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); - - // Thus, cannot delete and quit the method. - return; - } - - if (!currentIndex().isValid()) { - // Changes are done, unlock the update master lock and exit. - qApp->feedUpdateLock()->unlock(); - return; - } - - RootItem *selected_item = selectedItem(); - - if (selected_item != nullptr) { - if (selected_item->canBeDeleted()) { - // Ask user first. - if (MessageBox::show(qApp->mainFormWidget(), - QMessageBox::Question, - tr("Deleting \"%1\"").arg(selected_item->title()), - tr("You are about to completely delete item \"%1\".").arg(selected_item->title()), - tr("Are you sure?"), - QString(), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::No) { - // User refused. - qApp->feedUpdateLock()->unlock(); - return; - } - - // We have deleteable item selected, remove it via GUI. - if (!selected_item->deleteViaGui()) { - qApp->showGuiMessage(tr("Cannot delete \"%1\"").arg(selected_item->title()), - tr("This item cannot be deleted because something critically failed. Submit bug report."), - QSystemTrayIcon::Critical, - qApp->mainFormWidget(), - true); - } - } - else { - qApp->showGuiMessage(tr("Cannot delete \"%1\"").arg(selected_item->title()), - tr("This item cannot be deleted, because it does not support it\nor this functionality is not implemented yet."), - QSystemTrayIcon::Critical, - qApp->mainFormWidget(), - true); - } - } - - // Changes are done, unlock the update master lock. - qApp->feedUpdateLock()->unlock(); -} - -void FeedsView::markSelectedItemReadStatus(RootItem::ReadStatus read) { - m_sourceModel->markItemRead(selectedItem(), read); -} - -void FeedsView::markSelectedItemRead() { - markSelectedItemReadStatus(RootItem::Read); -} - -void FeedsView::markSelectedItemUnread() { - markSelectedItemReadStatus(RootItem::Unread); -} - -void FeedsView::markAllItemsReadStatus(RootItem::ReadStatus read) { - m_sourceModel->markItemRead(m_sourceModel->rootItem(), read); -} - -void FeedsView::markAllItemsRead() { - markAllItemsReadStatus(RootItem::Read); -} - -void FeedsView::openSelectedItemsInNewspaperMode() { - RootItem *selected_item = selectedItem(); - const QList messages = m_sourceModel->messagesForItem(selected_item); - - if (!messages.isEmpty()) { - emit openMessagesInNewspaperView(selected_item, messages); - } -} - -void FeedsView::selectNextItem() { - const QModelIndex &curr_idx = currentIndex(); - - if (m_proxyModel->hasChildren(curr_idx) && !isExpanded(curr_idx)) { - expand(curr_idx); - } - - const QModelIndex &index_next = moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier); - - if (index_next.isValid()) { - setCurrentIndex(index_next); - setFocus(); - } -} - -void FeedsView::selectPreviousItem() { - const QModelIndex &curr_idx = currentIndex(); - - if (m_proxyModel->hasChildren(curr_idx) && !isExpanded(curr_idx)) { - expand(curr_idx); - } - - const QModelIndex &index_previous = moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier); - - if (index_previous.isValid()) { - setCurrentIndex(index_previous); - setFocus(); - } -} - -void FeedsView::switchVisibility() { - setVisible(!isVisible()); -} - -void FeedsView::expandItemDelayed(const QModelIndex &idx) { - QTimer::singleShot(100, this, [=] { - // TODO: Z nastavení. - setExpanded(m_proxyModel->mapFromSource(idx), true); - }); -} - -QMenu *FeedsView::initializeContextMenuCategories(RootItem *clicked_item) { - if (m_contextMenuCategories == nullptr) { - m_contextMenuCategories = new QMenu(tr("Context menu for categories"), this); - } - else { - m_contextMenuCategories->clear(); - } - - QList specific_actions = clicked_item->contextMenu(); - - m_contextMenuCategories->addActions(QList() << - qApp->mainForm()->m_ui->m_actionUpdateSelectedItems << - qApp->mainForm()->m_ui->m_actionEditSelectedItem << - qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode << - qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsRead << - qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsUnread << - qApp->mainForm()->m_ui->m_actionDeleteSelectedItem); - - if (!specific_actions.isEmpty()) { - m_contextMenuCategories->addSeparator(); - m_contextMenuCategories->addActions(specific_actions); - } - - return m_contextMenuCategories; -} - -QMenu *FeedsView::initializeContextMenuFeeds(RootItem *clicked_item) { - if (m_contextMenuFeeds == nullptr) { - m_contextMenuFeeds = new QMenu(tr("Context menu for categories"), this); - } - else { - m_contextMenuFeeds->clear(); - } - - QList specific_actions = clicked_item->contextMenu(); - - m_contextMenuFeeds->addActions(QList() << - qApp->mainForm()->m_ui->m_actionUpdateSelectedItems << - qApp->mainForm()->m_ui->m_actionEditSelectedItem << - qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode << - qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsRead << - qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsUnread << - qApp->mainForm()->m_ui->m_actionDeleteSelectedItem); - - if (!specific_actions.isEmpty()) { - m_contextMenuFeeds->addSeparator(); - m_contextMenuFeeds->addActions(specific_actions); - } - - return m_contextMenuFeeds; -} - -QMenu *FeedsView::initializeContextMenuEmptySpace() { - if (m_contextMenuEmptySpace == nullptr) { - m_contextMenuEmptySpace = new QMenu(tr("Context menu for empty space"), this); - m_contextMenuEmptySpace->addAction(qApp->mainForm()->m_ui->m_actionUpdateAllItems); - m_contextMenuEmptySpace->addSeparator(); - } - - return m_contextMenuEmptySpace; -} - -QMenu *FeedsView::initializeContextMenuOtherItem(RootItem *clicked_item) { - if (m_contextMenuOtherItems == nullptr) { - m_contextMenuOtherItems = new QMenu(tr("Context menu for other items"), this); - } - else { - m_contextMenuOtherItems->clear(); - } - - QList specific_actions = clicked_item->contextMenu(); - - if (!specific_actions.isEmpty()) { - m_contextMenuOtherItems->addSeparator(); - m_contextMenuOtherItems->addActions(specific_actions); - } - else { - m_contextMenuOtherItems->addAction(qApp->mainForm()->m_ui->m_actionNoActions); - } - - return m_contextMenuOtherItems; -} - -void FeedsView::setupAppearance() { - // Setup column resize strategies. - header()->setSectionResizeMode(FDS_MODEL_TITLE_INDEX, QHeaderView::Stretch); - header()->setSectionResizeMode(FDS_MODEL_COUNTS_INDEX, QHeaderView::ResizeToContents); - - setUniformRowHeights(true); - setAnimated(true); - setSortingEnabled(true); - setItemsExpandable(true); - setExpandsOnDoubleClick(true); - setEditTriggers(QAbstractItemView::NoEditTriggers); - setIndentation(FEEDS_VIEW_INDENTATION); - setAcceptDrops(false); - setDragEnabled(true); - setDropIndicatorShown(true); - setDragDropMode(QAbstractItemView::InternalMove); - setAllColumnsShowFocus(false); - setRootIsDecorated(false); - setSelectionMode(QAbstractItemView::SingleSelection); - setItemDelegate(new StyledItemDelegateWithoutFocus(this)); - header()->setStretchLastSection(false); - header()->setSortIndicatorShown(false); -} - -void FeedsView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { - RootItem *selected_item = selectedItem(); - - m_proxyModel->setSelectedItem(selected_item); - QTreeView::selectionChanged(selected, deselected); - emit itemSelected(selected_item); - m_proxyModel->invalidateReadFeedsFilter(); -} - -void FeedsView::keyPressEvent(QKeyEvent *event) { - QTreeView::keyPressEvent(event); - - if (event->key() == Qt::Key_Delete) { - deleteSelectedItem(); - } -} - -void FeedsView::contextMenuEvent(QContextMenuEvent *event) { - const QModelIndex clicked_index = indexAt(event->pos()); - - if (clicked_index.isValid()) { - const QModelIndex mapped_index = model()->mapToSource(clicked_index); - RootItem *clicked_item = sourceModel()->itemForIndex(mapped_index); - - if (clicked_item->kind() == RootItemKind::Category) { - // Display context menu for categories. - initializeContextMenuCategories(clicked_item)->exec(event->globalPos()); - } - else if (clicked_item->kind() == RootItemKind::Feed) { - // Display context menu for feeds. - initializeContextMenuFeeds(clicked_item)->exec(event->globalPos()); - } - else { - initializeContextMenuOtherItem(clicked_item)->exec(event->globalPos()); - } - } - else { - // Display menu for empty space. - initializeContextMenuEmptySpace()->exec(event->globalPos()); - } -} - -void FeedsView::mouseDoubleClickEvent(QMouseEvent *event) { - QModelIndex idx = indexAt(event->pos()); - - if (idx.isValid()) { - RootItem *item = m_sourceModel->itemForIndex(m_proxyModel->mapToSource(idx)); - - if (item->kind() == RootItemKind::Feed || item->kind() == RootItemKind::Bin) { - const QList messages = m_sourceModel->messagesForItem(item); - - if (!messages.isEmpty()) { - emit openMessagesInNewspaperView(item, messages); - } - } - } - - QTreeView::mouseDoubleClickEvent(event); -} - -void FeedsView::saveSortState(int column, Qt::SortOrder order) { - qApp->settings()->setValue(GROUP(GUI), GUI::DefaultSortColumnFeeds, column); - qApp->settings()->setValue(GROUP(GUI), GUI::DefaultSortOrderFeeds, order); -} - -void FeedsView::validateItemAfterDragDrop(const QModelIndex &source_index) { - const QModelIndex mapped = m_proxyModel->mapFromSource(source_index); - - if (mapped.isValid()) { - expand(mapped); - setCurrentIndex(mapped); - } -} - -void FeedsView::onItemExpandRequested(const QList &items, bool exp) { - foreach (const RootItem *item, items) { - QModelIndex source_index = m_sourceModel->indexForItem(item); - QModelIndex proxy_index = m_proxyModel->mapFromSource(source_index); - - //setExpanded(proxy_index, !exp); - setExpanded(proxy_index, exp); - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/feedsview.h" + +#include "definitions/definitions.h" +#include "core/feedsmodel.h" +#include "core/feedsproxymodel.h" +#include "services/abstract/rootitem.h" +#include "miscellaneous/systemfactory.h" +#include "miscellaneous/feedreader.h" +#include "miscellaneous/mutex.h" +#include "gui/systemtrayicon.h" +#include "gui/messagebox.h" +#include "gui/styleditemdelegatewithoutfocus.h" +#include "gui/dialogs/formmain.h" +#include "services/abstract/feed.h" +#include "services/abstract/serviceroot.h" +#include "services/standard/standardcategory.h" +#include "services/standard/standardfeed.h" +#include "services/standard/gui/formstandardcategorydetails.h" + +#include +#include +#include +#include +#include +#include + + +FeedsView::FeedsView(QWidget* parent) + : QTreeView(parent), + m_contextMenuCategories(nullptr), + m_contextMenuFeeds(nullptr), + m_contextMenuEmptySpace(nullptr), + m_contextMenuOtherItems(nullptr) { + setObjectName(QSL("FeedsView")); + // Allocate models. + m_sourceModel = qApp->feedReader()->feedsModel(); + m_proxyModel = qApp->feedReader()->feedsProxyModel(); + // Connections. + connect(m_sourceModel, &FeedsModel::requireItemValidationAfterDragDrop, this, &FeedsView::validateItemAfterDragDrop); + connect(m_sourceModel, &FeedsModel::itemExpandRequested, this, &FeedsView::onItemExpandRequested); + connect(m_sourceModel, &FeedsModel::itemExpandStateSaveRequested, this, &FeedsView::onItemExpandStateSaveRequested); + connect(header(), &QHeaderView::sortIndicatorChanged, this, &FeedsView::saveSortState); + connect(m_proxyModel, &FeedsProxyModel::expandAfterFilterIn, this, &FeedsView::expandItemDelayed); + setModel(m_proxyModel); + setupAppearance(); +} + +FeedsView::~FeedsView() { + qDebug("Destroying FeedsView instance."); +} + +void FeedsView::setSortingEnabled(bool enable) { + disconnect(header(), &QHeaderView::sortIndicatorChanged, this, &FeedsView::saveSortState); + QTreeView::setSortingEnabled(enable); + connect(header(), &QHeaderView::sortIndicatorChanged, this, &FeedsView::saveSortState); +} + +QList FeedsView::selectedFeeds() const { + const QModelIndex current_index = currentIndex(); + + if (current_index.isValid()) { + return m_sourceModel->feedsForIndex(m_proxyModel->mapToSource(current_index)); + } + + else { + return QList(); + } +} + +RootItem* FeedsView::selectedItem() const { + const QModelIndexList selected_rows = selectionModel()->selectedRows(); + + if (selected_rows.isEmpty()) { + return nullptr; + } + + else { + RootItem* selected_item = m_sourceModel->itemForIndex(m_proxyModel->mapToSource(selected_rows.at(0))); + return selected_item == m_sourceModel->rootItem() ? nullptr : selected_item; + } +} + +void FeedsView::onItemExpandStateSaveRequested(RootItem* item) { + saveExpandStates(item); +} + +void FeedsView::saveAllExpandStates() { + saveExpandStates(sourceModel()->rootItem()); +} + +void FeedsView::saveExpandStates(RootItem* item) { + Settings* settings = qApp->settings(); + QList items = item->getSubTree(RootItemKind::Category | RootItemKind::ServiceRoot); + + // Iterate all categories and save their expand statuses. + foreach (const RootItem* item, items) { + const QString setting_name = item->hashCode(); + QModelIndex source_index = sourceModel()->indexForItem(item); + QModelIndex visible_index = model()->mapFromSource(source_index); + settings->setValue(GROUP(CategoriesExpandStates), + setting_name, + isExpanded(visible_index)); + } +} + +void FeedsView::loadAllExpandStates() { + const Settings* settings = qApp->settings(); + QList expandable_items; + expandable_items.append(sourceModel()->rootItem()->getSubTree(RootItemKind::Category | RootItemKind::ServiceRoot)); + + // Iterate all categories and save their expand statuses. + foreach (const RootItem* item, expandable_items) { + const QString setting_name = item->hashCode(); + setExpanded(model()->mapFromSource(sourceModel()->indexForItem(item)), + settings->value(GROUP(CategoriesExpandStates), setting_name, item->childCount() > 0).toBool()); + } + + sortByColumn(qApp->settings()->value(GROUP(GUI), SETTING(GUI::DefaultSortColumnFeeds)).toInt(), + static_cast(qApp->settings()->value(GROUP(GUI), SETTING(GUI::DefaultSortOrderFeeds)).toInt())); +} + +void FeedsView::sortByColumn(int column, Qt::SortOrder order) { + const int old_column = header()->sortIndicatorSection(); + const Qt::SortOrder old_order = header()->sortIndicatorOrder(); + + if (column == old_column && order == old_order) { + m_proxyModel->sort(column, order); + } + + else { + QTreeView::sortByColumn(column, order); + } +} + +void FeedsView::addFeedIntoSelectedAccount() { + const RootItem* selected = selectedItem(); + + if (selected != nullptr) { + ServiceRoot* root = selected->getParentServiceRoot(); + + if (root->supportsFeedAdding()) { + root->addNewFeed(); + } + + else { + qApp->showGuiMessage(tr("Not supported"), + tr("Selected account does not support adding of new feeds."), + QSystemTrayIcon::Warning, + qApp->mainFormWidget(), true); + } + } +} + +void FeedsView::addCategoryIntoSelectedAccount() { + const RootItem* selected = selectedItem(); + + if (selected != nullptr) { + ServiceRoot* root = selected->getParentServiceRoot(); + + if (root->supportsCategoryAdding()) { + root->addNewCategory(); + } + + else { + qApp->showGuiMessage(tr("Not supported"), + tr("Selected account does not support adding of new categories."), + QSystemTrayIcon::Warning, + qApp->mainFormWidget(), true); + } + } +} + +void FeedsView::expandCollapseCurrentItem() { + if (selectionModel()->selectedRows().size() == 1) { + QModelIndex index = selectionModel()->selectedRows().at(0); + + if (!index.child(0, 0).isValid() && index.parent().isValid()) { + setCurrentIndex(index.parent()); + index = index.parent(); + } + + isExpanded(index) ? collapse(index) : expand(index); + } +} + +void FeedsView::updateSelectedItems() { + qApp->feedReader()->updateFeeds(selectedFeeds()); +} + +void FeedsView::clearSelectedFeeds() { + m_sourceModel->markItemCleared(selectedItem(), false); +} + +void FeedsView::clearAllFeeds() { + m_sourceModel->markItemCleared(m_sourceModel->rootItem(), false); +} + +void FeedsView::editSelectedItem() { + if (!qApp->feedUpdateLock()->tryLock()) { + // Lock was not obtained because + // it is used probably by feed updater or application + // is quitting. + qApp->showGuiMessage(tr("Cannot edit item"), + tr("Selected item cannot be edited because another critical operation is ongoing."), + QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); + // Thus, cannot delete and quit the method. + return; + } + + if (selectedItem()->canBeEdited()) { + selectedItem()->editViaGui(); + } + + else { + qApp->showGuiMessage(tr("Cannot edit item"), + tr("Selected item cannot be edited, this is not (yet?) supported."), + QSystemTrayIcon::Warning, + qApp->mainFormWidget(), + true); + } + + // Changes are done, unlock the update master lock. + qApp->feedUpdateLock()->unlock(); +} + +void FeedsView::deleteSelectedItem() { + if (!qApp->feedUpdateLock()->tryLock()) { + // Lock was not obtained because + // it is used probably by feed updater or application + // is quitting. + qApp->showGuiMessage(tr("Cannot delete item"), + tr("Selected item cannot be deleted because another critical operation is ongoing."), + QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); + // Thus, cannot delete and quit the method. + return; + } + + if (!currentIndex().isValid()) { + // Changes are done, unlock the update master lock and exit. + qApp->feedUpdateLock()->unlock(); + return; + } + + RootItem* selected_item = selectedItem(); + + if (selected_item != nullptr) { + if (selected_item->canBeDeleted()) { + // Ask user first. + if (MessageBox::show(qApp->mainFormWidget(), + QMessageBox::Question, + tr("Deleting \"%1\"").arg(selected_item->title()), + tr("You are about to completely delete item \"%1\".").arg(selected_item->title()), + tr("Are you sure?"), + QString(), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::No) { + // User refused. + qApp->feedUpdateLock()->unlock(); + return; + } + + // We have deleteable item selected, remove it via GUI. + if (!selected_item->deleteViaGui()) { + qApp->showGuiMessage(tr("Cannot delete \"%1\"").arg(selected_item->title()), + tr("This item cannot be deleted because something critically failed. Submit bug report."), + QSystemTrayIcon::Critical, + qApp->mainFormWidget(), + true); + } + } + + else { + qApp->showGuiMessage(tr("Cannot delete \"%1\"").arg(selected_item->title()), + tr("This item cannot be deleted, because it does not support it\nor this functionality is not implemented yet."), + QSystemTrayIcon::Critical, + qApp->mainFormWidget(), + true); + } + } + + // Changes are done, unlock the update master lock. + qApp->feedUpdateLock()->unlock(); +} + +void FeedsView::markSelectedItemReadStatus(RootItem::ReadStatus read) { + m_sourceModel->markItemRead(selectedItem(), read); +} + +void FeedsView::markSelectedItemRead() { + markSelectedItemReadStatus(RootItem::Read); +} + +void FeedsView::markSelectedItemUnread() { + markSelectedItemReadStatus(RootItem::Unread); +} + +void FeedsView::markAllItemsReadStatus(RootItem::ReadStatus read) { + m_sourceModel->markItemRead(m_sourceModel->rootItem(), read); +} + +void FeedsView::markAllItemsRead() { + markAllItemsReadStatus(RootItem::Read); +} + +void FeedsView::openSelectedItemsInNewspaperMode() { + RootItem* selected_item = selectedItem(); + const QList messages = m_sourceModel->messagesForItem(selected_item); + + if (!messages.isEmpty()) { + emit openMessagesInNewspaperView(selected_item, messages); + } +} + +void FeedsView::selectNextItem() { + const QModelIndex& curr_idx = currentIndex(); + + if (m_proxyModel->hasChildren(curr_idx) && !isExpanded(curr_idx)) { + expand(curr_idx); + } + + const QModelIndex& index_next = moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier); + + if (index_next.isValid()) { + setCurrentIndex(index_next); + setFocus(); + } +} + +void FeedsView::selectPreviousItem() { + const QModelIndex& curr_idx = currentIndex(); + + if (m_proxyModel->hasChildren(curr_idx) && !isExpanded(curr_idx)) { + expand(curr_idx); + } + + const QModelIndex& index_previous = moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier); + + if (index_previous.isValid()) { + setCurrentIndex(index_previous); + setFocus(); + } +} + +void FeedsView::switchVisibility() { + setVisible(!isVisible()); +} + +void FeedsView::expandItemDelayed(const QModelIndex& idx) { + QTimer::singleShot(100, this, [ = ] { + // TODO: Z nastavení. + setExpanded(m_proxyModel->mapFromSource(idx), true); + }); +} + +QMenu* FeedsView::initializeContextMenuCategories(RootItem* clicked_item) { + if (m_contextMenuCategories == nullptr) { + m_contextMenuCategories = new QMenu(tr("Context menu for categories"), this); + } + + else { + m_contextMenuCategories->clear(); + } + + QList specific_actions = clicked_item->contextMenu(); + m_contextMenuCategories->addActions(QList() << + qApp->mainForm()->m_ui->m_actionUpdateSelectedItems << + qApp->mainForm()->m_ui->m_actionEditSelectedItem << + qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode << + qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsRead << + qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsUnread << + qApp->mainForm()->m_ui->m_actionDeleteSelectedItem); + + if (!specific_actions.isEmpty()) { + m_contextMenuCategories->addSeparator(); + m_contextMenuCategories->addActions(specific_actions); + } + + return m_contextMenuCategories; +} + +QMenu* FeedsView::initializeContextMenuFeeds(RootItem* clicked_item) { + if (m_contextMenuFeeds == nullptr) { + m_contextMenuFeeds = new QMenu(tr("Context menu for categories"), this); + } + + else { + m_contextMenuFeeds->clear(); + } + + QList specific_actions = clicked_item->contextMenu(); + m_contextMenuFeeds->addActions(QList() << + qApp->mainForm()->m_ui->m_actionUpdateSelectedItems << + qApp->mainForm()->m_ui->m_actionEditSelectedItem << + qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode << + qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsRead << + qApp->mainForm()->m_ui->m_actionMarkSelectedItemsAsUnread << + qApp->mainForm()->m_ui->m_actionDeleteSelectedItem); + + if (!specific_actions.isEmpty()) { + m_contextMenuFeeds->addSeparator(); + m_contextMenuFeeds->addActions(specific_actions); + } + + return m_contextMenuFeeds; +} + +QMenu* FeedsView::initializeContextMenuEmptySpace() { + if (m_contextMenuEmptySpace == nullptr) { + m_contextMenuEmptySpace = new QMenu(tr("Context menu for empty space"), this); + m_contextMenuEmptySpace->addAction(qApp->mainForm()->m_ui->m_actionUpdateAllItems); + m_contextMenuEmptySpace->addSeparator(); + } + + return m_contextMenuEmptySpace; +} + +QMenu* FeedsView::initializeContextMenuOtherItem(RootItem* clicked_item) { + if (m_contextMenuOtherItems == nullptr) { + m_contextMenuOtherItems = new QMenu(tr("Context menu for other items"), this); + } + + else { + m_contextMenuOtherItems->clear(); + } + + QList specific_actions = clicked_item->contextMenu(); + + if (!specific_actions.isEmpty()) { + m_contextMenuOtherItems->addSeparator(); + m_contextMenuOtherItems->addActions(specific_actions); + } + + else { + m_contextMenuOtherItems->addAction(qApp->mainForm()->m_ui->m_actionNoActions); + } + + return m_contextMenuOtherItems; +} + +void FeedsView::setupAppearance() { + // Setup column resize strategies. + header()->setSectionResizeMode(FDS_MODEL_TITLE_INDEX, QHeaderView::Stretch); + header()->setSectionResizeMode(FDS_MODEL_COUNTS_INDEX, QHeaderView::ResizeToContents); + setUniformRowHeights(true); + setAnimated(true); + setSortingEnabled(true); + setItemsExpandable(true); + setExpandsOnDoubleClick(true); + setEditTriggers(QAbstractItemView::NoEditTriggers); + setIndentation(FEEDS_VIEW_INDENTATION); + setAcceptDrops(false); + setDragEnabled(true); + setDropIndicatorShown(true); + setDragDropMode(QAbstractItemView::InternalMove); + setAllColumnsShowFocus(false); + setRootIsDecorated(false); + setSelectionMode(QAbstractItemView::SingleSelection); + setItemDelegate(new StyledItemDelegateWithoutFocus(this)); + header()->setStretchLastSection(false); + header()->setSortIndicatorShown(false); +} + +void FeedsView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { + RootItem* selected_item = selectedItem(); + m_proxyModel->setSelectedItem(selected_item); + QTreeView::selectionChanged(selected, deselected); + emit itemSelected(selected_item); + m_proxyModel->invalidateReadFeedsFilter(); +} + +void FeedsView::keyPressEvent(QKeyEvent* event) { + QTreeView::keyPressEvent(event); + + if (event->key() == Qt::Key_Delete) { + deleteSelectedItem(); + } +} + +void FeedsView::contextMenuEvent(QContextMenuEvent* event) { + const QModelIndex clicked_index = indexAt(event->pos()); + + if (clicked_index.isValid()) { + const QModelIndex mapped_index = model()->mapToSource(clicked_index); + RootItem* clicked_item = sourceModel()->itemForIndex(mapped_index); + + if (clicked_item->kind() == RootItemKind::Category) { + // Display context menu for categories. + initializeContextMenuCategories(clicked_item)->exec(event->globalPos()); + } + + else if (clicked_item->kind() == RootItemKind::Feed) { + // Display context menu for feeds. + initializeContextMenuFeeds(clicked_item)->exec(event->globalPos()); + } + + else { + initializeContextMenuOtherItem(clicked_item)->exec(event->globalPos()); + } + } + + else { + // Display menu for empty space. + initializeContextMenuEmptySpace()->exec(event->globalPos()); + } +} + +void FeedsView::mouseDoubleClickEvent(QMouseEvent* event) { + QModelIndex idx = indexAt(event->pos()); + + if (idx.isValid()) { + RootItem* item = m_sourceModel->itemForIndex(m_proxyModel->mapToSource(idx)); + + if (item->kind() == RootItemKind::Feed || item->kind() == RootItemKind::Bin) { + const QList messages = m_sourceModel->messagesForItem(item); + + if (!messages.isEmpty()) { + emit openMessagesInNewspaperView(item, messages); + } + } + } + + QTreeView::mouseDoubleClickEvent(event); +} + +void FeedsView::saveSortState(int column, Qt::SortOrder order) { + qApp->settings()->setValue(GROUP(GUI), GUI::DefaultSortColumnFeeds, column); + qApp->settings()->setValue(GROUP(GUI), GUI::DefaultSortOrderFeeds, order); +} + +void FeedsView::validateItemAfterDragDrop(const QModelIndex& source_index) { + const QModelIndex mapped = m_proxyModel->mapFromSource(source_index); + + if (mapped.isValid()) { + expand(mapped); + setCurrentIndex(mapped); + } +} + +void FeedsView::onItemExpandRequested(const QList& items, bool exp) { + foreach (const RootItem* item, items) { + QModelIndex source_index = m_sourceModel->indexForItem(item); + QModelIndex proxy_index = m_proxyModel->mapFromSource(source_index); + //setExpanded(proxy_index, !exp); + setExpanded(proxy_index, exp); + } +} diff --git a/src/gui/feedsview.h b/src/gui/feedsview.h index c5e98d850..fa0a1cc30 100755 --- a/src/gui/feedsview.h +++ b/src/gui/feedsview.h @@ -1,146 +1,146 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FEEDSVIEW_H -#define FEEDSVIEW_H - -#include - -#include "core/feedsmodel.h" - -#include - - -class FeedsProxyModel; -class Feed; -class Category; - -class FeedsView : public QTreeView { - Q_OBJECT - - public: - // Constructors and destructors. - explicit FeedsView(QWidget *parent = 0); - virtual ~FeedsView(); - - // Fundamental accessors. - inline FeedsProxyModel *model() const { - return m_proxyModel; - } - - inline FeedsModel *sourceModel() const { - return m_sourceModel; - } - - void setSortingEnabled(bool enable); - - // Returns list of selected/all feeds. - // NOTE: This is recursive method which returns all descendants. - QList selectedFeeds() const; - - // Returns pointers to selected feed/category if they are really - // selected. - RootItem *selectedItem() const; - - // Saves/loads expand states of all nodes (feeds/categories) of the list to/from settings. - void saveAllExpandStates(); - void loadAllExpandStates(); - - public slots: - void sortByColumn(int column, Qt::SortOrder order); - - void addFeedIntoSelectedAccount(); - void addCategoryIntoSelectedAccount(); - void expandCollapseCurrentItem(); - - // Feed updating. - void updateSelectedItems(); - - // Feed read/unread manipulators. - void markSelectedItemRead(); - void markSelectedItemUnread(); - void markAllItemsRead(); - - // Newspaper accessors. - void openSelectedItemsInNewspaperMode(); - - // Feed clearers. - void clearSelectedFeeds(); - void clearAllFeeds(); - - // Base manipulators. - void editSelectedItem(); - void deleteSelectedItem(); - - // Selects next/previous item (feed/category) in the list. - void selectNextItem(); - void selectPreviousItem(); - - // Switches visibility of the widget. - void switchVisibility(); - - signals: - // Emitted if user selects new feeds. - void itemSelected(RootItem *item); - - // Requests opening of given messages in newspaper mode. - void openMessagesInNewspaperView(RootItem *root, const QList &messages); - - protected: - // Handle selections. - void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); - - // React on "Del" key. - void keyPressEvent(QKeyEvent *event); - - // Show custom context menu. - void contextMenuEvent(QContextMenuEvent *event); - - void mouseDoubleClickEvent(QMouseEvent *event); - - private slots: - void expandItemDelayed(const QModelIndex &idx); - void markSelectedItemReadStatus(RootItem::ReadStatus read); - void markAllItemsReadStatus(RootItem::ReadStatus read); - - void saveSortState(int column, Qt::SortOrder order); - void validateItemAfterDragDrop(const QModelIndex &source_index); - void onItemExpandRequested(const QList &items, bool exp); - void onItemExpandStateSaveRequested(RootItem *item); - - private: - // Initializes context menus. - QMenu *initializeContextMenuCategories(RootItem *clicked_item); - QMenu *initializeContextMenuFeeds(RootItem *clicked_item); - QMenu *initializeContextMenuEmptySpace(); - QMenu *initializeContextMenuOtherItem(RootItem *clicked_item); - - // Sets up appearance of this widget. - void setupAppearance(); - - void saveExpandStates(RootItem *item); - - QMenu *m_contextMenuCategories; - QMenu *m_contextMenuFeeds; - QMenu *m_contextMenuEmptySpace; - QMenu *m_contextMenuOtherItems; - - FeedsModel *m_sourceModel; - FeedsProxyModel *m_proxyModel; -}; - -#endif // FEEDSVIEW_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FEEDSVIEW_H +#define FEEDSVIEW_H + +#include + +#include "core/feedsmodel.h" + +#include + + +class FeedsProxyModel; +class Feed; +class Category; + +class FeedsView : public QTreeView { + Q_OBJECT + + public: + // Constructors and destructors. + explicit FeedsView(QWidget* parent = 0); + virtual ~FeedsView(); + + // Fundamental accessors. + inline FeedsProxyModel* model() const { + return m_proxyModel; + } + + inline FeedsModel* sourceModel() const { + return m_sourceModel; + } + + void setSortingEnabled(bool enable); + + // Returns list of selected/all feeds. + // NOTE: This is recursive method which returns all descendants. + QList selectedFeeds() const; + + // Returns pointers to selected feed/category if they are really + // selected. + RootItem* selectedItem() const; + + // Saves/loads expand states of all nodes (feeds/categories) of the list to/from settings. + void saveAllExpandStates(); + void loadAllExpandStates(); + + public slots: + void sortByColumn(int column, Qt::SortOrder order); + + void addFeedIntoSelectedAccount(); + void addCategoryIntoSelectedAccount(); + void expandCollapseCurrentItem(); + + // Feed updating. + void updateSelectedItems(); + + // Feed read/unread manipulators. + void markSelectedItemRead(); + void markSelectedItemUnread(); + void markAllItemsRead(); + + // Newspaper accessors. + void openSelectedItemsInNewspaperMode(); + + // Feed clearers. + void clearSelectedFeeds(); + void clearAllFeeds(); + + // Base manipulators. + void editSelectedItem(); + void deleteSelectedItem(); + + // Selects next/previous item (feed/category) in the list. + void selectNextItem(); + void selectPreviousItem(); + + // Switches visibility of the widget. + void switchVisibility(); + + signals: + // Emitted if user selects new feeds. + void itemSelected(RootItem* item); + + // Requests opening of given messages in newspaper mode. + void openMessagesInNewspaperView(RootItem* root, const QList& messages); + + protected: + // Handle selections. + void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + + // React on "Del" key. + void keyPressEvent(QKeyEvent* event); + + // Show custom context menu. + void contextMenuEvent(QContextMenuEvent* event); + + void mouseDoubleClickEvent(QMouseEvent* event); + + private slots: + void expandItemDelayed(const QModelIndex& idx); + void markSelectedItemReadStatus(RootItem::ReadStatus read); + void markAllItemsReadStatus(RootItem::ReadStatus read); + + void saveSortState(int column, Qt::SortOrder order); + void validateItemAfterDragDrop(const QModelIndex& source_index); + void onItemExpandRequested(const QList& items, bool exp); + void onItemExpandStateSaveRequested(RootItem* item); + + private: + // Initializes context menus. + QMenu* initializeContextMenuCategories(RootItem* clicked_item); + QMenu* initializeContextMenuFeeds(RootItem* clicked_item); + QMenu* initializeContextMenuEmptySpace(); + QMenu* initializeContextMenuOtherItem(RootItem* clicked_item); + + // Sets up appearance of this widget. + void setupAppearance(); + + void saveExpandStates(RootItem* item); + + QMenu* m_contextMenuCategories; + QMenu* m_contextMenuFeeds; + QMenu* m_contextMenuEmptySpace; + QMenu* m_contextMenuOtherItems; + + FeedsModel* m_sourceModel; + FeedsProxyModel* m_proxyModel; +}; + +#endif // FEEDSVIEW_H diff --git a/src/gui/guiutilities.cpp b/src/gui/guiutilities.cpp index 55ccc4dae..87cd809d1 100755 --- a/src/gui/guiutilities.cpp +++ b/src/gui/guiutilities.cpp @@ -1,37 +1,38 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/guiutilities.h" - -#include "definitions/definitions.h" - -#include - - -void GuiUtilities::setLabelAsNotice(QLabel *label, bool is_warning) { - label->setMargin(6); - - if (is_warning) { - label->setStyleSheet(QSL("font-weight: bold; font-style: italic; color: red")); - } - else { - label->setStyleSheet(QSL("font-style: italic;")); - } -} - -GuiUtilities::GuiUtilities() { -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/guiutilities.h" + +#include "definitions/definitions.h" + +#include + + +void GuiUtilities::setLabelAsNotice(QLabel* label, bool is_warning) { + label->setMargin(6); + + if (is_warning) { + label->setStyleSheet(QSL("font-weight: bold; font-style: italic; color: red")); + } + + else { + label->setStyleSheet(QSL("font-style: italic;")); + } +} + +GuiUtilities::GuiUtilities() { +} diff --git a/src/gui/guiutilities.h b/src/gui/guiutilities.h index 999765931..4d0a326c2 100755 --- a/src/gui/guiutilities.h +++ b/src/gui/guiutilities.h @@ -1,32 +1,32 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef GUIUTILITIES_H -#define GUIUTILITIES_H - - -class QLabel; - -class GuiUtilities { - public: - static void setLabelAsNotice(QLabel *label, bool is_warning); - - private: - explicit GuiUtilities(); -}; - -#endif // GUIUTILITIES_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef GUIUTILITIES_H +#define GUIUTILITIES_H + + +class QLabel; + +class GuiUtilities { + public: + static void setLabelAsNotice(QLabel* label, bool is_warning); + + private: + explicit GuiUtilities(); +}; + +#endif // GUIUTILITIES_H diff --git a/src/gui/labelwithstatus.cpp b/src/gui/labelwithstatus.cpp index 5d53a3d7d..406bbde59 100755 --- a/src/gui/labelwithstatus.cpp +++ b/src/gui/labelwithstatus.cpp @@ -1,46 +1,44 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/labelwithstatus.h" - -#include "gui/plaintoolbutton.h" - -#include - - -LabelWithStatus::LabelWithStatus(QWidget *parent) - : WidgetWithStatus(parent) { - m_wdgInput = new QLabel(this); - - // Set correct size for the tool button. - int label_height = m_wdgInput->sizeHint().height(); - m_btnStatus->setFixedSize(label_height, label_height); - - // Compose the layout. - m_layout->addWidget(m_wdgInput); - m_layout->addWidget(m_btnStatus); -} - -LabelWithStatus::~LabelWithStatus() { -} - -void LabelWithStatus::setStatus(WidgetWithStatus::StatusType status, - const QString &label_text, - const QString &status_text) { - WidgetWithStatus::setStatus(status, status_text); - label()->setText(label_text); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/labelwithstatus.h" + +#include "gui/plaintoolbutton.h" + +#include + + +LabelWithStatus::LabelWithStatus(QWidget* parent) + : WidgetWithStatus(parent) { + m_wdgInput = new QLabel(this); + // Set correct size for the tool button. + int label_height = m_wdgInput->sizeHint().height(); + m_btnStatus->setFixedSize(label_height, label_height); + // Compose the layout. + m_layout->addWidget(m_wdgInput); + m_layout->addWidget(m_btnStatus); +} + +LabelWithStatus::~LabelWithStatus() { +} + +void LabelWithStatus::setStatus(WidgetWithStatus::StatusType status, + const QString& label_text, + const QString& status_text) { + WidgetWithStatus::setStatus(status, status_text); + label()->setText(label_text); +} diff --git a/src/gui/labelwithstatus.h b/src/gui/labelwithstatus.h index f26509a1d..71a9a2fb5 100755 --- a/src/gui/labelwithstatus.h +++ b/src/gui/labelwithstatus.h @@ -1,42 +1,42 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef LABELWITHSTATUS_H -#define LABELWITHSTATUS_H - -#include "gui/widgetwithstatus.h" - -#include - - -class LabelWithStatus : public WidgetWithStatus { - Q_OBJECT - - public: - // Constructors and destructors. - explicit LabelWithStatus(QWidget *parent = 0); - virtual ~LabelWithStatus(); - - void setStatus(StatusType status, const QString &label_text, const QString &status_text); - - // Access to label. - inline QLabel *label() const { - return static_cast(m_wdgInput); - } -}; - -#endif // LABELWITHSTATUS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef LABELWITHSTATUS_H +#define LABELWITHSTATUS_H + +#include "gui/widgetwithstatus.h" + +#include + + +class LabelWithStatus : public WidgetWithStatus { + Q_OBJECT + + public: + // Constructors and destructors. + explicit LabelWithStatus(QWidget* parent = 0); + virtual ~LabelWithStatus(); + + void setStatus(StatusType status, const QString& label_text, const QString& status_text); + + // Access to label. + inline QLabel* label() const { + return static_cast(m_wdgInput); + } +}; + +#endif // LABELWITHSTATUS_H diff --git a/src/gui/lineeditwithstatus.cpp b/src/gui/lineeditwithstatus.cpp index b916a8a7b..8c16bda34 100755 --- a/src/gui/lineeditwithstatus.cpp +++ b/src/gui/lineeditwithstatus.cpp @@ -1,42 +1,39 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/lineeditwithstatus.h" - -#include "gui/plaintoolbutton.h" -#include "gui/baselineedit.h" - -#include - - -LineEditWithStatus::LineEditWithStatus(QWidget *parent) - : WidgetWithStatus(parent) { - m_wdgInput = new BaseLineEdit(this); - - setFocusProxy(m_wdgInput); - - // Set correct size for the tool button. - const int txt_input_height = m_wdgInput->sizeHint().height(); - m_btnStatus->setFixedSize(txt_input_height, txt_input_height); - - // Compose the layout. - m_layout->addWidget(m_wdgInput); - m_layout->addWidget(m_btnStatus); -} - -LineEditWithStatus::~LineEditWithStatus() { -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/lineeditwithstatus.h" + +#include "gui/plaintoolbutton.h" +#include "gui/baselineedit.h" + +#include + + +LineEditWithStatus::LineEditWithStatus(QWidget* parent) + : WidgetWithStatus(parent) { + m_wdgInput = new BaseLineEdit(this); + setFocusProxy(m_wdgInput); + // Set correct size for the tool button. + const int txt_input_height = m_wdgInput->sizeHint().height(); + m_btnStatus->setFixedSize(txt_input_height, txt_input_height); + // Compose the layout. + m_layout->addWidget(m_wdgInput); + m_layout->addWidget(m_btnStatus); +} + +LineEditWithStatus::~LineEditWithStatus() { +} diff --git a/src/gui/lineeditwithstatus.h b/src/gui/lineeditwithstatus.h index d53797b91..6f4c483f5 100755 --- a/src/gui/lineeditwithstatus.h +++ b/src/gui/lineeditwithstatus.h @@ -1,40 +1,40 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef LINEEDITWITHSTATUS_H -#define LINEEDITWITHSTATUS_H - -#include "gui/widgetwithstatus.h" - -#include "gui/baselineedit.h" - - -class LineEditWithStatus : public WidgetWithStatus { - Q_OBJECT - - public: - // Constructors and destructors. - explicit LineEditWithStatus(QWidget *parent = 0); - virtual ~LineEditWithStatus(); - - // Access to line edit. - inline BaseLineEdit *lineEdit() const { - return static_cast(m_wdgInput); - } -}; - -#endif // LINEEDITWITHSTATUS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef LINEEDITWITHSTATUS_H +#define LINEEDITWITHSTATUS_H + +#include "gui/widgetwithstatus.h" + +#include "gui/baselineedit.h" + + +class LineEditWithStatus : public WidgetWithStatus { + Q_OBJECT + + public: + // Constructors and destructors. + explicit LineEditWithStatus(QWidget* parent = 0); + virtual ~LineEditWithStatus(); + + // Access to line edit. + inline BaseLineEdit* lineEdit() const { + return static_cast(m_wdgInput); + } +}; + +#endif // LINEEDITWITHSTATUS_H diff --git a/src/gui/locationlineedit.cpp b/src/gui/locationlineedit.cpp index 9474109d9..64af9a514 100755 --- a/src/gui/locationlineedit.cpp +++ b/src/gui/locationlineedit.cpp @@ -1,53 +1,52 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/locationlineedit.h" - -#include "network-web/googlesuggest.h" - -#include - - -LocationLineEdit::LocationLineEdit(QWidget *parent) - : BaseLineEdit(parent), m_mouseSelectsAllText(true), m_googleSuggest(new GoogleSuggest(this)) { - setPlaceholderText(tr("Website address goes here")); - connect(this, &LocationLineEdit::submitted, m_googleSuggest, &GoogleSuggest::preventSuggest); -} - -LocationLineEdit::~LocationLineEdit() { -} - -void LocationLineEdit::focusOutEvent(QFocusEvent *event) { - BaseLineEdit::focusOutEvent(event); - - // User now left text box, when he enters it again and clicks, - // then all text should be selected. - m_mouseSelectsAllText = true; -} - -void LocationLineEdit::mousePressEvent(QMouseEvent *event) { - if (m_mouseSelectsAllText) { - event->ignore(); - selectAll(); - - // User clicked and all text was selected. - m_mouseSelectsAllText = false; - } - else { - BaseLineEdit::mousePressEvent(event); - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/locationlineedit.h" + +#include "network-web/googlesuggest.h" + +#include + + +LocationLineEdit::LocationLineEdit(QWidget* parent) + : BaseLineEdit(parent), m_mouseSelectsAllText(true), m_googleSuggest(new GoogleSuggest(this)) { + setPlaceholderText(tr("Website address goes here")); + connect(this, &LocationLineEdit::submitted, m_googleSuggest, &GoogleSuggest::preventSuggest); +} + +LocationLineEdit::~LocationLineEdit() { +} + +void LocationLineEdit::focusOutEvent(QFocusEvent* event) { + BaseLineEdit::focusOutEvent(event); + // User now left text box, when he enters it again and clicks, + // then all text should be selected. + m_mouseSelectsAllText = true; +} + +void LocationLineEdit::mousePressEvent(QMouseEvent* event) { + if (m_mouseSelectsAllText) { + event->ignore(); + selectAll(); + // User clicked and all text was selected. + m_mouseSelectsAllText = false; + } + + else { + BaseLineEdit::mousePressEvent(event); + } +} diff --git a/src/gui/locationlineedit.h b/src/gui/locationlineedit.h index c150aac25..9388feef8 100755 --- a/src/gui/locationlineedit.h +++ b/src/gui/locationlineedit.h @@ -1,44 +1,44 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef LOCATIONLINEEDIT_H -#define LOCATIONLINEEDIT_H - -#include "gui/baselineedit.h" - - -class WebBrowser; -class GoogleSuggest; - -class LocationLineEdit : public BaseLineEdit { - Q_OBJECT - - public: - // Constructors and destructors. - explicit LocationLineEdit(QWidget *parent = 0); - virtual ~LocationLineEdit(); - - protected: - void focusOutEvent(QFocusEvent *event); - void mousePressEvent(QMouseEvent *event); - - private: - bool m_mouseSelectsAllText; - GoogleSuggest *m_googleSuggest; -}; - -#endif // LOCATIONLINEEDIT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef LOCATIONLINEEDIT_H +#define LOCATIONLINEEDIT_H + +#include "gui/baselineedit.h" + + +class WebBrowser; +class GoogleSuggest; + +class LocationLineEdit : public BaseLineEdit { + Q_OBJECT + + public: + // Constructors and destructors. + explicit LocationLineEdit(QWidget* parent = 0); + virtual ~LocationLineEdit(); + + protected: + void focusOutEvent(QFocusEvent* event); + void mousePressEvent(QMouseEvent* event); + + private: + bool m_mouseSelectsAllText; + GoogleSuggest* m_googleSuggest; +}; + +#endif // LOCATIONLINEEDIT_H diff --git a/src/gui/messagebox.cpp b/src/gui/messagebox.cpp index 7f8b1ad4a..cd145bf5b 100755 --- a/src/gui/messagebox.cpp +++ b/src/gui/messagebox.cpp @@ -1,110 +1,107 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/messagebox.h" - -#include "miscellaneous/application.h" -#include "miscellaneous/iconfactory.h" - -#include -#include -#include -#include -#include -#include - - -MessageBox::MessageBox(QWidget *parent) : QMessageBox(parent) { -} - -MessageBox::~MessageBox() { -} - -void MessageBox::setIcon(QMessageBox::Icon icon) { - // Determine correct status icon size. - const int icon_size = qApp->style()->pixelMetric(QStyle::PM_MessageBoxIconSize, 0, this); - - // Setup status icon. - setIconPixmap(iconForStatus(icon).pixmap(icon_size, icon_size)); -} - -void MessageBox::setCheckBox(QMessageBox *msg_box, const QString &text, bool *data) { - // Add "don't show this again checkbox. - QCheckBox *check_box = new QCheckBox(msg_box); - - check_box->setChecked(*data); - check_box->setText(text); - connect(check_box, &QCheckBox::toggled, [=](bool checked) { - *data = checked; - }); - - msg_box->setCheckBox(check_box); -} - -QIcon MessageBox::iconForStatus(QMessageBox::Icon status) { - switch (status) { - case QMessageBox::Information: - return qApp->icons()->fromTheme(QSL("dialog-information")); - - case QMessageBox::Warning: - return qApp->icons()->fromTheme(QSL("dialog-warning")); - - case QMessageBox::Critical: - return qApp->icons()->fromTheme(QSL("dialog-error")); - - case QMessageBox::Question: - return qApp->icons()->fromTheme(QSL("dialog-question")); - - case QMessageBox::NoIcon: - default: - return QIcon(); - } -} - -QMessageBox::StandardButton MessageBox::show(QWidget *parent, - QMessageBox::Icon icon, - const QString &title, - const QString &text, - const QString &informative_text, - const QString &detailed_text, - QMessageBox::StandardButtons buttons, - QMessageBox::StandardButton default_button, - bool *dont_show_again) { - // Create and find needed components. - MessageBox msg_box(parent); - - // Initialize message box properties. - msg_box.setWindowTitle(title); - msg_box.setText(text); - msg_box.setInformativeText(informative_text); - msg_box.setDetailedText(detailed_text); - msg_box.setIcon(icon); - msg_box.setStandardButtons(buttons); - msg_box.setDefaultButton(default_button); - - if (dont_show_again != nullptr) { - MessageBox::setCheckBox(&msg_box, tr("Do not show this dialog again."), dont_show_again); - } - - // Display it. - if (msg_box.exec() == -1) { - return QMessageBox::Cancel; - } - else { - return msg_box.standardButton(msg_box.clickedButton()); - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/messagebox.h" + +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" + +#include +#include +#include +#include +#include +#include + + +MessageBox::MessageBox(QWidget* parent) : QMessageBox(parent) { +} + +MessageBox::~MessageBox() { +} + +void MessageBox::setIcon(QMessageBox::Icon icon) { + // Determine correct status icon size. + const int icon_size = qApp->style()->pixelMetric(QStyle::PM_MessageBoxIconSize, 0, this); + // Setup status icon. + setIconPixmap(iconForStatus(icon).pixmap(icon_size, icon_size)); +} + +void MessageBox::setCheckBox(QMessageBox* msg_box, const QString& text, bool* data) { + // Add "don't show this again checkbox. + QCheckBox* check_box = new QCheckBox(msg_box); + check_box->setChecked(*data); + check_box->setText(text); + connect(check_box, &QCheckBox::toggled, [ = ](bool checked) { + *data = checked; + }); + msg_box->setCheckBox(check_box); +} + +QIcon MessageBox::iconForStatus(QMessageBox::Icon status) { + switch (status) { + case QMessageBox::Information: + return qApp->icons()->fromTheme(QSL("dialog-information")); + + case QMessageBox::Warning: + return qApp->icons()->fromTheme(QSL("dialog-warning")); + + case QMessageBox::Critical: + return qApp->icons()->fromTheme(QSL("dialog-error")); + + case QMessageBox::Question: + return qApp->icons()->fromTheme(QSL("dialog-question")); + + case QMessageBox::NoIcon: + default: + return QIcon(); + } +} + +QMessageBox::StandardButton MessageBox::show(QWidget* parent, + QMessageBox::Icon icon, + const QString& title, + const QString& text, + const QString& informative_text, + const QString& detailed_text, + QMessageBox::StandardButtons buttons, + QMessageBox::StandardButton default_button, + bool* dont_show_again) { + // Create and find needed components. + MessageBox msg_box(parent); + // Initialize message box properties. + msg_box.setWindowTitle(title); + msg_box.setText(text); + msg_box.setInformativeText(informative_text); + msg_box.setDetailedText(detailed_text); + msg_box.setIcon(icon); + msg_box.setStandardButtons(buttons); + msg_box.setDefaultButton(default_button); + + if (dont_show_again != nullptr) { + MessageBox::setCheckBox(&msg_box, tr("Do not show this dialog again."), dont_show_again); + } + + // Display it. + if (msg_box.exec() == -1) { + return QMessageBox::Cancel; + } + + else { + return msg_box.standardButton(msg_box.clickedButton()); + } +} diff --git a/src/gui/messagebox.h b/src/gui/messagebox.h index 338f47033..66e4d13a6 100755 --- a/src/gui/messagebox.h +++ b/src/gui/messagebox.h @@ -1,52 +1,52 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef MESSAGEBOX_H -#define MESSAGEBOX_H - -#include -#include - - -class MessageBox : public QMessageBox { - Q_OBJECT - - public: - // Constructors and destructors. - explicit MessageBox(QWidget *parent = 0); - virtual ~MessageBox(); - - // Custom icon setting. - void setIcon(Icon icon); - - static void setCheckBox(QMessageBox *msg_box, const QString &text, bool *data); - - // Displays custom message box. - static QMessageBox::StandardButton show(QWidget *parent, - QMessageBox::Icon icon, - const QString &title, - const QString &text, - const QString &informative_text = QString(), - const QString &detailed_text = QString(), - QMessageBox::StandardButtons buttons = QMessageBox::Ok, - QMessageBox::StandardButton default_button = QMessageBox::Ok, - bool *dont_show_again = nullptr); - - static QIcon iconForStatus(QMessageBox::Icon status); -}; - -#endif // MESSAGEBOX_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef MESSAGEBOX_H +#define MESSAGEBOX_H + +#include +#include + + +class MessageBox : public QMessageBox { + Q_OBJECT + + public: + // Constructors and destructors. + explicit MessageBox(QWidget* parent = 0); + virtual ~MessageBox(); + + // Custom icon setting. + void setIcon(Icon icon); + + static void setCheckBox(QMessageBox* msg_box, const QString& text, bool* data); + + // Displays custom message box. + static QMessageBox::StandardButton show(QWidget* parent, + QMessageBox::Icon icon, + const QString& title, + const QString& text, + const QString& informative_text = QString(), + const QString& detailed_text = QString(), + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton default_button = QMessageBox::Ok, + bool* dont_show_again = nullptr); + + static QIcon iconForStatus(QMessageBox::Icon status); +}; + +#endif // MESSAGEBOX_H diff --git a/src/gui/messagepreviewer.cpp b/src/gui/messagepreviewer.cpp index 739585b66..93608c826 100755 --- a/src/gui/messagepreviewer.cpp +++ b/src/gui/messagepreviewer.cpp @@ -1,234 +1,219 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/messagepreviewer.h" - -#include "miscellaneous/application.h" -#include "network-web/webfactory.h" -#include "miscellaneous/databasequeries.h" -#include "gui/messagebox.h" -#include "gui/dialogs/formmain.h" -#include "services/abstract/serviceroot.h" - -#include -#include -#include - - -void MessagePreviewer::createConnections() { - connect(m_ui->m_txtMessage, &QTextBrowser::anchorClicked, [=](const QUrl &url) { - if (!url.isEmpty()) { - bool open_externally_now = qApp->settings()->value(GROUP(Browser), - SETTING(Browser::OpenLinksInExternalBrowserRightAway)).toBool(); - - if (open_externally_now) { - WebFactory::instance()->openUrlInExternalBrowser(url.toString()); - } - else { - // User clicked some URL. Open it in external browser or download? - MessageBox box(qApp->mainForm()); - - 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::ActionRole); - QAbstractButton *btn_download = box.addButton(tr("Download"), QMessageBox::ActionRole); - QAbstractButton *btn_cancel = box.addButton(QMessageBox::Cancel); - - bool always; - MessageBox::setCheckBox(&box, tr("Always open links in external browser."), &always); - - box.setDefaultButton(QMessageBox::Cancel); - box.exec(); - - if (box.clickedButton() != box.button(QMessageBox::Cancel)) { - // Store selected checkbox value. - qApp->settings()->setValue(GROUP(Browser), Browser::OpenLinksInExternalBrowserRightAway, always); - } - - if (box.clickedButton() == btn_open) { - WebFactory::instance()->openUrlInExternalBrowser(url.toString()); - } - else if (box.clickedButton() == btn_download) { - qApp->downloadManager()->download(url); - } - - btn_download->deleteLater(); - btn_open->deleteLater(); - btn_cancel->deleteLater(); - } - } - else { - MessageBox::show(qApp->mainForm(), QMessageBox::Warning, tr("Incorrect link"), - tr("Selected hyperlink is invalid.")); - } - }); - connect(m_actionMarkRead = m_toolBar->addAction(qApp->icons()->fromTheme("mail-mark-read"), tr("Mark message as read")), - &QAction::triggered, - this, - &MessagePreviewer::markMessageAsRead); - connect(m_actionMarkUnread = m_toolBar->addAction(qApp->icons()->fromTheme("mail-mark-unread"), tr("Mark message as unread")), - &QAction::triggered, - this, - &MessagePreviewer::markMessageAsUnread); - connect(m_actionSwitchImportance = m_toolBar->addAction(qApp->icons()->fromTheme("mail-mark-important"), tr("Switch message importance")), - &QAction::triggered, - this, - &MessagePreviewer::switchMessageImportance); - connect(m_ui->m_txtMessage, - static_cast(&QTextBrowser::highlighted), - [=](const QString &text) { - Q_UNUSED(text) - - QToolTip::showText(QCursor::pos(), tr("Click this link to download it or open it with external browser."), this); - }); -} - -MessagePreviewer::MessagePreviewer(QWidget *parent) : QWidget(parent), - m_ui(new Ui::MessagePreviewer), m_pictures(QStringList()) { - m_ui->setupUi(this); - m_ui->m_txtMessage->viewport()->setAutoFillBackground(true); - m_toolBar = new QToolBar(this); - m_toolBar->setOrientation(Qt::Vertical); - m_ui->m_layout->addWidget(m_toolBar, 0, 0, -1, 1); - - createConnections(); - - m_actionSwitchImportance->setCheckable(true); - - reloadFontSettings(); - clear(); -} - -MessagePreviewer::~MessagePreviewer() { -} - -void MessagePreviewer::reloadFontSettings() { - const Settings *settings = qApp->settings(); - QFont fon; - - fon.fromString(settings->value(GROUP(Messages), - SETTING(Messages::PreviewerFontStandard)).toString()); - - m_ui->m_txtMessage->setFont(fon); -} - -void MessagePreviewer::clear() { - m_ui->m_txtMessage->clear(); - m_pictures.clear(); - hide(); -} - -void MessagePreviewer::hideToolbar() { - m_toolBar->setVisible(false); -} - -void MessagePreviewer::loadMessage(const Message &message, RootItem *root) { - m_message = message; - m_root = root; - - if (!m_root.isNull()) { - m_actionSwitchImportance->setChecked(m_message.m_isImportant); - m_ui->m_txtMessage->setHtml(prepareHtmlForMessage(m_message)); - - updateButtons(); - show(); - - m_ui->m_txtMessage->verticalScrollBar()->triggerAction(QScrollBar::SliderToMinimum); - } -} - -void MessagePreviewer::markMessageAsRead() { - markMessageAsReadUnread(RootItem::Read); -} - -void MessagePreviewer::markMessageAsUnread() { - markMessageAsReadUnread(RootItem::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()->connection(objectName(), DatabaseFactory::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::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::NotImportant : - RootItem::Important))) { - DatabaseQueries::switchMessagesImportance(qApp->database()->connection(objectName(), DatabaseFactory::FromSettings), - QStringList() << QString::number(m_message.m_id)); - - m_root->getParentServiceRoot()->onAfterSwitchMessageImportance(m_root.data(), - QList() << ImportanceChange(m_message, - m_message.m_isImportant ? - RootItem::NotImportant : - RootItem::Important)); - - emit markMessageImportant(m_message.m_id, checked ? RootItem::Important : RootItem::NotImportant); - m_message.m_isImportant = checked; - } - } -} - -void MessagePreviewer::updateButtons() { - m_actionMarkRead->setEnabled(!m_message.m_isRead); - m_actionMarkUnread->setEnabled(m_message.m_isRead); -} - -QString MessagePreviewer::prepareHtmlForMessage(const Message &message) { - QString html = QString("

%1

").arg(message.m_title); - - html += QString("[url] %1
").arg(message.m_url); - - foreach (const Enclosure &enc, message.m_enclosures) { - html += QString("[%2] %1
").arg(enc.m_url, enc.m_mimeType); - } - - int offset = 0; - QRegExp imgTagRegex("\\]*src\\s*=\\s*\"([^\"]*)\"[^\\>]*\\>", Qt::CaseInsensitive); - - imgTagRegex.setMinimal(true); - - while( (offset = imgTagRegex.indexIn(message.m_contents, offset)) != -1){ - m_pictures.append(imgTagRegex.cap(1)); - - offset += imgTagRegex.matchedLength(); - html += QString("[%2] %1
").arg(imgTagRegex.cap(1), tr("image")); - } - - html += "
"; - html += message.m_contents; - - return html; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/messagepreviewer.h" + +#include "miscellaneous/application.h" +#include "network-web/webfactory.h" +#include "miscellaneous/databasequeries.h" +#include "gui/messagebox.h" +#include "gui/dialogs/formmain.h" +#include "services/abstract/serviceroot.h" + +#include +#include +#include + + +void MessagePreviewer::createConnections() { + connect(m_ui->m_txtMessage, &QTextBrowser::anchorClicked, [ = ](const QUrl & url) { + if (!url.isEmpty()) { + bool open_externally_now = qApp->settings()->value(GROUP(Browser), + SETTING(Browser::OpenLinksInExternalBrowserRightAway)).toBool(); + + if (open_externally_now) { + WebFactory::instance()->openUrlInExternalBrowser(url.toString()); + } + + else { + // User clicked some URL. Open it in external browser or download? + MessageBox box(qApp->mainForm()); + 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::ActionRole); + QAbstractButton* btn_download = box.addButton(tr("Download"), QMessageBox::ActionRole); + QAbstractButton* btn_cancel = box.addButton(QMessageBox::Cancel); + bool always; + MessageBox::setCheckBox(&box, tr("Always open links in external browser."), &always); + box.setDefaultButton(QMessageBox::Cancel); + box.exec(); + + if (box.clickedButton() != box.button(QMessageBox::Cancel)) { + // Store selected checkbox value. + qApp->settings()->setValue(GROUP(Browser), Browser::OpenLinksInExternalBrowserRightAway, always); + } + + if (box.clickedButton() == btn_open) { + WebFactory::instance()->openUrlInExternalBrowser(url.toString()); + } + + else if (box.clickedButton() == btn_download) { + qApp->downloadManager()->download(url); + } + + btn_download->deleteLater(); + btn_open->deleteLater(); + btn_cancel->deleteLater(); + } + } + + else { + MessageBox::show(qApp->mainForm(), QMessageBox::Warning, tr("Incorrect link"), + tr("Selected hyperlink is invalid.")); + } + }); + connect(m_actionMarkRead = m_toolBar->addAction(qApp->icons()->fromTheme("mail-mark-read"), tr("Mark message as read")), + &QAction::triggered, + this, + &MessagePreviewer::markMessageAsRead); + connect(m_actionMarkUnread = m_toolBar->addAction(qApp->icons()->fromTheme("mail-mark-unread"), tr("Mark message as unread")), + &QAction::triggered, + this, + &MessagePreviewer::markMessageAsUnread); + connect(m_actionSwitchImportance = m_toolBar->addAction(qApp->icons()->fromTheme("mail-mark-important"), tr("Switch message importance")), + &QAction::triggered, + this, + &MessagePreviewer::switchMessageImportance); + connect(m_ui->m_txtMessage, + static_cast(&QTextBrowser::highlighted), + [ = ](const QString & text) { + Q_UNUSED(text) + QToolTip::showText(QCursor::pos(), tr("Click this link to download it or open it with external browser."), this); + }); +} + +MessagePreviewer::MessagePreviewer(QWidget* parent) : QWidget(parent), + m_ui(new Ui::MessagePreviewer), m_pictures(QStringList()) { + m_ui->setupUi(this); + m_ui->m_txtMessage->viewport()->setAutoFillBackground(true); + m_toolBar = new QToolBar(this); + m_toolBar->setOrientation(Qt::Vertical); + m_ui->m_layout->addWidget(m_toolBar, 0, 0, -1, 1); + createConnections(); + m_actionSwitchImportance->setCheckable(true); + reloadFontSettings(); + clear(); +} + +MessagePreviewer::~MessagePreviewer() { +} + +void MessagePreviewer::reloadFontSettings() { + const Settings* settings = qApp->settings(); + QFont fon; + fon.fromString(settings->value(GROUP(Messages), + SETTING(Messages::PreviewerFontStandard)).toString()); + m_ui->m_txtMessage->setFont(fon); +} + +void MessagePreviewer::clear() { + m_ui->m_txtMessage->clear(); + m_pictures.clear(); + hide(); +} + +void MessagePreviewer::hideToolbar() { + m_toolBar->setVisible(false); +} + +void MessagePreviewer::loadMessage(const Message& message, RootItem* root) { + m_message = message; + m_root = root; + + if (!m_root.isNull()) { + m_actionSwitchImportance->setChecked(m_message.m_isImportant); + m_ui->m_txtMessage->setHtml(prepareHtmlForMessage(m_message)); + updateButtons(); + show(); + m_ui->m_txtMessage->verticalScrollBar()->triggerAction(QScrollBar::SliderToMinimum); + } +} + +void MessagePreviewer::markMessageAsRead() { + markMessageAsReadUnread(RootItem::Read); +} + +void MessagePreviewer::markMessageAsUnread() { + markMessageAsReadUnread(RootItem::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()->connection(objectName(), DatabaseFactory::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::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::NotImportant : + RootItem::Important))) { + DatabaseQueries::switchMessagesImportance(qApp->database()->connection(objectName(), DatabaseFactory::FromSettings), + QStringList() << QString::number(m_message.m_id)); + m_root->getParentServiceRoot()->onAfterSwitchMessageImportance(m_root.data(), + QList() << ImportanceChange(m_message, + m_message.m_isImportant ? + RootItem::NotImportant : + RootItem::Important)); + emit markMessageImportant(m_message.m_id, checked ? RootItem::Important : RootItem::NotImportant); + m_message.m_isImportant = checked; + } + } +} + +void MessagePreviewer::updateButtons() { + m_actionMarkRead->setEnabled(!m_message.m_isRead); + m_actionMarkUnread->setEnabled(m_message.m_isRead); +} + +QString MessagePreviewer::prepareHtmlForMessage(const Message& message) { + QString html = QString("

%1

").arg(message.m_title); + html += QString("[url] %1
").arg(message.m_url); + + foreach (const Enclosure& enc, message.m_enclosures) { + html += QString("[%2] %1
").arg(enc.m_url, enc.m_mimeType); + } + + int offset = 0; + QRegExp imgTagRegex("\\]*src\\s*=\\s*\"([^\"]*)\"[^\\>]*\\>", Qt::CaseInsensitive); + imgTagRegex.setMinimal(true); + + while ((offset = imgTagRegex.indexIn(message.m_contents, offset)) != -1) { + m_pictures.append(imgTagRegex.cap(1)); + offset += imgTagRegex.matchedLength(); + html += QString("[%2] %1
").arg(imgTagRegex.cap(1), tr("image")); + } + + html += "
"; + html += message.m_contents; + return html; +} diff --git a/src/gui/messagepreviewer.h b/src/gui/messagepreviewer.h index 037de2742..77a2b9819 100755 --- a/src/gui/messagepreviewer.h +++ b/src/gui/messagepreviewer.h @@ -1,78 +1,78 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef MESSAGEPREVIEWER_H -#define MESSAGEPREVIEWER_H - -#include - -#include "ui_messagepreviewer.h" - -#include "core/message.h" -#include "services/abstract/rootitem.h" - -#include - - -namespace Ui { - class MessagePreviewer; -} - -class QToolBar; - -class MessagePreviewer : public QWidget { - Q_OBJECT - - public: - explicit MessagePreviewer(QWidget *parent = 0); - virtual ~MessagePreviewer(); - - void reloadFontSettings(); - - public slots: - void clear(); - void hideToolbar(); - void loadMessage(const Message &message, RootItem *root); - - private slots: - void markMessageAsRead(); - void markMessageAsUnread(); - void markMessageAsReadUnread(RootItem::ReadStatus read); - void switchMessageImportance(bool checked); - - signals: - void markMessageRead(int id, RootItem::ReadStatus read); - void markMessageImportant(int id, RootItem::Importance important); - void requestMessageListReload(bool mark_current_as_read); - - private: - void createConnections(); - void updateButtons(); - QString prepareHtmlForMessage(const Message &message); - - QToolBar *m_toolBar; - QScopedPointer m_ui; - Message m_message; - QStringList m_pictures; - QPointer m_root; - - QAction *m_actionMarkRead; - QAction *m_actionMarkUnread; - QAction *m_actionSwitchImportance; -}; - -#endif // MESSAGEPREVIEWER_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef MESSAGEPREVIEWER_H +#define MESSAGEPREVIEWER_H + +#include + +#include "ui_messagepreviewer.h" + +#include "core/message.h" +#include "services/abstract/rootitem.h" + +#include + + +namespace Ui { + class MessagePreviewer; +} + +class QToolBar; + +class MessagePreviewer : public QWidget { + Q_OBJECT + + public: + explicit MessagePreviewer(QWidget* parent = 0); + virtual ~MessagePreviewer(); + + void reloadFontSettings(); + + public slots: + void clear(); + void hideToolbar(); + void loadMessage(const Message& message, RootItem* root); + + private slots: + void markMessageAsRead(); + void markMessageAsUnread(); + void markMessageAsReadUnread(RootItem::ReadStatus read); + void switchMessageImportance(bool checked); + + signals: + void markMessageRead(int id, RootItem::ReadStatus read); + void markMessageImportant(int id, RootItem::Importance important); + void requestMessageListReload(bool mark_current_as_read); + + private: + void createConnections(); + void updateButtons(); + QString prepareHtmlForMessage(const Message& message); + + QToolBar* m_toolBar; + QScopedPointer m_ui; + Message m_message; + QStringList m_pictures; + QPointer m_root; + + QAction* m_actionMarkRead; + QAction* m_actionMarkUnread; + QAction* m_actionSwitchImportance; +}; + +#endif // MESSAGEPREVIEWER_H diff --git a/src/gui/messagessearchlineedit.cpp b/src/gui/messagessearchlineedit.cpp index 0a4178440..8c86a482b 100755 --- a/src/gui/messagessearchlineedit.cpp +++ b/src/gui/messagessearchlineedit.cpp @@ -1,25 +1,25 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/messagessearchlineedit.h" - - -MessagesSearchLineEdit::MessagesSearchLineEdit(QWidget *parent) : BaseLineEdit(parent) { -} - -MessagesSearchLineEdit::~MessagesSearchLineEdit() { -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/messagessearchlineedit.h" + + +MessagesSearchLineEdit::MessagesSearchLineEdit(QWidget* parent) : BaseLineEdit(parent) { +} + +MessagesSearchLineEdit::~MessagesSearchLineEdit() { +} diff --git a/src/gui/messagessearchlineedit.h b/src/gui/messagessearchlineedit.h index 6868fc343..0b1bf2a55 100755 --- a/src/gui/messagessearchlineedit.h +++ b/src/gui/messagessearchlineedit.h @@ -1,35 +1,35 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef MESSAGESEARCHLINEEDIT_H -#define MESSAGESEARCHLINEEDIT_H - -#include "gui/baselineedit.h" - - -class PlainToolButton; - -class MessagesSearchLineEdit : public BaseLineEdit { - Q_OBJECT - - public: - // Constructors and destructors. - explicit MessagesSearchLineEdit(QWidget *parent = 0); - virtual ~MessagesSearchLineEdit(); -}; - -#endif // MESSAGESEARCHLINEEDIT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef MESSAGESEARCHLINEEDIT_H +#define MESSAGESEARCHLINEEDIT_H + +#include "gui/baselineedit.h" + + +class PlainToolButton; + +class MessagesSearchLineEdit : public BaseLineEdit { + Q_OBJECT + + public: + // Constructors and destructors. + explicit MessagesSearchLineEdit(QWidget* parent = 0); + virtual ~MessagesSearchLineEdit(); +}; + +#endif // MESSAGESEARCHLINEEDIT_H diff --git a/src/gui/messagestoolbar.cpp b/src/gui/messagestoolbar.cpp index 6d93d140c..cb7cad76d 100755 --- a/src/gui/messagestoolbar.cpp +++ b/src/gui/messagestoolbar.cpp @@ -1,173 +1,165 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/messagestoolbar.h" - -#include "definitions/definitions.h" -#include "gui/baselineedit.h" -#include "gui/messagessearchlineedit.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/settings.h" - -#include -#include -#include - - -MessagesToolBar::MessagesToolBar(const QString &title, QWidget *parent) - : BaseToolBar(title, parent) { - initializeSearchBox(); - initializeHighlighter(); -} - -MessagesToolBar::~MessagesToolBar() { -} - -QList MessagesToolBar::availableActions() const { - QList available_actions = qApp->userActions(); - - available_actions.append(m_actionSearchMessages); - available_actions.append(m_actionMessageHighlighter); - - return available_actions; -} - -QList MessagesToolBar::changeableActions() const { - return actions(); -} - -void MessagesToolBar::saveChangeableActions(const QStringList& actions) { - qApp->settings()->setValue(GROUP(GUI), GUI::MessagesToolbarDefaultButtons, actions.join(QSL(","))); - loadSpecificActions(getSpecificActions(actions)); - - // If user hidden search messages box, then remove the filter. - if (!changeableActions().contains(m_actionSearchMessages)) { - m_txtSearchMessages->clear(); - } -} - -QList MessagesToolBar::getSpecificActions(const QStringList &actions) { - QList available_actions = availableActions(); - QList spec_actions; - - // Iterate action names and add respectable actions into the toolbar. - foreach (const QString &action_name, actions) { - QAction *matching_action = findMatchingAction(action_name, available_actions); - - if (matching_action != nullptr) { - // Add existing standard action. - spec_actions.append(matching_action); - } - else if (action_name == SEPARATOR_ACTION_NAME) { - // Add new separator. - QAction *act = new QAction(this); - act->setSeparator(true); - - spec_actions.append(act); - } - else if (action_name == SEACRH_MESSAGES_ACTION_NAME) { - // Add search box. - spec_actions.append(m_actionSearchMessages); - } - else if (action_name == HIGHLIGHTER_ACTION_NAME) { - // Add filter button. - spec_actions.append(m_actionMessageHighlighter); - } - else if (action_name == SPACER_ACTION_NAME) { - // Add new spacer. - QWidget *spacer = new QWidget(this); - spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - QWidgetAction *action = new QWidgetAction(this); - - action->setDefaultWidget(spacer); - action->setIcon(qApp->icons()->fromTheme(QSL("go-jump"))); - action->setProperty("type", SPACER_ACTION_NAME); - action->setProperty("name", tr("Toolbar spacer")); - - spec_actions.append(action); - } - } - - return spec_actions; -} - -void MessagesToolBar::loadSpecificActions(const QList &actions) { - clear(); - - foreach (QAction *act, actions) { - addAction(act); - } -} - -void MessagesToolBar::handleMessageHighlighterChange(QAction *action) { - m_btnMessageHighlighter->setIcon(action->icon()); - m_btnMessageHighlighter->setToolTip(action->text()); - - emit messageFilterChanged(action->data().value()); -} - -void MessagesToolBar::initializeSearchBox() { - m_txtSearchMessages = new MessagesSearchLineEdit(this); - m_txtSearchMessages->setFixedWidth(FILTER_WIDTH); - m_txtSearchMessages->setPlaceholderText(tr("Search messages")); - - // Setup wrapping action for search box. - m_actionSearchMessages = new QWidgetAction(this); - m_actionSearchMessages->setDefaultWidget(m_txtSearchMessages); - m_actionSearchMessages->setIcon(qApp->icons()->fromTheme(QSL("system-search"))); - m_actionSearchMessages->setProperty("type", SEACRH_MESSAGES_ACTION_NAME); - m_actionSearchMessages->setProperty("name", tr("Message search box")); - - connect(m_txtSearchMessages, &MessagesSearchLineEdit::textChanged, this, &MessagesToolBar::messageSearchPatternChanged); -} - -void MessagesToolBar::initializeHighlighter() { - m_menuMessageHighlighter = new QMenu(tr("Menu for highlighting messages"), this); - m_menuMessageHighlighter->addAction(qApp->icons()->fromTheme(QSL("mail-mark-read")), - tr("No extra highlighting"))->setData(QVariant::fromValue(MessagesModel::NoHighlighting)); - m_menuMessageHighlighter->addAction(qApp->icons()->fromTheme(QSL("mail-mark-unread")), - tr("Highlight unread messages"))->setData(QVariant::fromValue(MessagesModel::HighlightUnread)); - m_menuMessageHighlighter->addAction(qApp->icons()->fromTheme(QSL("mail-mark-important")), - tr("Highlight important messages"))->setData(QVariant::fromValue(MessagesModel::HighlightImportant)); - - m_btnMessageHighlighter = new QToolButton(this); - m_btnMessageHighlighter->setToolTip(tr("Display all messages")); - m_btnMessageHighlighter->setMenu(m_menuMessageHighlighter); - m_btnMessageHighlighter->setPopupMode(QToolButton::MenuButtonPopup); - m_btnMessageHighlighter->setIcon(qApp->icons()->fromTheme(QSL("mail-mark-read"))); - - m_actionMessageHighlighter = new QWidgetAction(this); - m_actionMessageHighlighter->setDefaultWidget(m_btnMessageHighlighter); - m_actionMessageHighlighter->setIcon(m_btnMessageHighlighter->icon()); - m_actionMessageHighlighter->setProperty("type", HIGHLIGHTER_ACTION_NAME); - m_actionMessageHighlighter->setProperty("name", tr("Message highlighter")); - - connect(m_menuMessageHighlighter, SIGNAL(triggered(QAction*)), - this, SLOT(handleMessageHighlighterChange(QAction*))); -} - -QStringList MessagesToolBar::defaultActions() const { - return QString(GUI::MessagesToolbarDefaultButtonsDef).split(',', - QString::SkipEmptyParts); -} - -QStringList MessagesToolBar::savedActions() const { - return qApp->settings()->value(GROUP(GUI), - SETTING(GUI::MessagesToolbarDefaultButtons)).toString().split(',', - QString::SkipEmptyParts); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/messagestoolbar.h" + +#include "definitions/definitions.h" +#include "gui/baselineedit.h" +#include "gui/messagessearchlineedit.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/settings.h" + +#include +#include +#include + + +MessagesToolBar::MessagesToolBar(const QString& title, QWidget* parent) + : BaseToolBar(title, parent) { + initializeSearchBox(); + initializeHighlighter(); +} + +MessagesToolBar::~MessagesToolBar() { +} + +QList MessagesToolBar::availableActions() const { + QList available_actions = qApp->userActions(); + available_actions.append(m_actionSearchMessages); + available_actions.append(m_actionMessageHighlighter); + return available_actions; +} + +QList MessagesToolBar::changeableActions() const { + return actions(); +} + +void MessagesToolBar::saveChangeableActions(const QStringList& actions) { + qApp->settings()->setValue(GROUP(GUI), GUI::MessagesToolbarDefaultButtons, actions.join(QSL(","))); + loadSpecificActions(getSpecificActions(actions)); + + // If user hidden search messages box, then remove the filter. + if (!changeableActions().contains(m_actionSearchMessages)) { + m_txtSearchMessages->clear(); + } +} + +QList MessagesToolBar::getSpecificActions(const QStringList& actions) { + QList available_actions = availableActions(); + QList spec_actions; + + // Iterate action names and add respectable actions into the toolbar. + foreach (const QString& action_name, actions) { + QAction* matching_action = findMatchingAction(action_name, available_actions); + + if (matching_action != nullptr) { + // Add existing standard action. + spec_actions.append(matching_action); + } + + else if (action_name == SEPARATOR_ACTION_NAME) { + // Add new separator. + QAction* act = new QAction(this); + act->setSeparator(true); + spec_actions.append(act); + } + + else if (action_name == SEACRH_MESSAGES_ACTION_NAME) { + // Add search box. + spec_actions.append(m_actionSearchMessages); + } + + else if (action_name == HIGHLIGHTER_ACTION_NAME) { + // Add filter button. + spec_actions.append(m_actionMessageHighlighter); + } + + else if (action_name == SPACER_ACTION_NAME) { + // Add new spacer. + QWidget* spacer = new QWidget(this); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QWidgetAction* action = new QWidgetAction(this); + action->setDefaultWidget(spacer); + action->setIcon(qApp->icons()->fromTheme(QSL("go-jump"))); + action->setProperty("type", SPACER_ACTION_NAME); + action->setProperty("name", tr("Toolbar spacer")); + spec_actions.append(action); + } + } + + return spec_actions; +} + +void MessagesToolBar::loadSpecificActions(const QList& actions) { + clear(); + + foreach (QAction* act, actions) { + addAction(act); + } +} + +void MessagesToolBar::handleMessageHighlighterChange(QAction* action) { + m_btnMessageHighlighter->setIcon(action->icon()); + m_btnMessageHighlighter->setToolTip(action->text()); + emit messageFilterChanged(action->data().value()); +} + +void MessagesToolBar::initializeSearchBox() { + m_txtSearchMessages = new MessagesSearchLineEdit(this); + m_txtSearchMessages->setFixedWidth(FILTER_WIDTH); + m_txtSearchMessages->setPlaceholderText(tr("Search messages")); + // Setup wrapping action for search box. + m_actionSearchMessages = new QWidgetAction(this); + m_actionSearchMessages->setDefaultWidget(m_txtSearchMessages); + m_actionSearchMessages->setIcon(qApp->icons()->fromTheme(QSL("system-search"))); + m_actionSearchMessages->setProperty("type", SEACRH_MESSAGES_ACTION_NAME); + m_actionSearchMessages->setProperty("name", tr("Message search box")); + connect(m_txtSearchMessages, &MessagesSearchLineEdit::textChanged, this, &MessagesToolBar::messageSearchPatternChanged); +} + +void MessagesToolBar::initializeHighlighter() { + m_menuMessageHighlighter = new QMenu(tr("Menu for highlighting messages"), this); + m_menuMessageHighlighter->addAction(qApp->icons()->fromTheme(QSL("mail-mark-read")), + tr("No extra highlighting"))->setData(QVariant::fromValue(MessagesModel::NoHighlighting)); + m_menuMessageHighlighter->addAction(qApp->icons()->fromTheme(QSL("mail-mark-unread")), + tr("Highlight unread messages"))->setData(QVariant::fromValue(MessagesModel::HighlightUnread)); + m_menuMessageHighlighter->addAction(qApp->icons()->fromTheme(QSL("mail-mark-important")), + tr("Highlight important messages"))->setData(QVariant::fromValue(MessagesModel::HighlightImportant)); + m_btnMessageHighlighter = new QToolButton(this); + m_btnMessageHighlighter->setToolTip(tr("Display all messages")); + m_btnMessageHighlighter->setMenu(m_menuMessageHighlighter); + m_btnMessageHighlighter->setPopupMode(QToolButton::MenuButtonPopup); + m_btnMessageHighlighter->setIcon(qApp->icons()->fromTheme(QSL("mail-mark-read"))); + m_actionMessageHighlighter = new QWidgetAction(this); + m_actionMessageHighlighter->setDefaultWidget(m_btnMessageHighlighter); + m_actionMessageHighlighter->setIcon(m_btnMessageHighlighter->icon()); + m_actionMessageHighlighter->setProperty("type", HIGHLIGHTER_ACTION_NAME); + m_actionMessageHighlighter->setProperty("name", tr("Message highlighter")); + connect(m_menuMessageHighlighter, SIGNAL(triggered(QAction*)), + this, SLOT(handleMessageHighlighterChange(QAction*))); +} + +QStringList MessagesToolBar::defaultActions() const { + return QString(GUI::MessagesToolbarDefaultButtonsDef).split(',', + QString::SkipEmptyParts); +} + +QStringList MessagesToolBar::savedActions() const { + return qApp->settings()->value(GROUP(GUI), + SETTING(GUI::MessagesToolbarDefaultButtons)).toString().split(',', + QString::SkipEmptyParts); +} diff --git a/src/gui/messagestoolbar.h b/src/gui/messagestoolbar.h index 8ce717ccd..12c2d2b38 100755 --- a/src/gui/messagestoolbar.h +++ b/src/gui/messagestoolbar.h @@ -1,82 +1,82 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef NEWSTOOLBAR_H -#define NEWSTOOLBAR_H - -#include "gui/basetoolbar.h" - -#include "core/messagesmodel.h" - - -class MessagesSearchLineEdit; -class QWidgetAction; -class QToolButton; -class QMenu; - -class MessagesToolBar : public BaseToolBar { - Q_OBJECT - - public: - // Constructors and destructors. - explicit MessagesToolBar(const QString &title, QWidget *parent = 0); - virtual ~MessagesToolBar(); - - // External access to search line edit. - inline MessagesSearchLineEdit *searchLineEdit() { - return m_txtSearchMessages; - } - - // Implementation of BaseToolBar interface. - QList availableActions() const; - QList changeableActions() const; - void saveChangeableActions(const QStringList &actions); - - // Loads actions as specified by external actions list. - // NOTE: This is used primarily for reloading actions - // when they are changed from settings. - void loadSpecificActions(const QList &actions); - - QList getSpecificActions(const QStringList &actions); - - QStringList defaultActions() const; - QStringList savedActions() const; - - signals: - void messageSearchPatternChanged(const QString &pattern); - - // Emitted if message filter is changed. - void messageFilterChanged(MessagesModel::MessageHighlighter filter); - - private slots: - // Called when highlighter gets changed. - void handleMessageHighlighterChange(QAction *action); - - private: - void initializeSearchBox(); - void initializeHighlighter(); - - private: - QWidgetAction *m_actionMessageHighlighter; - QToolButton *m_btnMessageHighlighter; - QMenu *m_menuMessageHighlighter; - - QWidgetAction *m_actionSearchMessages; - MessagesSearchLineEdit *m_txtSearchMessages; -}; - -#endif // NEWSTOOLBAR_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef NEWSTOOLBAR_H +#define NEWSTOOLBAR_H + +#include "gui/basetoolbar.h" + +#include "core/messagesmodel.h" + + +class MessagesSearchLineEdit; +class QWidgetAction; +class QToolButton; +class QMenu; + +class MessagesToolBar : public BaseToolBar { + Q_OBJECT + + public: + // Constructors and destructors. + explicit MessagesToolBar(const QString& title, QWidget* parent = 0); + virtual ~MessagesToolBar(); + + // External access to search line edit. + inline MessagesSearchLineEdit* searchLineEdit() { + return m_txtSearchMessages; + } + + // Implementation of BaseToolBar interface. + QList availableActions() const; + QList changeableActions() const; + void saveChangeableActions(const QStringList& actions); + + // Loads actions as specified by external actions list. + // NOTE: This is used primarily for reloading actions + // when they are changed from settings. + void loadSpecificActions(const QList& actions); + + QList getSpecificActions(const QStringList& actions); + + QStringList defaultActions() const; + QStringList savedActions() const; + + signals: + void messageSearchPatternChanged(const QString& pattern); + + // Emitted if message filter is changed. + void messageFilterChanged(MessagesModel::MessageHighlighter filter); + + private slots: + // Called when highlighter gets changed. + void handleMessageHighlighterChange(QAction* action); + + private: + void initializeSearchBox(); + void initializeHighlighter(); + + private: + QWidgetAction* m_actionMessageHighlighter; + QToolButton* m_btnMessageHighlighter; + QMenu* m_menuMessageHighlighter; + + QWidgetAction* m_actionSearchMessages; + MessagesSearchLineEdit* m_txtSearchMessages; +}; + +#endif // NEWSTOOLBAR_H diff --git a/src/gui/messagesview.cpp b/src/gui/messagesview.cpp index 7174dc558..aa191ebe5 100755 --- a/src/gui/messagesview.cpp +++ b/src/gui/messagesview.cpp @@ -1,546 +1,532 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/messagesview.h" - -#include "core/messagesproxymodel.h" -#include "core/messagesmodel.h" -#include "miscellaneous/settings.h" -#include "miscellaneous/feedreader.h" -#include "network-web/networkfactory.h" -#include "network-web/webfactory.h" -#include "gui/dialogs/formmain.h" -#include "gui/messagebox.h" -#include "gui/treeviewcolumnsmenu.h" -#include "gui/styleditemdelegatewithoutfocus.h" - -#include -#include -#include -#include - - -MessagesView::MessagesView(QWidget *parent) - : QTreeView(parent), m_contextMenu(nullptr), m_columnsAdjusted(false) { - m_sourceModel = qApp->feedReader()->messagesModel(); - m_proxyModel = qApp->feedReader()->messagesProxyModel(); - - // Forward count changes to the view. - createConnections(); - setModel(m_proxyModel); - setupAppearance(); - - header()->setContextMenuPolicy(Qt::CustomContextMenu); - connect(header(), &QHeaderView::customContextMenuRequested, [=](const QPoint &point) { - TreeViewColumnsMenu mm(header()); - mm.exec(header()->mapToGlobal(point)); - }); -} - -MessagesView::~MessagesView() { - qDebug("Destroying MessagesView instance."); -} - -void MessagesView::sort(int column, Qt::SortOrder order, bool repopulate_data, bool change_header, bool emit_changed_from_header) { - if (change_header && !emit_changed_from_header) { - header()->blockSignals(true); - } - - m_sourceModel->addSortState(column, order); - - if (repopulate_data) { - m_sourceModel->repopulate(); - } - - if (change_header) { - header()->setSortIndicator(column, order); - header()->blockSignals(false); - } -} - -void MessagesView::createConnections() { - connect(this, &MessagesView::doubleClicked, this, &MessagesView::openSelectedSourceMessagesExternally); - - // Adjust columns when layout gets changed. - connect(header(), &QHeaderView::geometriesChanged, this, &MessagesView::adjustColumns); - connect(header(), &QHeaderView::sortIndicatorChanged, this, &MessagesView::onSortIndicatorChanged); -} - -void MessagesView::keyboardSearch(const QString &search) { - // WARNING: This is quite hacky way how to force selection of next item even - // with extended selection enabled. - setSelectionMode(QAbstractItemView::SingleSelection); - QTreeView::keyboardSearch(search); - setSelectionMode(QAbstractItemView::ExtendedSelection); -} - -void MessagesView::reloadSelections() { - const QDateTime dt1 = QDateTime::currentDateTime(); - - QModelIndex current_index = selectionModel()->currentIndex(); - const QModelIndex mapped_current_index = m_proxyModel->mapToSource(current_index); - const Message selected_message = m_sourceModel->messageAt(mapped_current_index.row()); - const int col = header()->sortIndicatorSection(); - const Qt::SortOrder ord = header()->sortIndicatorOrder(); - - // Reload the model now. - sort(col, ord, true, false, false); - - // Now, we must find the same previously focused message. - if (selected_message.m_id > 0) { - if (m_proxyModel->rowCount() == 0) { - current_index = QModelIndex(); - } - else { - for (int i = 0; i < m_proxyModel->rowCount(); i++) { - QModelIndex msg_idx = m_proxyModel->index(i, MSG_DB_TITLE_INDEX); - Message msg = m_sourceModel->messageAt(m_proxyModel->mapToSource(msg_idx).row()); - - if (msg.m_id == selected_message.m_id) { - current_index = msg_idx; - break; - } - - if (i == m_proxyModel->rowCount() - 1) { - current_index = QModelIndex(); - } - } - } - } - - if (current_index.isValid()) { - scrollTo(current_index); - setCurrentIndex(current_index); - reselectIndexes(QModelIndexList() << current_index); - } - else { - // Messages were probably removed from the model, nothing can - // be selected and no message can be displayed. - emit currentMessageRemoved(); - } - - const QDateTime dt2 = QDateTime::currentDateTime(); - - qDebug("Reloading of msg selections took %lld miliseconds.", dt1.msecsTo(dt2)); -} - -void MessagesView::setupAppearance() { - setUniformRowHeights(true); - setAcceptDrops(false); - setDragEnabled(false); - setDragDropMode(QAbstractItemView::NoDragDrop); - setExpandsOnDoubleClick(false); - setRootIsDecorated(false); - setEditTriggers(QAbstractItemView::NoEditTriggers); - setItemsExpandable(false); - setSortingEnabled(true); - setAllColumnsShowFocus(false); - setSelectionMode(QAbstractItemView::ExtendedSelection); - setItemDelegate(new StyledItemDelegateWithoutFocus(this)); - - header()->setDefaultSectionSize(MESSAGES_VIEW_DEFAULT_COL); - header()->setMinimumSectionSize(MESSAGES_VIEW_MINIMUM_COL); - header()->setCascadingSectionResizes(false); - header()->setStretchLastSection(true); - header()->setSortIndicatorShown(true); -} - -void MessagesView::keyPressEvent(QKeyEvent *event) { - QTreeView::keyPressEvent(event); - - if (event->key() == Qt::Key_Delete) { - deleteSelectedMessages(); - } -} - -void MessagesView::contextMenuEvent(QContextMenuEvent *event) { - const QModelIndex clicked_index = indexAt(event->pos()); - - if (!clicked_index.isValid()) { - TreeViewColumnsMenu menu(header()); - menu.exec(event->globalPos()); - } - else { - // Context menu is not initialized, initialize. - initializeContextMenu(); - - m_contextMenu->exec(event->globalPos()); - } -} - -void MessagesView::initializeContextMenu() { - if (m_contextMenu == nullptr) { - m_contextMenu = new QMenu(tr("Context menu for messages"), this); - } - - m_contextMenu->clear(); - m_contextMenu->addActions(QList() << - qApp->mainForm()->m_ui->m_actionSendMessageViaEmail << - qApp->mainForm()->m_ui->m_actionOpenSelectedSourceArticlesExternally << - qApp->mainForm()->m_ui->m_actionOpenSelectedMessagesInternally << - qApp->mainForm()->m_ui->m_actionMarkSelectedMessagesAsRead << - qApp->mainForm()->m_ui->m_actionMarkSelectedMessagesAsUnread << - qApp->mainForm()->m_ui->m_actionSwitchImportanceOfSelectedMessages << - qApp->mainForm()->m_ui->m_actionDeleteSelectedMessages); - - if (m_sourceModel->loadedItem() != nullptr && m_sourceModel->loadedItem()->kind() == RootItemKind::Bin) { - m_contextMenu->addAction(qApp->mainForm()->m_ui->m_actionRestoreSelectedMessages); - } -} - -void MessagesView::mousePressEvent(QMouseEvent *event) { - QTreeView::mousePressEvent(event); - - switch (event->button()) { - case Qt::LeftButton: { - // Make sure that message importance is switched when user - // clicks the "important" column. - const QModelIndex clicked_index = indexAt(event->pos()); - - if (clicked_index.isValid()) { - const QModelIndex mapped_index = m_proxyModel->mapToSource(clicked_index); - - if (mapped_index.column() == MSG_DB_IMPORTANT_INDEX) { - if (m_sourceModel->switchMessageImportance(mapped_index.row())) { - emit currentMessageChanged(m_sourceModel->messageAt(mapped_index.row()), m_sourceModel->loadedItem()); - } - } - } - - break; - } - - case Qt::MiddleButton: { - // Make sure that message importance is switched when user - // clicks the "important" column. - const QModelIndex clicked_index = indexAt(event->pos()); - - if (clicked_index.isValid()) { - const QModelIndex mapped_index = m_proxyModel->mapToSource(clicked_index); - const QString url = m_sourceModel->messageAt(mapped_index.row()).m_url; - - if (!url.isEmpty()) { - qApp->mainForm()->tabWidget()->addLinkedBrowser(url); - } - } - - - break; - } - - default: - break; - } -} - -void MessagesView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { - const QModelIndexList selected_rows = selectionModel()->selectedRows(); - const QModelIndex current_index = currentIndex(); - const QModelIndex mapped_current_index = m_proxyModel->mapToSource(current_index); - - qDebug("Current row changed - row [%d,%d] source [%d, %d].", - current_index.row(), current_index.column(), - mapped_current_index.row(), mapped_current_index.column()); - - if (mapped_current_index.isValid() && selected_rows.count() > 0) { - Message message = m_sourceModel->messageAt(m_proxyModel->mapToSource(current_index).row()); - - // Set this message as read only if current item - // wasn't changed by "mark selected messages unread" action. - m_sourceModel->setMessageRead(mapped_current_index.row(), RootItem::Read); - message.m_isRead = true; - - emit currentMessageChanged(message, m_sourceModel->loadedItem()); - } - else { - emit currentMessageRemoved(); - } - - if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::KeepCursorInCenter)).toBool()) { - scrollTo(currentIndex(), QAbstractItemView::PositionAtCenter); - } - - QTreeView::selectionChanged(selected, deselected); -} - -void MessagesView::loadItem(RootItem *item) { - const int col = header()->sortIndicatorSection(); - const Qt::SortOrder ord = header()->sortIndicatorOrder(); - - scrollToTop(); - sort(col, ord, false, true, false); - m_sourceModel->loadMessages(item); - - // Messages are loaded, make sure that previously - // active message is not shown in browser. - // BUG: Qt 5 is probably bugged here. Selections - // should be cleared automatically when SQL model is reset. - emit currentMessageRemoved(); -} - -void MessagesView::openSelectedSourceMessagesExternally() { - foreach (const QModelIndex &index, selectionModel()->selectedRows()) { - const QString link = m_sourceModel->messageAt(m_proxyModel->mapToSource(index).row()).m_url; - - if (!WebFactory::instance()->openUrlInExternalBrowser(link)) { - qApp->showGuiMessage(tr("Problem with starting external web browser"), - tr("External web browser could not be started."), - QSystemTrayIcon::Critical); - return; - } - } - - // Finally, mark opened messages as read. - if (!selectionModel()->selectedRows().isEmpty()) { - QTimer::singleShot(0, this, SLOT(markSelectedMessagesRead())); - } -} - -void MessagesView::openSelectedMessagesInternally() { - QList messages; - - foreach (const QModelIndex &index, selectionModel()->selectedRows()) { - messages << m_sourceModel->messageAt(m_proxyModel->mapToSource(index).row()); - } - - if (!messages.isEmpty()) { - emit openMessagesInNewspaperView(m_sourceModel->loadedItem(), messages); - } -} - -void MessagesView::sendSelectedMessageViaEmail() { - if (selectionModel()->selectedRows().size() == 1) { - const Message message = m_sourceModel->messageAt(m_proxyModel->mapToSource(selectionModel()->selectedRows().at(0)).row()); - - if (!WebFactory::instance()->sendMessageViaEmail(message)) { - MessageBox::show(this, - QMessageBox::Critical, - tr("Problem with starting external e-mail client"), - tr("External e-mail client could not be started.")); - } - } -} - -void MessagesView::markSelectedMessagesRead() { - setSelectedMessagesReadStatus(RootItem::Read); -} - -void MessagesView::markSelectedMessagesUnread() { - setSelectedMessagesReadStatus(RootItem::Unread); -} - -void MessagesView::setSelectedMessagesReadStatus(RootItem::ReadStatus read) { - QModelIndex current_index = selectionModel()->currentIndex(); - - if (!current_index.isValid()) { - return; - } - - QModelIndexList selected_indexes = selectionModel()->selectedRows(); - const QModelIndexList mapped_indexes = m_proxyModel->mapListToSource(selected_indexes); - - m_sourceModel->setBatchMessagesRead(mapped_indexes, read); - current_index = m_proxyModel->index(current_index.row(), current_index.column()); - - if (current_index.isValid()) { - emit currentMessageChanged(m_sourceModel->messageAt(m_proxyModel->mapToSource(current_index).row()), m_sourceModel->loadedItem()); - } - else { - emit currentMessageRemoved(); - } -} - -void MessagesView::deleteSelectedMessages() { - QModelIndex current_index = selectionModel()->currentIndex(); - - if (!current_index.isValid()) { - return; - } - - const QModelIndexList selected_indexes = selectionModel()->selectedRows(); - const QModelIndexList mapped_indexes = m_proxyModel->mapListToSource(selected_indexes); - - m_sourceModel->setBatchMessagesDeleted(mapped_indexes); - current_index = moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier); - - if (current_index.isValid()) { - setCurrentIndex(current_index); - emit currentMessageChanged(m_sourceModel->messageAt(m_proxyModel->mapToSource(current_index).row()), m_sourceModel->loadedItem()); - } - else { - emit currentMessageRemoved(); - } -} - -void MessagesView::restoreSelectedMessages() { - QModelIndex current_index = selectionModel()->currentIndex(); - - if (!current_index.isValid()) { - return; - } - - const QModelIndexList selected_indexes = selectionModel()->selectedRows(); - const QModelIndexList mapped_indexes = m_proxyModel->mapListToSource(selected_indexes); - - m_sourceModel->setBatchMessagesRestored(mapped_indexes); - current_index = m_proxyModel->index(current_index.row(), current_index.column()); - - if (current_index.isValid()) { - emit currentMessageChanged(m_sourceModel->messageAt(m_proxyModel->mapToSource(current_index).row()), m_sourceModel->loadedItem()); - - } - else { - emit currentMessageRemoved(); - } -} - -void MessagesView::switchSelectedMessagesImportance() { - QModelIndex current_index = selectionModel()->currentIndex(); - - if (!current_index.isValid()) { - return; - } - - QModelIndexList selected_indexes = selectionModel()->selectedRows(); - const QModelIndexList mapped_indexes = m_proxyModel->mapListToSource(selected_indexes); - - m_sourceModel->switchBatchMessageImportance(mapped_indexes); - current_index = m_proxyModel->index(current_index.row(), current_index.column()); - - if (current_index.isValid()) { - emit currentMessageChanged(m_sourceModel->messageAt(m_proxyModel->mapToSource(current_index).row()), m_sourceModel->loadedItem()); - } - else { - // Messages were probably removed from the model, nothing can - // be selected and no message can be displayed. - emit currentMessageRemoved(); - } -} - -void MessagesView::reselectIndexes(const QModelIndexList &indexes) { - if (indexes.size() < RESELECT_MESSAGE_THRESSHOLD) { - QItemSelection selection; - - foreach (const QModelIndex &index, indexes) { - selection.merge(QItemSelection(index, index), QItemSelectionModel::Select); - } - - selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); - } -} - -void MessagesView::selectNextItem() { - const QModelIndex index_next = moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier); - - if (index_next.isValid()) { - setCurrentIndex(index_next); - selectionModel()->select(index_next, QItemSelectionModel::Select | QItemSelectionModel::Rows); - setFocus(); - } -} - -void MessagesView::selectPreviousItem() { - const QModelIndex index_previous = moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier); - - if (index_previous.isValid()) { - setCurrentIndex(index_previous); - selectionModel()->select(index_previous, QItemSelectionModel::Select | QItemSelectionModel::Rows); - setFocus(); - } -} - -void MessagesView::selectNextUnreadItem() { - // FIXME: Use this to solve #112. - - const QModelIndexList selected_rows = selectionModel()->selectedRows(); - int active_row; - - if (!selected_rows.isEmpty()) { - // Okay, something is selected, start from it. - active_row = selected_rows.at(0).row(); - } - else { - active_row = 0; - } - - const QModelIndex next_unread = m_proxyModel->getNextPreviousUnreadItemIndex(active_row); - - if (next_unread.isValid()) { - // We found unread message, mark it. - setCurrentIndex(next_unread); - selectionModel()->select(next_unread, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); - setFocus(); - } -} - -void MessagesView::searchMessages(const QString &pattern) { - m_proxyModel->setFilterRegExp(pattern); - - if (selectionModel()->selectedRows().size() == 0) { - emit currentMessageRemoved(); - } - else { - // Scroll to selected message, it could become scrolled out due to filter change. - scrollTo(selectionModel()->selectedRows().at(0)); - } -} - -void MessagesView::filterMessages(MessagesModel::MessageHighlighter filter) { - m_sourceModel->highlightMessages(filter); -} - -void MessagesView::adjustColumns() { - if (header()->count() > 0 && !m_columnsAdjusted) { - m_columnsAdjusted = true; - - // Setup column resize strategies. - header()->setSectionResizeMode(MSG_DB_ID_INDEX, QHeaderView::Interactive); - header()->setSectionResizeMode(MSG_DB_READ_INDEX, QHeaderView::ResizeToContents); - header()->setSectionResizeMode(MSG_DB_DELETED_INDEX, QHeaderView::Interactive); - header()->setSectionResizeMode(MSG_DB_IMPORTANT_INDEX, QHeaderView::ResizeToContents); - header()->setSectionResizeMode(MSG_DB_FEED_TITLE_INDEX, QHeaderView::Interactive); - header()->setSectionResizeMode(MSG_DB_TITLE_INDEX, QHeaderView::Interactive); - header()->setSectionResizeMode(MSG_DB_URL_INDEX, QHeaderView::Interactive); - header()->setSectionResizeMode(MSG_DB_AUTHOR_INDEX, QHeaderView::Interactive); - header()->setSectionResizeMode(MSG_DB_DCREATED_INDEX, QHeaderView::Interactive); - header()->setSectionResizeMode(MSG_DB_CONTENTS_INDEX, QHeaderView::Interactive); - header()->setSectionResizeMode(MSG_DB_PDELETED_INDEX, QHeaderView::Interactive); - - //header()->resizeSection(MSG_DB_READ_INDEX, MESSAGES_VIEW_MINIMUM_COL); - //header()->resizeSection(MSG_DB_IMPORTANT_INDEX, MESSAGES_VIEW_MINIMUM_COL); - - // Hide columns. - hideColumn(MSG_DB_ID_INDEX); - hideColumn(MSG_DB_DELETED_INDEX); - hideColumn(MSG_DB_URL_INDEX); - hideColumn(MSG_DB_CONTENTS_INDEX); - hideColumn(MSG_DB_PDELETED_INDEX); - hideColumn(MSG_DB_ENCLOSURES_INDEX); - hideColumn(MSG_DB_ACCOUNT_ID_INDEX); - hideColumn(MSG_DB_CUSTOM_ID_INDEX); - hideColumn(MSG_DB_CUSTOM_HASH_INDEX); - hideColumn(MSG_DB_FEED_CUSTOM_ID_INDEX); - - qDebug("Adjusting column resize modes for MessagesView."); - } -} - -void MessagesView::onSortIndicatorChanged(int column, Qt::SortOrder order) { - // Repopulate the shit. - sort(column, order, true, false, false); - emit currentMessageRemoved(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/messagesview.h" + +#include "core/messagesproxymodel.h" +#include "core/messagesmodel.h" +#include "miscellaneous/settings.h" +#include "miscellaneous/feedreader.h" +#include "network-web/networkfactory.h" +#include "network-web/webfactory.h" +#include "gui/dialogs/formmain.h" +#include "gui/messagebox.h" +#include "gui/treeviewcolumnsmenu.h" +#include "gui/styleditemdelegatewithoutfocus.h" + +#include +#include +#include +#include + + +MessagesView::MessagesView(QWidget* parent) + : QTreeView(parent), m_contextMenu(nullptr), m_columnsAdjusted(false) { + m_sourceModel = qApp->feedReader()->messagesModel(); + m_proxyModel = qApp->feedReader()->messagesProxyModel(); + // Forward count changes to the view. + createConnections(); + setModel(m_proxyModel); + setupAppearance(); + header()->setContextMenuPolicy(Qt::CustomContextMenu); + connect(header(), &QHeaderView::customContextMenuRequested, [ = ](const QPoint & point) { + TreeViewColumnsMenu mm(header()); + mm.exec(header()->mapToGlobal(point)); + }); +} + +MessagesView::~MessagesView() { + qDebug("Destroying MessagesView instance."); +} + +void MessagesView::sort(int column, Qt::SortOrder order, bool repopulate_data, bool change_header, bool emit_changed_from_header) { + if (change_header && !emit_changed_from_header) { + header()->blockSignals(true); + } + + m_sourceModel->addSortState(column, order); + + if (repopulate_data) { + m_sourceModel->repopulate(); + } + + if (change_header) { + header()->setSortIndicator(column, order); + header()->blockSignals(false); + } +} + +void MessagesView::createConnections() { + connect(this, &MessagesView::doubleClicked, this, &MessagesView::openSelectedSourceMessagesExternally); + // Adjust columns when layout gets changed. + connect(header(), &QHeaderView::geometriesChanged, this, &MessagesView::adjustColumns); + connect(header(), &QHeaderView::sortIndicatorChanged, this, &MessagesView::onSortIndicatorChanged); +} + +void MessagesView::keyboardSearch(const QString& search) { + // WARNING: This is quite hacky way how to force selection of next item even + // with extended selection enabled. + setSelectionMode(QAbstractItemView::SingleSelection); + QTreeView::keyboardSearch(search); + setSelectionMode(QAbstractItemView::ExtendedSelection); +} + +void MessagesView::reloadSelections() { + const QDateTime dt1 = QDateTime::currentDateTime(); + QModelIndex current_index = selectionModel()->currentIndex(); + const QModelIndex mapped_current_index = m_proxyModel->mapToSource(current_index); + const Message selected_message = m_sourceModel->messageAt(mapped_current_index.row()); + const int col = header()->sortIndicatorSection(); + const Qt::SortOrder ord = header()->sortIndicatorOrder(); + // Reload the model now. + sort(col, ord, true, false, false); + + // Now, we must find the same previously focused message. + if (selected_message.m_id > 0) { + if (m_proxyModel->rowCount() == 0) { + current_index = QModelIndex(); + } + + else { + for (int i = 0; i < m_proxyModel->rowCount(); i++) { + QModelIndex msg_idx = m_proxyModel->index(i, MSG_DB_TITLE_INDEX); + Message msg = m_sourceModel->messageAt(m_proxyModel->mapToSource(msg_idx).row()); + + if (msg.m_id == selected_message.m_id) { + current_index = msg_idx; + break; + } + + if (i == m_proxyModel->rowCount() - 1) { + current_index = QModelIndex(); + } + } + } + } + + if (current_index.isValid()) { + scrollTo(current_index); + setCurrentIndex(current_index); + reselectIndexes(QModelIndexList() << current_index); + } + + else { + // Messages were probably removed from the model, nothing can + // be selected and no message can be displayed. + emit currentMessageRemoved(); + } + + const QDateTime dt2 = QDateTime::currentDateTime(); + qDebug("Reloading of msg selections took %lld miliseconds.", dt1.msecsTo(dt2)); +} + +void MessagesView::setupAppearance() { + setUniformRowHeights(true); + setAcceptDrops(false); + setDragEnabled(false); + setDragDropMode(QAbstractItemView::NoDragDrop); + setExpandsOnDoubleClick(false); + setRootIsDecorated(false); + setEditTriggers(QAbstractItemView::NoEditTriggers); + setItemsExpandable(false); + setSortingEnabled(true); + setAllColumnsShowFocus(false); + setSelectionMode(QAbstractItemView::ExtendedSelection); + setItemDelegate(new StyledItemDelegateWithoutFocus(this)); + header()->setDefaultSectionSize(MESSAGES_VIEW_DEFAULT_COL); + header()->setMinimumSectionSize(MESSAGES_VIEW_MINIMUM_COL); + header()->setCascadingSectionResizes(false); + header()->setStretchLastSection(true); + header()->setSortIndicatorShown(true); +} + +void MessagesView::keyPressEvent(QKeyEvent* event) { + QTreeView::keyPressEvent(event); + + if (event->key() == Qt::Key_Delete) { + deleteSelectedMessages(); + } +} + +void MessagesView::contextMenuEvent(QContextMenuEvent* event) { + const QModelIndex clicked_index = indexAt(event->pos()); + + if (!clicked_index.isValid()) { + TreeViewColumnsMenu menu(header()); + menu.exec(event->globalPos()); + } + + else { + // Context menu is not initialized, initialize. + initializeContextMenu(); + m_contextMenu->exec(event->globalPos()); + } +} + +void MessagesView::initializeContextMenu() { + if (m_contextMenu == nullptr) { + m_contextMenu = new QMenu(tr("Context menu for messages"), this); + } + + m_contextMenu->clear(); + m_contextMenu->addActions(QList() << + qApp->mainForm()->m_ui->m_actionSendMessageViaEmail << + qApp->mainForm()->m_ui->m_actionOpenSelectedSourceArticlesExternally << + qApp->mainForm()->m_ui->m_actionOpenSelectedMessagesInternally << + qApp->mainForm()->m_ui->m_actionMarkSelectedMessagesAsRead << + qApp->mainForm()->m_ui->m_actionMarkSelectedMessagesAsUnread << + qApp->mainForm()->m_ui->m_actionSwitchImportanceOfSelectedMessages << + qApp->mainForm()->m_ui->m_actionDeleteSelectedMessages); + + if (m_sourceModel->loadedItem() != nullptr && m_sourceModel->loadedItem()->kind() == RootItemKind::Bin) { + m_contextMenu->addAction(qApp->mainForm()->m_ui->m_actionRestoreSelectedMessages); + } +} + +void MessagesView::mousePressEvent(QMouseEvent* event) { + QTreeView::mousePressEvent(event); + + switch (event->button()) { + case Qt::LeftButton: { + // Make sure that message importance is switched when user + // clicks the "important" column. + const QModelIndex clicked_index = indexAt(event->pos()); + + if (clicked_index.isValid()) { + const QModelIndex mapped_index = m_proxyModel->mapToSource(clicked_index); + + if (mapped_index.column() == MSG_DB_IMPORTANT_INDEX) { + if (m_sourceModel->switchMessageImportance(mapped_index.row())) { + emit currentMessageChanged(m_sourceModel->messageAt(mapped_index.row()), m_sourceModel->loadedItem()); + } + } + } + + break; + } + + case Qt::MiddleButton: { + // Make sure that message importance is switched when user + // clicks the "important" column. + const QModelIndex clicked_index = indexAt(event->pos()); + + if (clicked_index.isValid()) { + const QModelIndex mapped_index = m_proxyModel->mapToSource(clicked_index); + const QString url = m_sourceModel->messageAt(mapped_index.row()).m_url; + + if (!url.isEmpty()) { + qApp->mainForm()->tabWidget()->addLinkedBrowser(url); + } + } + + break; + } + + default: + break; + } +} + +void MessagesView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { + const QModelIndexList selected_rows = selectionModel()->selectedRows(); + const QModelIndex current_index = currentIndex(); + const QModelIndex mapped_current_index = m_proxyModel->mapToSource(current_index); + qDebug("Current row changed - row [%d,%d] source [%d, %d].", + current_index.row(), current_index.column(), + mapped_current_index.row(), mapped_current_index.column()); + + if (mapped_current_index.isValid() && selected_rows.count() > 0) { + Message message = m_sourceModel->messageAt(m_proxyModel->mapToSource(current_index).row()); + // Set this message as read only if current item + // wasn't changed by "mark selected messages unread" action. + m_sourceModel->setMessageRead(mapped_current_index.row(), RootItem::Read); + message.m_isRead = true; + emit currentMessageChanged(message, m_sourceModel->loadedItem()); + } + + else { + emit currentMessageRemoved(); + } + + if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::KeepCursorInCenter)).toBool()) { + scrollTo(currentIndex(), QAbstractItemView::PositionAtCenter); + } + + QTreeView::selectionChanged(selected, deselected); +} + +void MessagesView::loadItem(RootItem* item) { + const int col = header()->sortIndicatorSection(); + const Qt::SortOrder ord = header()->sortIndicatorOrder(); + scrollToTop(); + sort(col, ord, false, true, false); + m_sourceModel->loadMessages(item); + // Messages are loaded, make sure that previously + // active message is not shown in browser. + // BUG: Qt 5 is probably bugged here. Selections + // should be cleared automatically when SQL model is reset. + emit currentMessageRemoved(); +} + +void MessagesView::openSelectedSourceMessagesExternally() { + foreach (const QModelIndex& index, selectionModel()->selectedRows()) { + const QString link = m_sourceModel->messageAt(m_proxyModel->mapToSource(index).row()).m_url; + + if (!WebFactory::instance()->openUrlInExternalBrowser(link)) { + qApp->showGuiMessage(tr("Problem with starting external web browser"), + tr("External web browser could not be started."), + QSystemTrayIcon::Critical); + return; + } + } + + // Finally, mark opened messages as read. + if (!selectionModel()->selectedRows().isEmpty()) { + QTimer::singleShot(0, this, SLOT(markSelectedMessagesRead())); + } +} + +void MessagesView::openSelectedMessagesInternally() { + QList messages; + + foreach (const QModelIndex& index, selectionModel()->selectedRows()) { + messages << m_sourceModel->messageAt(m_proxyModel->mapToSource(index).row()); + } + + if (!messages.isEmpty()) { + emit openMessagesInNewspaperView(m_sourceModel->loadedItem(), messages); + } +} + +void MessagesView::sendSelectedMessageViaEmail() { + if (selectionModel()->selectedRows().size() == 1) { + const Message message = m_sourceModel->messageAt(m_proxyModel->mapToSource(selectionModel()->selectedRows().at(0)).row()); + + if (!WebFactory::instance()->sendMessageViaEmail(message)) { + MessageBox::show(this, + QMessageBox::Critical, + tr("Problem with starting external e-mail client"), + tr("External e-mail client could not be started.")); + } + } +} + +void MessagesView::markSelectedMessagesRead() { + setSelectedMessagesReadStatus(RootItem::Read); +} + +void MessagesView::markSelectedMessagesUnread() { + setSelectedMessagesReadStatus(RootItem::Unread); +} + +void MessagesView::setSelectedMessagesReadStatus(RootItem::ReadStatus read) { + QModelIndex current_index = selectionModel()->currentIndex(); + + if (!current_index.isValid()) { + return; + } + + QModelIndexList selected_indexes = selectionModel()->selectedRows(); + const QModelIndexList mapped_indexes = m_proxyModel->mapListToSource(selected_indexes); + m_sourceModel->setBatchMessagesRead(mapped_indexes, read); + current_index = m_proxyModel->index(current_index.row(), current_index.column()); + + if (current_index.isValid()) { + emit currentMessageChanged(m_sourceModel->messageAt(m_proxyModel->mapToSource(current_index).row()), m_sourceModel->loadedItem()); + } + + else { + emit currentMessageRemoved(); + } +} + +void MessagesView::deleteSelectedMessages() { + QModelIndex current_index = selectionModel()->currentIndex(); + + if (!current_index.isValid()) { + return; + } + + const QModelIndexList selected_indexes = selectionModel()->selectedRows(); + const QModelIndexList mapped_indexes = m_proxyModel->mapListToSource(selected_indexes); + m_sourceModel->setBatchMessagesDeleted(mapped_indexes); + current_index = moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier); + + if (current_index.isValid()) { + setCurrentIndex(current_index); + emit currentMessageChanged(m_sourceModel->messageAt(m_proxyModel->mapToSource(current_index).row()), m_sourceModel->loadedItem()); + } + + else { + emit currentMessageRemoved(); + } +} + +void MessagesView::restoreSelectedMessages() { + QModelIndex current_index = selectionModel()->currentIndex(); + + if (!current_index.isValid()) { + return; + } + + const QModelIndexList selected_indexes = selectionModel()->selectedRows(); + const QModelIndexList mapped_indexes = m_proxyModel->mapListToSource(selected_indexes); + m_sourceModel->setBatchMessagesRestored(mapped_indexes); + current_index = m_proxyModel->index(current_index.row(), current_index.column()); + + if (current_index.isValid()) { + emit currentMessageChanged(m_sourceModel->messageAt(m_proxyModel->mapToSource(current_index).row()), m_sourceModel->loadedItem()); + } + + else { + emit currentMessageRemoved(); + } +} + +void MessagesView::switchSelectedMessagesImportance() { + QModelIndex current_index = selectionModel()->currentIndex(); + + if (!current_index.isValid()) { + return; + } + + QModelIndexList selected_indexes = selectionModel()->selectedRows(); + const QModelIndexList mapped_indexes = m_proxyModel->mapListToSource(selected_indexes); + m_sourceModel->switchBatchMessageImportance(mapped_indexes); + current_index = m_proxyModel->index(current_index.row(), current_index.column()); + + if (current_index.isValid()) { + emit currentMessageChanged(m_sourceModel->messageAt(m_proxyModel->mapToSource(current_index).row()), m_sourceModel->loadedItem()); + } + + else { + // Messages were probably removed from the model, nothing can + // be selected and no message can be displayed. + emit currentMessageRemoved(); + } +} + +void MessagesView::reselectIndexes(const QModelIndexList& indexes) { + if (indexes.size() < RESELECT_MESSAGE_THRESSHOLD) { + QItemSelection selection; + + foreach (const QModelIndex& index, indexes) { + selection.merge(QItemSelection(index, index), QItemSelectionModel::Select); + } + + selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + } +} + +void MessagesView::selectNextItem() { + const QModelIndex index_next = moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier); + + if (index_next.isValid()) { + setCurrentIndex(index_next); + selectionModel()->select(index_next, QItemSelectionModel::Select | QItemSelectionModel::Rows); + setFocus(); + } +} + +void MessagesView::selectPreviousItem() { + const QModelIndex index_previous = moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier); + + if (index_previous.isValid()) { + setCurrentIndex(index_previous); + selectionModel()->select(index_previous, QItemSelectionModel::Select | QItemSelectionModel::Rows); + setFocus(); + } +} + +void MessagesView::selectNextUnreadItem() { + // FIXME: Use this to solve #112. + const QModelIndexList selected_rows = selectionModel()->selectedRows(); + int active_row; + + if (!selected_rows.isEmpty()) { + // Okay, something is selected, start from it. + active_row = selected_rows.at(0).row(); + } + + else { + active_row = 0; + } + + const QModelIndex next_unread = m_proxyModel->getNextPreviousUnreadItemIndex(active_row); + + if (next_unread.isValid()) { + // We found unread message, mark it. + setCurrentIndex(next_unread); + selectionModel()->select(next_unread, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + setFocus(); + } +} + +void MessagesView::searchMessages(const QString& pattern) { + m_proxyModel->setFilterRegExp(pattern); + + if (selectionModel()->selectedRows().size() == 0) { + emit currentMessageRemoved(); + } + + else { + // Scroll to selected message, it could become scrolled out due to filter change. + scrollTo(selectionModel()->selectedRows().at(0)); + } +} + +void MessagesView::filterMessages(MessagesModel::MessageHighlighter filter) { + m_sourceModel->highlightMessages(filter); +} + +void MessagesView::adjustColumns() { + if (header()->count() > 0 && !m_columnsAdjusted) { + m_columnsAdjusted = true; + // Setup column resize strategies. + header()->setSectionResizeMode(MSG_DB_ID_INDEX, QHeaderView::Interactive); + header()->setSectionResizeMode(MSG_DB_READ_INDEX, QHeaderView::ResizeToContents); + header()->setSectionResizeMode(MSG_DB_DELETED_INDEX, QHeaderView::Interactive); + header()->setSectionResizeMode(MSG_DB_IMPORTANT_INDEX, QHeaderView::ResizeToContents); + header()->setSectionResizeMode(MSG_DB_FEED_TITLE_INDEX, QHeaderView::Interactive); + header()->setSectionResizeMode(MSG_DB_TITLE_INDEX, QHeaderView::Interactive); + header()->setSectionResizeMode(MSG_DB_URL_INDEX, QHeaderView::Interactive); + header()->setSectionResizeMode(MSG_DB_AUTHOR_INDEX, QHeaderView::Interactive); + header()->setSectionResizeMode(MSG_DB_DCREATED_INDEX, QHeaderView::Interactive); + header()->setSectionResizeMode(MSG_DB_CONTENTS_INDEX, QHeaderView::Interactive); + header()->setSectionResizeMode(MSG_DB_PDELETED_INDEX, QHeaderView::Interactive); + //header()->resizeSection(MSG_DB_READ_INDEX, MESSAGES_VIEW_MINIMUM_COL); + //header()->resizeSection(MSG_DB_IMPORTANT_INDEX, MESSAGES_VIEW_MINIMUM_COL); + // Hide columns. + hideColumn(MSG_DB_ID_INDEX); + hideColumn(MSG_DB_DELETED_INDEX); + hideColumn(MSG_DB_URL_INDEX); + hideColumn(MSG_DB_CONTENTS_INDEX); + hideColumn(MSG_DB_PDELETED_INDEX); + hideColumn(MSG_DB_ENCLOSURES_INDEX); + hideColumn(MSG_DB_ACCOUNT_ID_INDEX); + hideColumn(MSG_DB_CUSTOM_ID_INDEX); + hideColumn(MSG_DB_CUSTOM_HASH_INDEX); + hideColumn(MSG_DB_FEED_CUSTOM_ID_INDEX); + qDebug("Adjusting column resize modes for MessagesView."); + } +} + +void MessagesView::onSortIndicatorChanged(int column, Qt::SortOrder order) { + // Repopulate the shit. + sort(column, order, true, false, false); + emit currentMessageRemoved(); +} diff --git a/src/gui/messagesview.h b/src/gui/messagesview.h index ef93501c8..b1aba065d 100755 --- a/src/gui/messagesview.h +++ b/src/gui/messagesview.h @@ -1,125 +1,125 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef MESSAGESVIEW_H -#define MESSAGESVIEW_H - -#include "core/messagesmodel.h" - -#include "services/abstract/rootitem.h" - -#include -#include - - -class MessagesProxyModel; - -class MessagesView : public QTreeView { - Q_OBJECT - - public: - // Constructors and destructors. - explicit MessagesView(QWidget *parent = 0); - virtual ~MessagesView(); - - // Model accessors. - inline MessagesProxyModel *model() const { - return m_proxyModel; - } - - inline MessagesModel *sourceModel() const { - return m_sourceModel; - } - - public slots: - void keyboardSearch(const QString &search); - - // Called after data got changed externally - // and it needs to be reloaded to the view. - void reloadSelections(); - - // Loads un-deleted messages from selected feeds. - void loadItem(RootItem *item); - - // Message manipulators. - void openSelectedSourceMessagesExternally(); - void openSelectedMessagesInternally(); - void sendSelectedMessageViaEmail(); - - // Works with SELECTED messages only. - void setSelectedMessagesReadStatus(RootItem::ReadStatus read); - void markSelectedMessagesRead(); - void markSelectedMessagesUnread(); - void switchSelectedMessagesImportance(); - void deleteSelectedMessages(); - void restoreSelectedMessages(); - - void selectNextItem(); - void selectPreviousItem(); - void selectNextUnreadItem(); - - // Searchs the visible message according to given pattern. - void searchMessages(const QString &pattern); - void filterMessages(MessagesModel::MessageHighlighter filter); - - private slots: - // Marks given indexes as selected. - void reselectIndexes(const QModelIndexList &indexes); - - // Changes resize mode for all columns. - void adjustColumns(); - - // Saves current sort state. - void onSortIndicatorChanged(int column, Qt::SortOrder order); - - signals: - // Link/message openers. - void openLinkNewTab(const QString &link); - void openLinkMiniBrowser(const QString &link); - void openMessagesInNewspaperView(RootItem *root, const QList &messages); - - // Notify others about message selections. - void currentMessageChanged(const Message &message, RootItem *root); - void currentMessageRemoved(); - - private: - void sort(int column, Qt::SortOrder order, bool repopulate_data, bool change_header, bool emit_changed_from_header); - - // Creates needed connections. - void createConnections(); - - // Initializes context menu. - void initializeContextMenu(); - - // Sets up appearance. - void setupAppearance(); - - // Event reimplementations. - void contextMenuEvent(QContextMenuEvent *event); - void mousePressEvent(QMouseEvent *event); - void keyPressEvent(QKeyEvent *event); - void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); - - QMenu *m_contextMenu; - - MessagesProxyModel *m_proxyModel; - MessagesModel *m_sourceModel; - - bool m_columnsAdjusted; -}; - -#endif // MESSAGESVIEW_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef MESSAGESVIEW_H +#define MESSAGESVIEW_H + +#include "core/messagesmodel.h" + +#include "services/abstract/rootitem.h" + +#include +#include + + +class MessagesProxyModel; + +class MessagesView : public QTreeView { + Q_OBJECT + + public: + // Constructors and destructors. + explicit MessagesView(QWidget* parent = 0); + virtual ~MessagesView(); + + // Model accessors. + inline MessagesProxyModel* model() const { + return m_proxyModel; + } + + inline MessagesModel* sourceModel() const { + return m_sourceModel; + } + + public slots: + void keyboardSearch(const QString& search); + + // Called after data got changed externally + // and it needs to be reloaded to the view. + void reloadSelections(); + + // Loads un-deleted messages from selected feeds. + void loadItem(RootItem* item); + + // Message manipulators. + void openSelectedSourceMessagesExternally(); + void openSelectedMessagesInternally(); + void sendSelectedMessageViaEmail(); + + // Works with SELECTED messages only. + void setSelectedMessagesReadStatus(RootItem::ReadStatus read); + void markSelectedMessagesRead(); + void markSelectedMessagesUnread(); + void switchSelectedMessagesImportance(); + void deleteSelectedMessages(); + void restoreSelectedMessages(); + + void selectNextItem(); + void selectPreviousItem(); + void selectNextUnreadItem(); + + // Searchs the visible message according to given pattern. + void searchMessages(const QString& pattern); + void filterMessages(MessagesModel::MessageHighlighter filter); + + private slots: + // Marks given indexes as selected. + void reselectIndexes(const QModelIndexList& indexes); + + // Changes resize mode for all columns. + void adjustColumns(); + + // Saves current sort state. + void onSortIndicatorChanged(int column, Qt::SortOrder order); + + signals: + // Link/message openers. + void openLinkNewTab(const QString& link); + void openLinkMiniBrowser(const QString& link); + void openMessagesInNewspaperView(RootItem* root, const QList& messages); + + // Notify others about message selections. + void currentMessageChanged(const Message& message, RootItem* root); + void currentMessageRemoved(); + + private: + void sort(int column, Qt::SortOrder order, bool repopulate_data, bool change_header, bool emit_changed_from_header); + + // Creates needed connections. + void createConnections(); + + // Initializes context menu. + void initializeContextMenu(); + + // Sets up appearance. + void setupAppearance(); + + // Event reimplementations. + void contextMenuEvent(QContextMenuEvent* event); + void mousePressEvent(QMouseEvent* event); + void keyPressEvent(QKeyEvent* event); + void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + + QMenu* m_contextMenu; + + MessagesProxyModel* m_proxyModel; + MessagesModel* m_sourceModel; + + bool m_columnsAdjusted; +}; + +#endif // MESSAGESVIEW_H diff --git a/src/gui/messagetextbrowser.cpp b/src/gui/messagetextbrowser.cpp index 6612f3d53..318f5100c 100755 --- a/src/gui/messagetextbrowser.cpp +++ b/src/gui/messagetextbrowser.cpp @@ -1,51 +1,51 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/messagetextbrowser.h" - -#include "miscellaneous/application.h" -#include "miscellaneous/iconfactory.h" -#include "network-web/networkfactory.h" - - -MessageTextBrowser::MessageTextBrowser(QWidget *parent) : QTextBrowser(parent) { -} - -MessageTextBrowser::~MessageTextBrowser() { -} - -QVariant MessageTextBrowser::loadResource(int type, const QUrl &name) { - Q_UNUSED(name) - - switch (type) { - case QTextDocument::ImageResource: { - if (m_imagePlaceholder.isNull()) { - m_imagePlaceholder = qApp->icons()->miscPixmap(QSL("image-placeholder")).scaledToWidth(20, Qt::FastTransformation); - } - - return m_imagePlaceholder; - } - - default: - return QTextBrowser::loadResource(type, name); - } -} - -void MessageTextBrowser::wheelEvent(QWheelEvent *e) { - QTextBrowser::wheelEvent(e); - qApp->settings()->setValue(GROUP(Messages), Messages::PreviewerFontStandard, font().toString()); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/messagetextbrowser.h" + +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" +#include "network-web/networkfactory.h" + + +MessageTextBrowser::MessageTextBrowser(QWidget* parent) : QTextBrowser(parent) { +} + +MessageTextBrowser::~MessageTextBrowser() { +} + +QVariant MessageTextBrowser::loadResource(int type, const QUrl& name) { + Q_UNUSED(name) + + switch (type) { + case QTextDocument::ImageResource: { + if (m_imagePlaceholder.isNull()) { + m_imagePlaceholder = qApp->icons()->miscPixmap(QSL("image-placeholder")).scaledToWidth(20, Qt::FastTransformation); + } + + return m_imagePlaceholder; + } + + default: + return QTextBrowser::loadResource(type, name); + } +} + +void MessageTextBrowser::wheelEvent(QWheelEvent* e) { + QTextBrowser::wheelEvent(e); + qApp->settings()->setValue(GROUP(Messages), Messages::PreviewerFontStandard, font().toString()); +} diff --git a/src/gui/messagetextbrowser.h b/src/gui/messagetextbrowser.h index 535d96a05..41be0c620 100755 --- a/src/gui/messagetextbrowser.h +++ b/src/gui/messagetextbrowser.h @@ -1,40 +1,40 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef MESSAGETEXTBROWSER_H -#define MESSAGETEXTBROWSER_H - -#include - - -class MessageTextBrowser : public QTextBrowser { - Q_OBJECT - - public: - explicit MessageTextBrowser(QWidget *parent = 0); - virtual ~MessageTextBrowser(); - - QVariant loadResource(int type, const QUrl &name); - - protected: - void wheelEvent(QWheelEvent *e); - - private: - QPixmap m_imagePlaceholder; -}; - -#endif // MESSAGETEXTBROWSER_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef MESSAGETEXTBROWSER_H +#define MESSAGETEXTBROWSER_H + +#include + + +class MessageTextBrowser : public QTextBrowser { + Q_OBJECT + + public: + explicit MessageTextBrowser(QWidget* parent = 0); + virtual ~MessageTextBrowser(); + + QVariant loadResource(int type, const QUrl& name); + + protected: + void wheelEvent(QWheelEvent* e); + + private: + QPixmap m_imagePlaceholder; +}; + +#endif // MESSAGETEXTBROWSER_H diff --git a/src/gui/newspaperpreviewer.cpp b/src/gui/newspaperpreviewer.cpp index aaf09b4f1..3b04ee382 100755 --- a/src/gui/newspaperpreviewer.cpp +++ b/src/gui/newspaperpreviewer.cpp @@ -1,65 +1,64 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/newspaperpreviewer.h" - -#include "gui/messagepreviewer.h" -#include "gui/dialogs/formmain.h" -#include "miscellaneous/application.h" - -#include - - -NewspaperPreviewer::NewspaperPreviewer(RootItem *root, QList messages, QWidget *parent) - : TabContent(parent), m_ui(new Ui::NewspaperPreviewer), m_root(root), m_messages(messages) { - m_ui->setupUi(this); - connect(m_ui->m_btnShowMoreMessages, &QPushButton::clicked, this, &NewspaperPreviewer::showMoreMessages); - showMoreMessages(); -} - -NewspaperPreviewer::~NewspaperPreviewer() { -} - -void NewspaperPreviewer::showMoreMessages() { - if (!m_root.isNull()) { - int current_scroll = m_ui->scrollArea->verticalScrollBar()->value(); - - for (int i = 0; i < 10 && !m_messages.isEmpty(); i++) { - Message msg = m_messages.takeFirst(); - MessagePreviewer *prev = new MessagePreviewer(this); - QMargins margins = prev->layout()->contentsMargins(); - - connect(prev, &MessagePreviewer::requestMessageListReload, this, &NewspaperPreviewer::requestMessageListReload); - - margins.setRight(0); - prev->layout()->setContentsMargins(margins); - prev->setFixedHeight(300); - prev->loadMessage(msg, m_root); - m_ui->m_layout->insertWidget(m_ui->m_layout->count() - 2, prev); - } - - m_ui->m_btnShowMoreMessages->setText(tr("Show more messages (%n remaining)", "", m_messages.size())); - m_ui->m_btnShowMoreMessages->setEnabled(!m_messages.isEmpty()); - m_ui->scrollArea->verticalScrollBar()->setValue(current_scroll); - } - else { - qApp->showGuiMessage(tr("Cannot show more messages"), - tr("Cannot show more messages because parent feed was removed."), - QSystemTrayIcon::Warning, - qApp->mainForm(), true); - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/newspaperpreviewer.h" + +#include "gui/messagepreviewer.h" +#include "gui/dialogs/formmain.h" +#include "miscellaneous/application.h" + +#include + + +NewspaperPreviewer::NewspaperPreviewer(RootItem* root, QList messages, QWidget* parent) + : TabContent(parent), m_ui(new Ui::NewspaperPreviewer), m_root(root), m_messages(messages) { + m_ui->setupUi(this); + connect(m_ui->m_btnShowMoreMessages, &QPushButton::clicked, this, &NewspaperPreviewer::showMoreMessages); + showMoreMessages(); +} + +NewspaperPreviewer::~NewspaperPreviewer() { +} + +void NewspaperPreviewer::showMoreMessages() { + if (!m_root.isNull()) { + int current_scroll = m_ui->scrollArea->verticalScrollBar()->value(); + + for (int i = 0; i < 10 && !m_messages.isEmpty(); i++) { + Message msg = m_messages.takeFirst(); + MessagePreviewer* prev = new MessagePreviewer(this); + QMargins margins = prev->layout()->contentsMargins(); + connect(prev, &MessagePreviewer::requestMessageListReload, this, &NewspaperPreviewer::requestMessageListReload); + margins.setRight(0); + prev->layout()->setContentsMargins(margins); + prev->setFixedHeight(300); + prev->loadMessage(msg, m_root); + m_ui->m_layout->insertWidget(m_ui->m_layout->count() - 2, prev); + } + + m_ui->m_btnShowMoreMessages->setText(tr("Show more messages (%n remaining)", "", m_messages.size())); + m_ui->m_btnShowMoreMessages->setEnabled(!m_messages.isEmpty()); + m_ui->scrollArea->verticalScrollBar()->setValue(current_scroll); + } + + else { + qApp->showGuiMessage(tr("Cannot show more messages"), + tr("Cannot show more messages because parent feed was removed."), + QSystemTrayIcon::Warning, + qApp->mainForm(), true); + } +} diff --git a/src/gui/newspaperpreviewer.h b/src/gui/newspaperpreviewer.h index a214343b9..3f52ae0da 100755 --- a/src/gui/newspaperpreviewer.h +++ b/src/gui/newspaperpreviewer.h @@ -1,58 +1,58 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef NEWSPAPERPREVIEWER_H -#define NEWSPAPERPREVIEWER_H - -#include - -#include "gui/tabcontent.h" - -#include "ui_newspaperpreviewer.h" - -#include "core/message.h" -#include "services/abstract/rootitem.h" - -#include - - -namespace Ui { - class NewspaperPreviewer; -} - -class RootItem; - -class NewspaperPreviewer : public TabContent { - Q_OBJECT - - public: - explicit NewspaperPreviewer(RootItem *root, QList messages, QWidget *parent = 0); - virtual ~NewspaperPreviewer(); - - private slots: - void showMoreMessages(); - - signals: - void requestMessageListReload(bool mark_current_as_read); - - private: - QScopedPointer m_ui; - QPointer m_root; - QList m_messages; -}; - -#endif // NEWSPAPERPREVIEWER_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef NEWSPAPERPREVIEWER_H +#define NEWSPAPERPREVIEWER_H + +#include + +#include "gui/tabcontent.h" + +#include "ui_newspaperpreviewer.h" + +#include "core/message.h" +#include "services/abstract/rootitem.h" + +#include + + +namespace Ui { + class NewspaperPreviewer; +} + +class RootItem; + +class NewspaperPreviewer : public TabContent { + Q_OBJECT + + public: + explicit NewspaperPreviewer(RootItem* root, QList messages, QWidget* parent = 0); + virtual ~NewspaperPreviewer(); + + private slots: + void showMoreMessages(); + + signals: + void requestMessageListReload(bool mark_current_as_read); + + private: + QScopedPointer m_ui; + QPointer m_root; + QList m_messages; +}; + +#endif // NEWSPAPERPREVIEWER_H diff --git a/src/gui/plaintoolbutton.cpp b/src/gui/plaintoolbutton.cpp index 25c973a45..a72d40616 100755 --- a/src/gui/plaintoolbutton.cpp +++ b/src/gui/plaintoolbutton.cpp @@ -25,57 +25,56 @@ #include -PlainToolButton::PlainToolButton(QWidget *parent) : QToolButton(parent), m_padding(0) { +PlainToolButton::PlainToolButton(QWidget* parent) : QToolButton(parent), m_padding(0) { } PlainToolButton::~PlainToolButton() { } -void PlainToolButton::paintEvent(QPaintEvent *e) { - Q_UNUSED(e) +void PlainToolButton::paintEvent(QPaintEvent* e) { + Q_UNUSED(e) + QPainter p(this); + QRect rect(QPoint(0, 0), size()); + // Set padding. + rect.adjust(m_padding, m_padding, -m_padding, -m_padding); - QPainter p(this); - QRect rect(QPoint(0, 0), size()); + if (isEnabled()) { + if (underMouse() || isChecked()) { + p.setOpacity(0.7); + } + } - // Set padding. - rect.adjust(m_padding, m_padding, -m_padding, -m_padding); + else { + p.setOpacity(0.3); + } - if (isEnabled()) { - if (underMouse() || isChecked()) { - p.setOpacity(0.7); - } - } - else { - p.setOpacity(0.3); - } - - icon().paint(&p, rect); + icon().paint(&p, rect); } int PlainToolButton::padding() const { - return m_padding; + return m_padding; } void PlainToolButton::setPadding(int padding) { - m_padding = padding; - repaint(); + m_padding = padding; + repaint(); } void PlainToolButton::setChecked(bool checked) { - QToolButton::setChecked(checked); - repaint(); + QToolButton::setChecked(checked); + repaint(); } -void PlainToolButton::reactOnActionChange(QAction *action) { - if (action != nullptr) { - setEnabled(action->isEnabled()); - setCheckable(action->isCheckable()); - setChecked(action->isChecked()); - setIcon(action->icon()); - setToolTip(action->toolTip()); - } +void PlainToolButton::reactOnActionChange(QAction* action) { + if (action != nullptr) { + setEnabled(action->isEnabled()); + setCheckable(action->isCheckable()); + setChecked(action->isChecked()); + setIcon(action->icon()); + setToolTip(action->toolTip()); + } } void PlainToolButton::reactOnSenderActionChange() { - reactOnActionChange(qobject_cast(sender())); + reactOnActionChange(qobject_cast(sender())); } diff --git a/src/gui/plaintoolbutton.h b/src/gui/plaintoolbutton.h index 7130a84f9..690e1c8de 100755 --- a/src/gui/plaintoolbutton.h +++ b/src/gui/plaintoolbutton.h @@ -22,28 +22,28 @@ class PlainToolButton : public QToolButton { - Q_OBJECT + Q_OBJECT - public: - // Contructors and destructors. - explicit PlainToolButton(QWidget *parent = 0); - virtual ~PlainToolButton(); + public: + // Contructors and destructors. + explicit PlainToolButton(QWidget* parent = 0); + virtual ~PlainToolButton(); - // Padding changers. - int padding() const; - void setPadding(int padding); + // Padding changers. + int padding() const; + void setPadding(int padding); - public slots: - void setChecked(bool checked); - void reactOnActionChange(QAction *action); - void reactOnSenderActionChange(); + public slots: + void setChecked(bool checked); + void reactOnActionChange(QAction* action); + void reactOnSenderActionChange(); - protected: - // Custom look. - void paintEvent(QPaintEvent *e); + protected: + // Custom look. + void paintEvent(QPaintEvent* e); - private: - int m_padding; + private: + int m_padding; }; #endif // CLOSEBUTTON_H diff --git a/src/gui/settings/settingsbrowsermail.cpp b/src/gui/settings/settingsbrowsermail.cpp index fe3fd2f4c..5bdc399a4 100755 --- a/src/gui/settings/settingsbrowsermail.cpp +++ b/src/gui/settings/settingsbrowsermail.cpp @@ -1,202 +1,185 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/settings/settingsbrowsermail.h" - -#include "network-web/silentnetworkaccessmanager.h" -#include "miscellaneous/application.h" -#include "miscellaneous/textfactory.h" -#include "gui/guiutilities.h" - -#include -#include - - -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); - -#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); - connect(m_ui->m_txtProxyPassword, &QLineEdit::textChanged, this, &SettingsBrowserMail::dirtifySettings); - connect(m_ui->m_txtProxyUsername, &QLineEdit::textChanged, this, &SettingsBrowserMail::dirtifySettings); - connect(m_ui->m_spinProxyPort, static_cast(&QSpinBox::valueChanged), this, &SettingsBrowserMail::dirtifySettings); - connect(m_ui->m_grpCustomExternalBrowser, &QGroupBox::toggled, this, &SettingsBrowserMail::dirtifySettings); - connect(m_ui->m_grpCustomExternalEmail, &QGroupBox::toggled, this, &SettingsBrowserMail::dirtifySettings); - connect(m_ui->m_txtExternalBrowserArguments, &QLineEdit::textChanged, this, &SettingsBrowserMail::dirtifySettings); - connect(m_ui->m_txtExternalBrowserExecutable, &QLineEdit::textChanged, this, &SettingsBrowserMail::dirtifySettings); - connect(m_ui->m_txtExternalEmailArguments, &QLineEdit::textChanged, this, &SettingsBrowserMail::dirtifySettings); - connect(m_ui->m_txtExternalEmailExecutable, &QLineEdit::textChanged, this, &SettingsBrowserMail::dirtifySettings); - - connect(m_ui->m_cmbProxyType, static_cast(&QComboBox::currentIndexChanged), this, &SettingsBrowserMail::onProxyTypeChanged); - connect(m_ui->m_checkShowPassword, &QCheckBox::stateChanged, this, &SettingsBrowserMail::displayProxyPassword); - connect(m_ui->m_cmbExternalBrowserPreset, static_cast(&QComboBox::currentIndexChanged), this, &SettingsBrowserMail::changeDefaultBrowserArguments); - connect(m_ui->m_btnExternalBrowserExecutable, &QPushButton::clicked, this, &SettingsBrowserMail::selectBrowserExecutable); - connect(m_ui->m_cmbExternalEmailPreset, static_cast(&QComboBox::currentIndexChanged), this, &SettingsBrowserMail::changeDefaultEmailArguments); - connect(m_ui->m_btnExternalEmailExecutable, &QPushButton::clicked, this, &SettingsBrowserMail::selectEmailExecutable); -} - -SettingsBrowserMail::~SettingsBrowserMail() { - delete m_ui; -} - -void SettingsBrowserMail::changeDefaultBrowserArguments(int index) { - if (index != 0) { - m_ui->m_txtExternalBrowserArguments->setText(m_ui->m_cmbExternalBrowserPreset->itemData(index).toString()); - } -} - -void SettingsBrowserMail::selectBrowserExecutable() { - const QString executable_file = QFileDialog::getOpenFileName(this, - tr("Select web browser executable"), - qApp->getHomeFolderPath(), - //: File filter for external browser selection dialog. - #if defined(Q_OS_LINUX) - tr("Executables (*)") - #else - tr("Executables (*.*)") - #endif - ); - - if (!executable_file.isEmpty()) { - m_ui->m_txtExternalBrowserExecutable->setText(QDir::toNativeSeparators(executable_file)); - } -} - -void SettingsBrowserMail::displayProxyPassword(int state) { - if (state == Qt::Checked) { - m_ui->m_txtProxyPassword->setEchoMode(QLineEdit::Normal); - } - else { - m_ui->m_txtProxyPassword->setEchoMode(QLineEdit::PasswordEchoOnEdit); - } -} - -void SettingsBrowserMail::onProxyTypeChanged(int index) { - const QNetworkProxy::ProxyType selected_type = static_cast(m_ui->m_cmbProxyType->itemData(index).toInt()); - const bool is_proxy_selected = selected_type != QNetworkProxy::NoProxy && selected_type != QNetworkProxy::DefaultProxy; - - m_ui->m_txtProxyHost->setEnabled(is_proxy_selected); - m_ui->m_txtProxyPassword->setEnabled(is_proxy_selected); - m_ui->m_txtProxyUsername->setEnabled(is_proxy_selected); - m_ui->m_spinProxyPort->setEnabled(is_proxy_selected); - m_ui->m_checkShowPassword->setEnabled(is_proxy_selected); - m_ui->m_lblProxyHost->setEnabled(is_proxy_selected); - 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); -} - -void SettingsBrowserMail::changeDefaultEmailArguments(int index) { - if (index != 0) { - m_ui->m_txtExternalEmailArguments->setText(m_ui->m_cmbExternalEmailPreset->itemData(index).toString()); - } -} - -void SettingsBrowserMail::selectEmailExecutable() { - QString executable_file = QFileDialog::getOpenFileName(this, - tr("Select e-mail executable"), - qApp->getHomeFolderPath(), - //: File filter for external e-mail selection dialog. - #if defined(Q_OS_LINUX) - tr("Executables (*)") - #else - tr("Executables (*.*)") - #endif - ); - - if (!executable_file.isEmpty()) { - m_ui->m_txtExternalEmailExecutable->setText(QDir::toNativeSeparators(executable_file)); - } -} - -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. - 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. - 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()); - m_ui->m_grpCustomExternalEmail->setChecked(settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalEmailEnabled)).toBool()); - - m_ui->m_cmbProxyType->addItem(tr("No proxy"), QNetworkProxy::NoProxy); - 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. - 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)); - m_ui->m_txtProxyHost->setText(settings()->value(GROUP(Proxy), SETTING(Proxy::Host)).toString()); - 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()); - - 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. - 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. - 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()); - - settings()->setValue(GROUP(Proxy), Proxy::Type, m_ui->m_cmbProxyType->itemData(m_ui->m_cmbProxyType->currentIndex())); - settings()->setValue(GROUP(Proxy), Proxy::Host, m_ui->m_txtProxyHost->text()); - 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. - SilentNetworkAccessManager::instance()->loadSettings(); - - onEndSaveSettings(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/settings/settingsbrowsermail.h" + +#include "network-web/silentnetworkaccessmanager.h" +#include "miscellaneous/application.h" +#include "miscellaneous/textfactory.h" +#include "gui/guiutilities.h" + +#include +#include + + +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); +#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); + connect(m_ui->m_txtProxyPassword, &QLineEdit::textChanged, this, &SettingsBrowserMail::dirtifySettings); + connect(m_ui->m_txtProxyUsername, &QLineEdit::textChanged, this, &SettingsBrowserMail::dirtifySettings); + connect(m_ui->m_spinProxyPort, static_cast(&QSpinBox::valueChanged), this, &SettingsBrowserMail::dirtifySettings); + connect(m_ui->m_grpCustomExternalBrowser, &QGroupBox::toggled, this, &SettingsBrowserMail::dirtifySettings); + connect(m_ui->m_grpCustomExternalEmail, &QGroupBox::toggled, this, &SettingsBrowserMail::dirtifySettings); + connect(m_ui->m_txtExternalBrowserArguments, &QLineEdit::textChanged, this, &SettingsBrowserMail::dirtifySettings); + connect(m_ui->m_txtExternalBrowserExecutable, &QLineEdit::textChanged, this, &SettingsBrowserMail::dirtifySettings); + connect(m_ui->m_txtExternalEmailArguments, &QLineEdit::textChanged, this, &SettingsBrowserMail::dirtifySettings); + connect(m_ui->m_txtExternalEmailExecutable, &QLineEdit::textChanged, this, &SettingsBrowserMail::dirtifySettings); + connect(m_ui->m_cmbProxyType, static_cast(&QComboBox::currentIndexChanged), this, &SettingsBrowserMail::onProxyTypeChanged); + connect(m_ui->m_checkShowPassword, &QCheckBox::stateChanged, this, &SettingsBrowserMail::displayProxyPassword); + connect(m_ui->m_cmbExternalBrowserPreset, static_cast(&QComboBox::currentIndexChanged), this, &SettingsBrowserMail::changeDefaultBrowserArguments); + connect(m_ui->m_btnExternalBrowserExecutable, &QPushButton::clicked, this, &SettingsBrowserMail::selectBrowserExecutable); + connect(m_ui->m_cmbExternalEmailPreset, static_cast(&QComboBox::currentIndexChanged), this, &SettingsBrowserMail::changeDefaultEmailArguments); + connect(m_ui->m_btnExternalEmailExecutable, &QPushButton::clicked, this, &SettingsBrowserMail::selectEmailExecutable); +} + +SettingsBrowserMail::~SettingsBrowserMail() { + delete m_ui; +} + +void SettingsBrowserMail::changeDefaultBrowserArguments(int index) { + if (index != 0) { + m_ui->m_txtExternalBrowserArguments->setText(m_ui->m_cmbExternalBrowserPreset->itemData(index).toString()); + } +} + +void SettingsBrowserMail::selectBrowserExecutable() { + const QString executable_file = QFileDialog::getOpenFileName(this, + tr("Select web browser executable"), + qApp->getHomeFolderPath(), + //: File filter for external browser selection dialog. +#if defined(Q_OS_LINUX) + tr("Executables (*)") +#else + tr("Executables (*.*)") +#endif + ); + + if (!executable_file.isEmpty()) { + m_ui->m_txtExternalBrowserExecutable->setText(QDir::toNativeSeparators(executable_file)); + } +} + +void SettingsBrowserMail::displayProxyPassword(int state) { + if (state == Qt::Checked) { + m_ui->m_txtProxyPassword->setEchoMode(QLineEdit::Normal); + } + + else { + m_ui->m_txtProxyPassword->setEchoMode(QLineEdit::PasswordEchoOnEdit); + } +} + +void SettingsBrowserMail::onProxyTypeChanged(int index) { + const QNetworkProxy::ProxyType selected_type = static_cast(m_ui->m_cmbProxyType->itemData(index).toInt()); + const bool is_proxy_selected = selected_type != QNetworkProxy::NoProxy && selected_type != QNetworkProxy::DefaultProxy; + m_ui->m_txtProxyHost->setEnabled(is_proxy_selected); + m_ui->m_txtProxyPassword->setEnabled(is_proxy_selected); + m_ui->m_txtProxyUsername->setEnabled(is_proxy_selected); + m_ui->m_spinProxyPort->setEnabled(is_proxy_selected); + m_ui->m_checkShowPassword->setEnabled(is_proxy_selected); + m_ui->m_lblProxyHost->setEnabled(is_proxy_selected); + 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); +} + +void SettingsBrowserMail::changeDefaultEmailArguments(int index) { + if (index != 0) { + m_ui->m_txtExternalEmailArguments->setText(m_ui->m_cmbExternalEmailPreset->itemData(index).toString()); + } +} + +void SettingsBrowserMail::selectEmailExecutable() { + QString executable_file = QFileDialog::getOpenFileName(this, + tr("Select e-mail executable"), + qApp->getHomeFolderPath(), + //: File filter for external e-mail selection dialog. +#if defined(Q_OS_LINUX) + tr("Executables (*)") +#else + tr("Executables (*.*)") +#endif + ); + + if (!executable_file.isEmpty()) { + m_ui->m_txtExternalEmailExecutable->setText(QDir::toNativeSeparators(executable_file)); + } +} + +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. + 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. + 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()); + m_ui->m_grpCustomExternalEmail->setChecked(settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalEmailEnabled)).toBool()); + m_ui->m_cmbProxyType->addItem(tr("No proxy"), QNetworkProxy::NoProxy); + 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. + 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)); + m_ui->m_txtProxyHost->setText(settings()->value(GROUP(Proxy), SETTING(Proxy::Host)).toString()); + 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()); + 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. + 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. + 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()); + settings()->setValue(GROUP(Proxy), Proxy::Type, m_ui->m_cmbProxyType->itemData(m_ui->m_cmbProxyType->currentIndex())); + settings()->setValue(GROUP(Proxy), Proxy::Host, m_ui->m_txtProxyHost->text()); + 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. + SilentNetworkAccessManager::instance()->loadSettings(); + onEndSaveSettings(); +} diff --git a/src/gui/settings/settingsbrowsermail.h b/src/gui/settings/settingsbrowsermail.h index e02200103..d23b23147 100755 --- a/src/gui/settings/settingsbrowsermail.h +++ b/src/gui/settings/settingsbrowsermail.h @@ -1,52 +1,52 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SETTINGSBROWSERMAIL_H -#define SETTINGSBROWSERMAIL_H - -#include "gui/settings/settingspanel.h" - -#include "ui_settingsbrowsermail.h" - - -class SettingsBrowserMail : public SettingsPanel { - Q_OBJECT - - public: - explicit SettingsBrowserMail(Settings *settings, QWidget *parent = 0); - virtual ~SettingsBrowserMail(); - - inline QString title() const { - return tr("Web browser & e-mail & proxy"); - } - - void loadSettings(); - void saveSettings(); - - private slots: - void changeDefaultBrowserArguments(int index); - void selectBrowserExecutable(); - void changeDefaultEmailArguments(int index); - void selectEmailExecutable(); - void displayProxyPassword(int state); - void onProxyTypeChanged(int index); - - private: - Ui::SettingsBrowserMail *m_ui; -}; - -#endif // SETTINGSBROWSERMAIL_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SETTINGSBROWSERMAIL_H +#define SETTINGSBROWSERMAIL_H + +#include "gui/settings/settingspanel.h" + +#include "ui_settingsbrowsermail.h" + + +class SettingsBrowserMail : public SettingsPanel { + Q_OBJECT + + public: + explicit SettingsBrowserMail(Settings* settings, QWidget* parent = 0); + virtual ~SettingsBrowserMail(); + + inline QString title() const { + return tr("Web browser & e-mail & proxy"); + } + + void loadSettings(); + void saveSettings(); + + private slots: + void changeDefaultBrowserArguments(int index); + void selectBrowserExecutable(); + void changeDefaultEmailArguments(int index); + void selectEmailExecutable(); + void displayProxyPassword(int state); + void onProxyTypeChanged(int index); + + private: + Ui::SettingsBrowserMail* m_ui; +}; + +#endif // SETTINGSBROWSERMAIL_H diff --git a/src/gui/settings/settingsdatabase.cpp b/src/gui/settings/settingsdatabase.cpp index d10a73162..b311d0b3f 100755 --- a/src/gui/settings/settingsdatabase.cpp +++ b/src/gui/settings/settingsdatabase.cpp @@ -1,216 +1,206 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/settings/settingsdatabase.h" - -#include "miscellaneous/databasefactory.h" -#include "definitions/definitions.h" -#include "miscellaneous/application.h" -#include "miscellaneous/textfactory.h" -#include "gui/guiutilities.h" - - -SettingsDatabase::SettingsDatabase(Settings *settings, QWidget *parent) - : SettingsPanel(settings, parent), m_ui(new Ui::SettingsDatabase) { - m_ui->setupUi(this); - - GuiUtilities::setLabelAsNotice(m_ui->m_lblDataStorageWarning, true); - GuiUtilities::setLabelAsNotice(m_ui->m_lblMysqlInfo, false); - GuiUtilities::setLabelAsNotice(m_ui->m_lblSqliteInMemoryWarnings, true); - - connect(m_ui->m_cmbDatabaseDriver, static_cast(&QComboBox::currentIndexChanged), this, &SettingsDatabase::dirtifySettings); - connect(m_ui->m_checkSqliteUseInMemoryDatabase, &QCheckBox::toggled, this, &SettingsDatabase::dirtifySettings); - connect(m_ui->m_txtMysqlDatabase->lineEdit(), &QLineEdit::textChanged, this, &SettingsDatabase::dirtifySettings); - connect(m_ui->m_txtMysqlHostname->lineEdit(), &QLineEdit::textChanged, this, &SettingsDatabase::dirtifySettings); - connect(m_ui->m_txtMysqlPassword->lineEdit(), &QLineEdit::textChanged, this, &SettingsDatabase::dirtifySettings); - connect(m_ui->m_checkUseTransactions, &QCheckBox::toggled, this, &SettingsDatabase::dirtifySettings); - connect(m_ui->m_txtMysqlUsername->lineEdit(), &QLineEdit::textChanged, this, &SettingsDatabase::dirtifySettings); - connect(m_ui->m_spinMysqlPort, static_cast(&QSpinBox::valueChanged), this, &SettingsDatabase::dirtifySettings); - - connect(m_ui->m_cmbDatabaseDriver, static_cast(&QComboBox::currentIndexChanged), this, &SettingsDatabase::selectSqlBackend); - connect(m_ui->m_checkMysqlShowPassword, &QCheckBox::toggled, this, &SettingsDatabase::switchMysqlPasswordVisiblity); - connect(m_ui->m_txtMysqlUsername->lineEdit(), &BaseLineEdit::textChanged, this, &SettingsDatabase::onMysqlUsernameChanged); - connect(m_ui->m_txtMysqlHostname->lineEdit(), &BaseLineEdit::textChanged, this, &SettingsDatabase::onMysqlHostnameChanged); - connect(m_ui->m_txtMysqlPassword->lineEdit(), &BaseLineEdit::textChanged, this, &SettingsDatabase::onMysqlPasswordChanged); - connect(m_ui->m_txtMysqlDatabase->lineEdit(), &BaseLineEdit::textChanged, this, &SettingsDatabase::onMysqlDatabaseChanged); - connect(m_ui->m_btnMysqlTestSetup, &QPushButton::clicked, this, &SettingsDatabase::mysqlTestConnection); - - connect(m_ui->m_cmbDatabaseDriver, static_cast(&QComboBox::currentIndexChanged), this, &SettingsDatabase::requireRestart); - connect(m_ui->m_checkSqliteUseInMemoryDatabase, &QCheckBox::toggled, this, &SettingsDatabase::requireRestart); - connect(m_ui->m_spinMysqlPort, &QSpinBox::editingFinished, this, &SettingsDatabase::requireRestart); - connect(m_ui->m_txtMysqlHostname->lineEdit(), &BaseLineEdit::textEdited, this, &SettingsDatabase::requireRestart); - connect(m_ui->m_txtMysqlPassword->lineEdit(), &BaseLineEdit::textEdited, this, &SettingsDatabase::requireRestart); - connect(m_ui->m_txtMysqlUsername->lineEdit(), &BaseLineEdit::textEdited, this, &SettingsDatabase::requireRestart); -} - -SettingsDatabase::~SettingsDatabase() { - delete m_ui; -} - -void SettingsDatabase::mysqlTestConnection() { - const DatabaseFactory::MySQLError error_code = qApp->database()->mysqlTestConnection(m_ui->m_txtMysqlHostname->lineEdit()->text(), - m_ui->m_spinMysqlPort->value(), - m_ui->m_txtMysqlDatabase->lineEdit()->text(), - m_ui->m_txtMysqlUsername->lineEdit()->text(), - m_ui->m_txtMysqlPassword->lineEdit()->text()); - const QString interpretation = qApp->database()->mysqlInterpretErrorCode(error_code); - - - switch (error_code) { - case DatabaseFactory::MySQLOk: - case DatabaseFactory::MySQLUnknownDatabase: - m_ui->m_lblMysqlTestResult->setStatus(WidgetWithStatus::Ok, interpretation, interpretation); - break; - - default: - m_ui->m_lblMysqlTestResult->setStatus(WidgetWithStatus::Error, interpretation, interpretation); - break; - } -} - -void SettingsDatabase::onMysqlHostnameChanged(const QString &new_hostname) { - if (new_hostname.isEmpty()) { - m_ui->m_txtMysqlHostname->setStatus(LineEditWithStatus::Warning, tr("Hostname is empty.")); - } - else { - m_ui->m_txtMysqlHostname->setStatus(LineEditWithStatus::Ok, tr("Hostname looks ok.")); - } -} - -void SettingsDatabase::onMysqlUsernameChanged(const QString &new_username) { - if (new_username.isEmpty()) { - m_ui->m_txtMysqlUsername->setStatus(LineEditWithStatus::Warning, tr("Username is empty.")); - } - else { - m_ui->m_txtMysqlUsername->setStatus(LineEditWithStatus::Ok, tr("Username looks ok.")); - } -} - -void SettingsDatabase::onMysqlPasswordChanged(const QString &new_password) { - if (new_password.isEmpty()) { - m_ui->m_txtMysqlPassword->setStatus(LineEditWithStatus::Warning, tr("Password is empty.")); - } - else { - m_ui->m_txtMysqlPassword->setStatus(LineEditWithStatus::Ok, tr("Password looks ok.")); - } -} - -void SettingsDatabase::onMysqlDatabaseChanged(const QString &new_database) { - if (new_database.isEmpty()) { - m_ui->m_txtMysqlDatabase->setStatus(LineEditWithStatus::Warning, tr("Working database is empty.")); - } - else { - m_ui->m_txtMysqlDatabase->setStatus(LineEditWithStatus::Ok, tr("Working database is ok.")); - } -} - -void SettingsDatabase::selectSqlBackend(int index) { - const QString selected_db_driver = m_ui->m_cmbDatabaseDriver->itemData(index).toString(); - - if (selected_db_driver == APP_DB_SQLITE_DRIVER) { - m_ui->m_stackedDatabaseDriver->setCurrentIndex(0); - } - else if (selected_db_driver == APP_DB_MYSQL_DRIVER) { - m_ui->m_stackedDatabaseDriver->setCurrentIndex(1); - } - else { - qWarning("GUI for given database driver '%s' is not available.", qPrintable(selected_db_driver)); - } -} - -void SettingsDatabase::switchMysqlPasswordVisiblity(bool visible) { - m_ui->m_txtMysqlPassword->lineEdit()->setEchoMode(visible ? QLineEdit::Normal : QLineEdit::Password); -} - -void SettingsDatabase::loadSettings() { - onBeginLoadSettings(); - - m_ui->m_checkUseTransactions->setChecked(qApp->settings()->value(GROUP(Database), SETTING(Database::UseTransactions)).toBool()); - m_ui->m_lblMysqlTestResult->setStatus(WidgetWithStatus::Information, tr("No connection test triggered so far."), tr("You did not executed any connection test yet.")); - - // Load SQLite. - m_ui->m_cmbDatabaseDriver->addItem(qApp->database()->humanDriverName(DatabaseFactory::SQLITE), APP_DB_SQLITE_DRIVER); - - // Load in-memory database status. - m_ui->m_checkSqliteUseInMemoryDatabase->setChecked(settings()->value(GROUP(Database), SETTING(Database::UseInMemory)).toBool()); - - if (QSqlDatabase::isDriverAvailable(APP_DB_MYSQL_DRIVER)) { - onMysqlHostnameChanged(QString()); - onMysqlUsernameChanged(QString()); - onMysqlPasswordChanged(QString()); - onMysqlDatabaseChanged(QString()); - - // Load MySQL. - m_ui->m_cmbDatabaseDriver->addItem(qApp->database()->humanDriverName(DatabaseFactory::MYSQL), APP_DB_MYSQL_DRIVER); - - // Setup placeholders. - m_ui->m_txtMysqlHostname->lineEdit()->setPlaceholderText(tr("Hostname of your MySQL server")); - m_ui->m_txtMysqlUsername->lineEdit()->setPlaceholderText(tr("Username to login with")); - m_ui->m_txtMysqlPassword->lineEdit()->setPlaceholderText(tr("Password for your username")); - m_ui->m_txtMysqlDatabase->lineEdit()->setPlaceholderText(tr("Working database which you have full access to.")); - - m_ui->m_txtMysqlHostname->lineEdit()->setText(settings()->value(GROUP(Database), SETTING(Database::MySQLHostname)).toString()); - m_ui->m_txtMysqlUsername->lineEdit()->setText(settings()->value(GROUP(Database), SETTING(Database::MySQLUsername)).toString()); - m_ui->m_txtMysqlPassword->lineEdit()->setText(TextFactory::decrypt(settings()->value(GROUP(Database), SETTING(Database::MySQLPassword)).toString())); - m_ui->m_txtMysqlDatabase->lineEdit()->setText(settings()->value(GROUP(Database), SETTING(Database::MySQLDatabase)).toString()); - m_ui->m_spinMysqlPort->setValue(settings()->value(GROUP(Database), SETTING(Database::MySQLPort)).toInt()); - - m_ui->m_checkMysqlShowPassword->setChecked(false); - } - - int index_current_backend = m_ui->m_cmbDatabaseDriver->findData(settings()->value(GROUP(Database), SETTING(Database::ActiveDriver)).toString()); - - if (index_current_backend >= 0) { - m_ui->m_cmbDatabaseDriver->setCurrentIndex(index_current_backend); - } - - onEndLoadSettings(); -} - -void SettingsDatabase::saveSettings() { - onBeginSaveSettings(); - - // Setup in-memory database status. - const bool original_inmemory = settings()->value(GROUP(Database), SETTING(Database::UseInMemory)).toBool(); - const bool new_inmemory = m_ui->m_checkSqliteUseInMemoryDatabase->isChecked(); - - qApp->settings()->setValue(GROUP(Database), Database::UseTransactions, m_ui->m_checkUseTransactions->isChecked()); - - // Save data storage settings. - QString original_db_driver = settings()->value(GROUP(Database), SETTING(Database::ActiveDriver)).toString(); - QString selected_db_driver = m_ui->m_cmbDatabaseDriver->itemData(m_ui->m_cmbDatabaseDriver->currentIndex()).toString(); - - // Save SQLite. - settings()->setValue(GROUP(Database), Database::UseInMemory, new_inmemory); - - if (QSqlDatabase::isDriverAvailable(APP_DB_MYSQL_DRIVER)) { - // Save MySQL. - settings()->setValue(GROUP(Database), Database::MySQLHostname, m_ui->m_txtMysqlHostname->lineEdit()->text()); - settings()->setValue(GROUP(Database), Database::MySQLUsername, m_ui->m_txtMysqlUsername->lineEdit()->text()); - settings()->setValue(GROUP(Database), Database::MySQLPassword, TextFactory::encrypt(m_ui->m_txtMysqlPassword->lineEdit()->text())); - settings()->setValue(GROUP(Database), Database::MySQLDatabase, m_ui->m_txtMysqlDatabase->lineEdit()->text()); - settings()->setValue(GROUP(Database), Database::MySQLPort, m_ui->m_spinMysqlPort->value()); - } - - settings()->setValue(GROUP(Database), Database::ActiveDriver, selected_db_driver); - - if (original_db_driver != selected_db_driver || original_inmemory != new_inmemory) { - requireRestart(); - } - - onEndSaveSettings(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/settings/settingsdatabase.h" + +#include "miscellaneous/databasefactory.h" +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "miscellaneous/textfactory.h" +#include "gui/guiutilities.h" + + +SettingsDatabase::SettingsDatabase(Settings* settings, QWidget* parent) + : SettingsPanel(settings, parent), m_ui(new Ui::SettingsDatabase) { + m_ui->setupUi(this); + GuiUtilities::setLabelAsNotice(m_ui->m_lblDataStorageWarning, true); + GuiUtilities::setLabelAsNotice(m_ui->m_lblMysqlInfo, false); + GuiUtilities::setLabelAsNotice(m_ui->m_lblSqliteInMemoryWarnings, true); + connect(m_ui->m_cmbDatabaseDriver, static_cast(&QComboBox::currentIndexChanged), this, &SettingsDatabase::dirtifySettings); + connect(m_ui->m_checkSqliteUseInMemoryDatabase, &QCheckBox::toggled, this, &SettingsDatabase::dirtifySettings); + connect(m_ui->m_txtMysqlDatabase->lineEdit(), &QLineEdit::textChanged, this, &SettingsDatabase::dirtifySettings); + connect(m_ui->m_txtMysqlHostname->lineEdit(), &QLineEdit::textChanged, this, &SettingsDatabase::dirtifySettings); + connect(m_ui->m_txtMysqlPassword->lineEdit(), &QLineEdit::textChanged, this, &SettingsDatabase::dirtifySettings); + connect(m_ui->m_checkUseTransactions, &QCheckBox::toggled, this, &SettingsDatabase::dirtifySettings); + connect(m_ui->m_txtMysqlUsername->lineEdit(), &QLineEdit::textChanged, this, &SettingsDatabase::dirtifySettings); + connect(m_ui->m_spinMysqlPort, static_cast(&QSpinBox::valueChanged), this, &SettingsDatabase::dirtifySettings); + connect(m_ui->m_cmbDatabaseDriver, static_cast(&QComboBox::currentIndexChanged), this, &SettingsDatabase::selectSqlBackend); + connect(m_ui->m_checkMysqlShowPassword, &QCheckBox::toggled, this, &SettingsDatabase::switchMysqlPasswordVisiblity); + connect(m_ui->m_txtMysqlUsername->lineEdit(), &BaseLineEdit::textChanged, this, &SettingsDatabase::onMysqlUsernameChanged); + connect(m_ui->m_txtMysqlHostname->lineEdit(), &BaseLineEdit::textChanged, this, &SettingsDatabase::onMysqlHostnameChanged); + connect(m_ui->m_txtMysqlPassword->lineEdit(), &BaseLineEdit::textChanged, this, &SettingsDatabase::onMysqlPasswordChanged); + connect(m_ui->m_txtMysqlDatabase->lineEdit(), &BaseLineEdit::textChanged, this, &SettingsDatabase::onMysqlDatabaseChanged); + connect(m_ui->m_btnMysqlTestSetup, &QPushButton::clicked, this, &SettingsDatabase::mysqlTestConnection); + connect(m_ui->m_cmbDatabaseDriver, static_cast(&QComboBox::currentIndexChanged), this, &SettingsDatabase::requireRestart); + connect(m_ui->m_checkSqliteUseInMemoryDatabase, &QCheckBox::toggled, this, &SettingsDatabase::requireRestart); + connect(m_ui->m_spinMysqlPort, &QSpinBox::editingFinished, this, &SettingsDatabase::requireRestart); + connect(m_ui->m_txtMysqlHostname->lineEdit(), &BaseLineEdit::textEdited, this, &SettingsDatabase::requireRestart); + connect(m_ui->m_txtMysqlPassword->lineEdit(), &BaseLineEdit::textEdited, this, &SettingsDatabase::requireRestart); + connect(m_ui->m_txtMysqlUsername->lineEdit(), &BaseLineEdit::textEdited, this, &SettingsDatabase::requireRestart); +} + +SettingsDatabase::~SettingsDatabase() { + delete m_ui; +} + +void SettingsDatabase::mysqlTestConnection() { + const DatabaseFactory::MySQLError error_code = qApp->database()->mysqlTestConnection(m_ui->m_txtMysqlHostname->lineEdit()->text(), + m_ui->m_spinMysqlPort->value(), + m_ui->m_txtMysqlDatabase->lineEdit()->text(), + m_ui->m_txtMysqlUsername->lineEdit()->text(), + m_ui->m_txtMysqlPassword->lineEdit()->text()); + const QString interpretation = qApp->database()->mysqlInterpretErrorCode(error_code); + + switch (error_code) { + case DatabaseFactory::MySQLOk: + case DatabaseFactory::MySQLUnknownDatabase: + m_ui->m_lblMysqlTestResult->setStatus(WidgetWithStatus::Ok, interpretation, interpretation); + break; + + default: + m_ui->m_lblMysqlTestResult->setStatus(WidgetWithStatus::Error, interpretation, interpretation); + break; + } +} + +void SettingsDatabase::onMysqlHostnameChanged(const QString& new_hostname) { + if (new_hostname.isEmpty()) { + m_ui->m_txtMysqlHostname->setStatus(LineEditWithStatus::Warning, tr("Hostname is empty.")); + } + + else { + m_ui->m_txtMysqlHostname->setStatus(LineEditWithStatus::Ok, tr("Hostname looks ok.")); + } +} + +void SettingsDatabase::onMysqlUsernameChanged(const QString& new_username) { + if (new_username.isEmpty()) { + m_ui->m_txtMysqlUsername->setStatus(LineEditWithStatus::Warning, tr("Username is empty.")); + } + + else { + m_ui->m_txtMysqlUsername->setStatus(LineEditWithStatus::Ok, tr("Username looks ok.")); + } +} + +void SettingsDatabase::onMysqlPasswordChanged(const QString& new_password) { + if (new_password.isEmpty()) { + m_ui->m_txtMysqlPassword->setStatus(LineEditWithStatus::Warning, tr("Password is empty.")); + } + + else { + m_ui->m_txtMysqlPassword->setStatus(LineEditWithStatus::Ok, tr("Password looks ok.")); + } +} + +void SettingsDatabase::onMysqlDatabaseChanged(const QString& new_database) { + if (new_database.isEmpty()) { + m_ui->m_txtMysqlDatabase->setStatus(LineEditWithStatus::Warning, tr("Working database is empty.")); + } + + else { + m_ui->m_txtMysqlDatabase->setStatus(LineEditWithStatus::Ok, tr("Working database is ok.")); + } +} + +void SettingsDatabase::selectSqlBackend(int index) { + const QString selected_db_driver = m_ui->m_cmbDatabaseDriver->itemData(index).toString(); + + if (selected_db_driver == APP_DB_SQLITE_DRIVER) { + m_ui->m_stackedDatabaseDriver->setCurrentIndex(0); + } + + else if (selected_db_driver == APP_DB_MYSQL_DRIVER) { + m_ui->m_stackedDatabaseDriver->setCurrentIndex(1); + } + + else { + qWarning("GUI for given database driver '%s' is not available.", qPrintable(selected_db_driver)); + } +} + +void SettingsDatabase::switchMysqlPasswordVisiblity(bool visible) { + m_ui->m_txtMysqlPassword->lineEdit()->setEchoMode(visible ? QLineEdit::Normal : QLineEdit::Password); +} + +void SettingsDatabase::loadSettings() { + onBeginLoadSettings(); + m_ui->m_checkUseTransactions->setChecked(qApp->settings()->value(GROUP(Database), SETTING(Database::UseTransactions)).toBool()); + m_ui->m_lblMysqlTestResult->setStatus(WidgetWithStatus::Information, tr("No connection test triggered so far."), tr("You did not executed any connection test yet.")); + // Load SQLite. + m_ui->m_cmbDatabaseDriver->addItem(qApp->database()->humanDriverName(DatabaseFactory::SQLITE), APP_DB_SQLITE_DRIVER); + // Load in-memory database status. + m_ui->m_checkSqliteUseInMemoryDatabase->setChecked(settings()->value(GROUP(Database), SETTING(Database::UseInMemory)).toBool()); + + if (QSqlDatabase::isDriverAvailable(APP_DB_MYSQL_DRIVER)) { + onMysqlHostnameChanged(QString()); + onMysqlUsernameChanged(QString()); + onMysqlPasswordChanged(QString()); + onMysqlDatabaseChanged(QString()); + // Load MySQL. + m_ui->m_cmbDatabaseDriver->addItem(qApp->database()->humanDriverName(DatabaseFactory::MYSQL), APP_DB_MYSQL_DRIVER); + // Setup placeholders. + m_ui->m_txtMysqlHostname->lineEdit()->setPlaceholderText(tr("Hostname of your MySQL server")); + m_ui->m_txtMysqlUsername->lineEdit()->setPlaceholderText(tr("Username to login with")); + m_ui->m_txtMysqlPassword->lineEdit()->setPlaceholderText(tr("Password for your username")); + m_ui->m_txtMysqlDatabase->lineEdit()->setPlaceholderText(tr("Working database which you have full access to.")); + m_ui->m_txtMysqlHostname->lineEdit()->setText(settings()->value(GROUP(Database), SETTING(Database::MySQLHostname)).toString()); + m_ui->m_txtMysqlUsername->lineEdit()->setText(settings()->value(GROUP(Database), SETTING(Database::MySQLUsername)).toString()); + m_ui->m_txtMysqlPassword->lineEdit()->setText(TextFactory::decrypt(settings()->value(GROUP(Database), SETTING(Database::MySQLPassword)).toString())); + m_ui->m_txtMysqlDatabase->lineEdit()->setText(settings()->value(GROUP(Database), SETTING(Database::MySQLDatabase)).toString()); + m_ui->m_spinMysqlPort->setValue(settings()->value(GROUP(Database), SETTING(Database::MySQLPort)).toInt()); + m_ui->m_checkMysqlShowPassword->setChecked(false); + } + + int index_current_backend = m_ui->m_cmbDatabaseDriver->findData(settings()->value(GROUP(Database), SETTING(Database::ActiveDriver)).toString()); + + if (index_current_backend >= 0) { + m_ui->m_cmbDatabaseDriver->setCurrentIndex(index_current_backend); + } + + onEndLoadSettings(); +} + +void SettingsDatabase::saveSettings() { + onBeginSaveSettings(); + // Setup in-memory database status. + const bool original_inmemory = settings()->value(GROUP(Database), SETTING(Database::UseInMemory)).toBool(); + const bool new_inmemory = m_ui->m_checkSqliteUseInMemoryDatabase->isChecked(); + qApp->settings()->setValue(GROUP(Database), Database::UseTransactions, m_ui->m_checkUseTransactions->isChecked()); + // Save data storage settings. + QString original_db_driver = settings()->value(GROUP(Database), SETTING(Database::ActiveDriver)).toString(); + QString selected_db_driver = m_ui->m_cmbDatabaseDriver->itemData(m_ui->m_cmbDatabaseDriver->currentIndex()).toString(); + // Save SQLite. + settings()->setValue(GROUP(Database), Database::UseInMemory, new_inmemory); + + if (QSqlDatabase::isDriverAvailable(APP_DB_MYSQL_DRIVER)) { + // Save MySQL. + settings()->setValue(GROUP(Database), Database::MySQLHostname, m_ui->m_txtMysqlHostname->lineEdit()->text()); + settings()->setValue(GROUP(Database), Database::MySQLUsername, m_ui->m_txtMysqlUsername->lineEdit()->text()); + settings()->setValue(GROUP(Database), Database::MySQLPassword, TextFactory::encrypt(m_ui->m_txtMysqlPassword->lineEdit()->text())); + settings()->setValue(GROUP(Database), Database::MySQLDatabase, m_ui->m_txtMysqlDatabase->lineEdit()->text()); + settings()->setValue(GROUP(Database), Database::MySQLPort, m_ui->m_spinMysqlPort->value()); + } + + settings()->setValue(GROUP(Database), Database::ActiveDriver, selected_db_driver); + + if (original_db_driver != selected_db_driver || original_inmemory != new_inmemory) { + requireRestart(); + } + + onEndSaveSettings(); +} diff --git a/src/gui/settings/settingsdatabase.h b/src/gui/settings/settingsdatabase.h index 7c4cf5748..f6c4655f6 100755 --- a/src/gui/settings/settingsdatabase.h +++ b/src/gui/settings/settingsdatabase.h @@ -1,52 +1,52 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SETTINGSDATABASE_H -#define SETTINGSDATABASE_H - -#include "gui/settings/settingspanel.h" - -#include "ui_settingsdatabase.h" - - -class SettingsDatabase : public SettingsPanel { - Q_OBJECT - - public: - explicit SettingsDatabase(Settings *settings, QWidget *parent = 0); - virtual ~SettingsDatabase(); - - inline QString title() const { - return tr("Data storage"); - } - - void loadSettings(); - void saveSettings(); - - private: - void mysqlTestConnection(); - void onMysqlHostnameChanged(const QString &new_hostname); - void onMysqlUsernameChanged(const QString &new_username); - void onMysqlPasswordChanged(const QString &new_password); - void onMysqlDatabaseChanged(const QString &new_database); - void selectSqlBackend(int index); - void switchMysqlPasswordVisiblity(bool visible); - - Ui::SettingsDatabase *m_ui; -}; - -#endif // SETTINGSDATABASE_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SETTINGSDATABASE_H +#define SETTINGSDATABASE_H + +#include "gui/settings/settingspanel.h" + +#include "ui_settingsdatabase.h" + + +class SettingsDatabase : public SettingsPanel { + Q_OBJECT + + public: + explicit SettingsDatabase(Settings* settings, QWidget* parent = 0); + virtual ~SettingsDatabase(); + + inline QString title() const { + return tr("Data storage"); + } + + void loadSettings(); + void saveSettings(); + + private: + void mysqlTestConnection(); + void onMysqlHostnameChanged(const QString& new_hostname); + void onMysqlUsernameChanged(const QString& new_username); + void onMysqlPasswordChanged(const QString& new_password); + void onMysqlDatabaseChanged(const QString& new_database); + void selectSqlBackend(int index); + void switchMysqlPasswordVisiblity(bool visible); + + Ui::SettingsDatabase* m_ui; +}; + +#endif // SETTINGSDATABASE_H diff --git a/src/gui/settings/settingsdownloads.cpp b/src/gui/settings/settingsdownloads.cpp index 32ed3c381..cfe82729e 100755 --- a/src/gui/settings/settingsdownloads.cpp +++ b/src/gui/settings/settingsdownloads.cpp @@ -1,75 +1,69 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/settings/settingsdownloads.h" - -#include "miscellaneous/settings.h" -#include "network-web/downloadmanager.h" -#include "miscellaneous/application.h" - -#include - - -SettingsDownloads::SettingsDownloads(Settings *settings, QWidget *parent) - : SettingsPanel(settings, parent), m_ui(new Ui::SettingsDownloads) { - m_ui->setupUi(this); - - connect(m_ui->m_checkOpenManagerWhenDownloadStarts, &QCheckBox::toggled, this, &SettingsDownloads::dirtifySettings); - connect(m_ui->m_txtDownloadsTargetDirectory, &QLineEdit::textChanged, this, &SettingsDownloads::dirtifySettings); - connect(m_ui->m_rbDownloadsAskEachFile, &QRadioButton::toggled, this, &SettingsDownloads::dirtifySettings); - - connect(m_ui->m_btnDownloadsTargetDirectory, &QPushButton::clicked, this, &SettingsDownloads::selectDownloadsDirectory); -} - -SettingsDownloads::~SettingsDownloads() { - delete m_ui; -} - -void SettingsDownloads::selectDownloadsDirectory() { - const QString target_directory = QFileDialog::getExistingDirectory(this, - tr("Select downloads target directory"), - m_ui->m_txtDownloadsTargetDirectory->text() - ); - - if (!target_directory.isEmpty()) { - m_ui->m_txtDownloadsTargetDirectory->setText(QDir::toNativeSeparators(target_directory)); - } -} - -void SettingsDownloads::loadSettings() { - onBeginLoadSettings(); - - m_ui->m_checkOpenManagerWhenDownloadStarts->setChecked(settings()->value(GROUP(Downloads), - SETTING(Downloads::ShowDownloadsWhenNewDownloadStarts)).toBool()); - m_ui->m_txtDownloadsTargetDirectory->setText(QDir::toNativeSeparators(settings()->value(GROUP(Downloads), - SETTING(Downloads::TargetDirectory)).toString())); - m_ui->m_rbDownloadsAskEachFile->setChecked(settings()->value(GROUP(Downloads), - SETTING(Downloads::AlwaysPromptForFilename)).toBool()); - - onEndLoadSettings(); -} - -void SettingsDownloads::saveSettings() { - onBeginSaveSettings(); - - settings()->setValue(GROUP(Downloads), Downloads::ShowDownloadsWhenNewDownloadStarts, m_ui->m_checkOpenManagerWhenDownloadStarts->isChecked()); - settings()->setValue(GROUP(Downloads), Downloads::TargetDirectory, m_ui->m_txtDownloadsTargetDirectory->text()); - settings()->setValue(GROUP(Downloads), Downloads::AlwaysPromptForFilename, m_ui->m_rbDownloadsAskEachFile->isChecked()); - qApp->downloadManager()->setDownloadDirectory(m_ui->m_txtDownloadsTargetDirectory->text()); - - onEndSaveSettings(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/settings/settingsdownloads.h" + +#include "miscellaneous/settings.h" +#include "network-web/downloadmanager.h" +#include "miscellaneous/application.h" + +#include + + +SettingsDownloads::SettingsDownloads(Settings* settings, QWidget* parent) + : SettingsPanel(settings, parent), m_ui(new Ui::SettingsDownloads) { + m_ui->setupUi(this); + connect(m_ui->m_checkOpenManagerWhenDownloadStarts, &QCheckBox::toggled, this, &SettingsDownloads::dirtifySettings); + connect(m_ui->m_txtDownloadsTargetDirectory, &QLineEdit::textChanged, this, &SettingsDownloads::dirtifySettings); + connect(m_ui->m_rbDownloadsAskEachFile, &QRadioButton::toggled, this, &SettingsDownloads::dirtifySettings); + connect(m_ui->m_btnDownloadsTargetDirectory, &QPushButton::clicked, this, &SettingsDownloads::selectDownloadsDirectory); +} + +SettingsDownloads::~SettingsDownloads() { + delete m_ui; +} + +void SettingsDownloads::selectDownloadsDirectory() { + const QString target_directory = QFileDialog::getExistingDirectory(this, + tr("Select downloads target directory"), + m_ui->m_txtDownloadsTargetDirectory->text() + ); + + if (!target_directory.isEmpty()) { + m_ui->m_txtDownloadsTargetDirectory->setText(QDir::toNativeSeparators(target_directory)); + } +} + +void SettingsDownloads::loadSettings() { + onBeginLoadSettings(); + m_ui->m_checkOpenManagerWhenDownloadStarts->setChecked(settings()->value(GROUP(Downloads), + SETTING(Downloads::ShowDownloadsWhenNewDownloadStarts)).toBool()); + m_ui->m_txtDownloadsTargetDirectory->setText(QDir::toNativeSeparators(settings()->value(GROUP(Downloads), + SETTING(Downloads::TargetDirectory)).toString())); + m_ui->m_rbDownloadsAskEachFile->setChecked(settings()->value(GROUP(Downloads), + SETTING(Downloads::AlwaysPromptForFilename)).toBool()); + onEndLoadSettings(); +} + +void SettingsDownloads::saveSettings() { + onBeginSaveSettings(); + settings()->setValue(GROUP(Downloads), Downloads::ShowDownloadsWhenNewDownloadStarts, m_ui->m_checkOpenManagerWhenDownloadStarts->isChecked()); + settings()->setValue(GROUP(Downloads), Downloads::TargetDirectory, m_ui->m_txtDownloadsTargetDirectory->text()); + settings()->setValue(GROUP(Downloads), Downloads::AlwaysPromptForFilename, m_ui->m_rbDownloadsAskEachFile->isChecked()); + qApp->downloadManager()->setDownloadDirectory(m_ui->m_txtDownloadsTargetDirectory->text()); + onEndSaveSettings(); +} diff --git a/src/gui/settings/settingsdownloads.h b/src/gui/settings/settingsdownloads.h index 3a3a92fcd..91390fa65 100755 --- a/src/gui/settings/settingsdownloads.h +++ b/src/gui/settings/settingsdownloads.h @@ -1,47 +1,47 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SETTINGSDOWNLOADS_H -#define SETTINGSDOWNLOADS_H - -#include "gui/settings/settingspanel.h" - -#include "ui_settingsdownloads.h" - - -class SettingsDownloads : public SettingsPanel { - Q_OBJECT - - public: - explicit SettingsDownloads(Settings *settings, QWidget *parent = 0); - virtual ~SettingsDownloads(); - - inline QString title() const { - return tr("Downloads"); - } - - void loadSettings(); - void saveSettings(); - - private slots: - void selectDownloadsDirectory(); - - private: - Ui::SettingsDownloads *m_ui; -}; - -#endif // SETTINGSDOWNLOADS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SETTINGSDOWNLOADS_H +#define SETTINGSDOWNLOADS_H + +#include "gui/settings/settingspanel.h" + +#include "ui_settingsdownloads.h" + + +class SettingsDownloads : public SettingsPanel { + Q_OBJECT + + public: + explicit SettingsDownloads(Settings* settings, QWidget* parent = 0); + virtual ~SettingsDownloads(); + + inline QString title() const { + return tr("Downloads"); + } + + void loadSettings(); + void saveSettings(); + + private slots: + void selectDownloadsDirectory(); + + private: + Ui::SettingsDownloads* m_ui; +}; + +#endif // SETTINGSDOWNLOADS_H diff --git a/src/gui/settings/settingsfeedsmessages.cpp b/src/gui/settings/settingsfeedsmessages.cpp index 4d8e15891..41e6ffda6 100755 --- a/src/gui/settings/settingsfeedsmessages.cpp +++ b/src/gui/settings/settingsfeedsmessages.cpp @@ -1,150 +1,139 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/settings/settingsfeedsmessages.h" - -#include "definitions/definitions.h" -#include "miscellaneous/application.h" -#include "miscellaneous/feedreader.h" -#include "gui/dialogs/formmain.h" -#include "gui/feedmessageviewer.h" -#include "gui/feedsview.h" -#include "gui/messagesview.h" -#include "gui/timespinbox.h" -#include "gui/guiutilities.h" - -#include - - -SettingsFeedsMessages::SettingsFeedsMessages(Settings *settings, QWidget *parent) - : SettingsPanel(settings, parent), m_ui(new Ui::SettingsFeedsMessages){ - m_ui->setupUi(this); - initializeMessageDateFormats(); - - GuiUtilities::setLabelAsNotice(m_ui->label_9, false); - - connect(m_ui->m_checkAutoUpdateNotification, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings); - connect(m_ui->m_checkAutoUpdate, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings); - connect(m_ui->m_checkKeppMessagesInTheMiddle, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings); - connect(m_ui->m_checkMessagesDateTimeFormat, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings); - connect(m_ui->m_checkRemoveReadMessagesOnExit, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings); - connect(m_ui->m_checkUpdateAllFeedsOnStartup, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings); - connect(m_ui->m_spinAutoUpdateInterval, static_cast(&QDoubleSpinBox::valueChanged), - this, &SettingsFeedsMessages::dirtifySettings); - connect(m_ui->m_spinHeightImageAttachments, static_cast(&QSpinBox::valueChanged), - this, &SettingsFeedsMessages::dirtifySettings); - connect(m_ui->m_checkAutoUpdate, &QCheckBox::toggled, m_ui->m_spinAutoUpdateInterval, &TimeSpinBox::setEnabled); - connect(m_ui->m_spinFeedUpdateTimeout, static_cast(&QSpinBox::valueChanged), this, &SettingsFeedsMessages::dirtifySettings); - connect(m_ui->m_cmbMessagesDateTimeFormat, static_cast(&QComboBox::currentIndexChanged), this, &SettingsFeedsMessages::dirtifySettings); - connect(m_ui->m_cmbCountsFeedList, &QComboBox::currentTextChanged, this, &SettingsFeedsMessages::dirtifySettings); - connect(m_ui->m_cmbCountsFeedList, static_cast(&QComboBox::currentIndexChanged), this, &SettingsFeedsMessages::dirtifySettings); - - connect(m_ui->m_btnChangeMessagesFont, &QPushButton::clicked, this, &SettingsFeedsMessages::changeMessagesFont); - - if (!m_ui->m_spinFeedUpdateTimeout->suffix().startsWith(' ')) { - m_ui->m_spinFeedUpdateTimeout->setSuffix(QSL(" ") + m_ui->m_spinFeedUpdateTimeout->suffix()); - } -} - -SettingsFeedsMessages::~SettingsFeedsMessages() { - delete m_ui; -} - -void SettingsFeedsMessages::initializeMessageDateFormats() { - QStringList best_formats; best_formats << QSL("d/M/yyyy hh:mm:ss") << QSL("ddd, d. M. yy hh:mm:ss") << - QSL("yyyy-MM-dd HH:mm:ss.z") << QSL("yyyy-MM-ddThh:mm:ss") << - QSL("MMM d yyyy hh:mm:ss");; - const QLocale current_locale = qApp->localization()->loadedLocale(); - const QDateTime current_dt = QDateTime::currentDateTime(); - - foreach (const QString &format, best_formats) { - m_ui->m_cmbMessagesDateTimeFormat->addItem(current_locale.toString(current_dt, format), format); - } -} - -void SettingsFeedsMessages::changeMessagesFont() { - bool ok; - QFont new_font = QFontDialog::getFont(&ok, m_ui->m_lblMessagesFont->font(), - this, tr("Select new font for message viewer"), - QFontDialog::DontUseNativeDialog); - - if (ok) { - m_ui->m_lblMessagesFont->setFont(new_font); - dirtifySettings(); - } -} - -void SettingsFeedsMessages::loadSettings() { - onBeginLoadSettings(); - - m_ui->m_checkAutoUpdateNotification->setChecked(settings()->value(GROUP(Feeds), SETTING(Feeds::EnableAutoUpdateNotification)).toBool()); - m_ui->m_checkKeppMessagesInTheMiddle->setChecked(settings()->value(GROUP(Messages), SETTING(Messages::KeepCursorInCenter)).toBool()); - m_ui->m_checkRemoveReadMessagesOnExit->setChecked(settings()->value(GROUP(Messages), SETTING(Messages::ClearReadOnExit)).toBool()); - m_ui->m_checkAutoUpdate->setChecked(settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateEnabled)).toBool()); - m_ui->m_spinAutoUpdateInterval->setValue(settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateInterval)).toInt()); - m_ui->m_spinFeedUpdateTimeout->setValue(settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt()); - m_ui->m_checkUpdateAllFeedsOnStartup->setChecked(settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateOnStartup)).toBool()); - m_ui->m_cmbCountsFeedList->addItems(QStringList() << "(%unread)" << "[%unread]" << "%unread/%all" << "%unread-%all" << "[%unread|%all]"); - m_ui->m_cmbCountsFeedList->setEditText(settings()->value(GROUP(Feeds), SETTING(Feeds::CountFormat)).toString()); - m_ui->m_spinHeightImageAttachments->setValue(settings()->value(GROUP(Messages), SETTING(Messages::MessageHeadImageHeight)).toInt()); - - initializeMessageDateFormats(); - - m_ui->m_checkMessagesDateTimeFormat->setChecked(settings()->value(GROUP(Messages), SETTING(Messages::UseCustomDate)).toBool()); - const int index_format = m_ui->m_cmbMessagesDateTimeFormat->findData(settings()->value(GROUP(Messages), SETTING(Messages::CustomDateFormat)).toString()); - - if (index_format >= 0) { - m_ui->m_cmbMessagesDateTimeFormat->setCurrentIndex(index_format); - } - - m_ui->m_lblMessagesFont->setText(tr("Font preview")); - QFont fon; - fon.fromString(settings()->value(GROUP(Messages), - SETTING(Messages::PreviewerFontStandard)).toString()); - m_ui->m_lblMessagesFont->setFont(fon); - - onEndLoadSettings(); -} - -void SettingsFeedsMessages::saveSettings() { - onBeginSaveSettings(); - - settings()->setValue(GROUP(Feeds), Feeds::EnableAutoUpdateNotification, m_ui->m_checkAutoUpdateNotification->isChecked()); - settings()->setValue(GROUP(Messages), Messages::KeepCursorInCenter, m_ui->m_checkKeppMessagesInTheMiddle->isChecked()); - settings()->setValue(GROUP(Messages), Messages::ClearReadOnExit, m_ui->m_checkRemoveReadMessagesOnExit->isChecked()); - settings()->setValue(GROUP(Feeds), Feeds::AutoUpdateEnabled, m_ui->m_checkAutoUpdate->isChecked()); - settings()->setValue(GROUP(Feeds), Feeds::AutoUpdateInterval, m_ui->m_spinAutoUpdateInterval->value()); - settings()->setValue(GROUP(Feeds), Feeds::UpdateTimeout, m_ui->m_spinFeedUpdateTimeout->value()); - settings()->setValue(GROUP(Feeds), Feeds::FeedsUpdateOnStartup, m_ui->m_checkUpdateAllFeedsOnStartup->isChecked()); - settings()->setValue(GROUP(Feeds), Feeds::CountFormat, m_ui->m_cmbCountsFeedList->currentText()); - settings()->setValue(GROUP(Messages), Messages::UseCustomDate, m_ui->m_checkMessagesDateTimeFormat->isChecked()); - settings()->setValue(GROUP(Messages), Messages::MessageHeadImageHeight, m_ui->m_spinHeightImageAttachments->value()); - settings()->setValue(GROUP(Messages), Messages::CustomDateFormat, - m_ui->m_cmbMessagesDateTimeFormat->itemData(m_ui->m_cmbMessagesDateTimeFormat->currentIndex()).toString()); - - // Save fonts. - settings()->setValue(GROUP(Messages), Messages::PreviewerFontStandard, m_ui->m_lblMessagesFont->font().toString()); - - qApp->mainForm()->tabWidget()->feedMessageViewer()->loadMessageViewerFonts(); - - qApp->feedReader()->updateAutoUpdateStatus(); - qApp->feedReader()->feedsModel()->reloadWholeLayout(); - qApp->feedReader()->messagesModel()->updateDateFormat(); - qApp->feedReader()->messagesModel()->reloadWholeLayout(); - - onEndSaveSettings(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/settings/settingsfeedsmessages.h" + +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "miscellaneous/feedreader.h" +#include "gui/dialogs/formmain.h" +#include "gui/feedmessageviewer.h" +#include "gui/feedsview.h" +#include "gui/messagesview.h" +#include "gui/timespinbox.h" +#include "gui/guiutilities.h" + +#include + + +SettingsFeedsMessages::SettingsFeedsMessages(Settings* settings, QWidget* parent) + : SettingsPanel(settings, parent), m_ui(new Ui::SettingsFeedsMessages) { + m_ui->setupUi(this); + initializeMessageDateFormats(); + GuiUtilities::setLabelAsNotice(m_ui->label_9, false); + connect(m_ui->m_checkAutoUpdateNotification, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings); + connect(m_ui->m_checkAutoUpdate, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings); + connect(m_ui->m_checkKeppMessagesInTheMiddle, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings); + connect(m_ui->m_checkMessagesDateTimeFormat, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings); + connect(m_ui->m_checkRemoveReadMessagesOnExit, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings); + connect(m_ui->m_checkUpdateAllFeedsOnStartup, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings); + connect(m_ui->m_spinAutoUpdateInterval, static_cast(&QDoubleSpinBox::valueChanged), + this, &SettingsFeedsMessages::dirtifySettings); + connect(m_ui->m_spinHeightImageAttachments, static_cast(&QSpinBox::valueChanged), + this, &SettingsFeedsMessages::dirtifySettings); + connect(m_ui->m_checkAutoUpdate, &QCheckBox::toggled, m_ui->m_spinAutoUpdateInterval, &TimeSpinBox::setEnabled); + connect(m_ui->m_spinFeedUpdateTimeout, static_cast(&QSpinBox::valueChanged), this, &SettingsFeedsMessages::dirtifySettings); + connect(m_ui->m_cmbMessagesDateTimeFormat, static_cast(&QComboBox::currentIndexChanged), this, &SettingsFeedsMessages::dirtifySettings); + connect(m_ui->m_cmbCountsFeedList, &QComboBox::currentTextChanged, this, &SettingsFeedsMessages::dirtifySettings); + connect(m_ui->m_cmbCountsFeedList, static_cast(&QComboBox::currentIndexChanged), this, &SettingsFeedsMessages::dirtifySettings); + connect(m_ui->m_btnChangeMessagesFont, &QPushButton::clicked, this, &SettingsFeedsMessages::changeMessagesFont); + + if (!m_ui->m_spinFeedUpdateTimeout->suffix().startsWith(' ')) { + m_ui->m_spinFeedUpdateTimeout->setSuffix(QSL(" ") + m_ui->m_spinFeedUpdateTimeout->suffix()); + } +} + +SettingsFeedsMessages::~SettingsFeedsMessages() { + delete m_ui; +} + +void SettingsFeedsMessages::initializeMessageDateFormats() { + QStringList best_formats; + best_formats << QSL("d/M/yyyy hh:mm:ss") << QSL("ddd, d. M. yy hh:mm:ss") << + QSL("yyyy-MM-dd HH:mm:ss.z") << QSL("yyyy-MM-ddThh:mm:ss") << + QSL("MMM d yyyy hh:mm:ss");; + const QLocale current_locale = qApp->localization()->loadedLocale(); + const QDateTime current_dt = QDateTime::currentDateTime(); + + foreach (const QString& format, best_formats) { + m_ui->m_cmbMessagesDateTimeFormat->addItem(current_locale.toString(current_dt, format), format); + } +} + +void SettingsFeedsMessages::changeMessagesFont() { + bool ok; + QFont new_font = QFontDialog::getFont(&ok, m_ui->m_lblMessagesFont->font(), + this, tr("Select new font for message viewer"), + QFontDialog::DontUseNativeDialog); + + if (ok) { + m_ui->m_lblMessagesFont->setFont(new_font); + dirtifySettings(); + } +} + +void SettingsFeedsMessages::loadSettings() { + onBeginLoadSettings(); + m_ui->m_checkAutoUpdateNotification->setChecked(settings()->value(GROUP(Feeds), SETTING(Feeds::EnableAutoUpdateNotification)).toBool()); + m_ui->m_checkKeppMessagesInTheMiddle->setChecked(settings()->value(GROUP(Messages), SETTING(Messages::KeepCursorInCenter)).toBool()); + m_ui->m_checkRemoveReadMessagesOnExit->setChecked(settings()->value(GROUP(Messages), SETTING(Messages::ClearReadOnExit)).toBool()); + m_ui->m_checkAutoUpdate->setChecked(settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateEnabled)).toBool()); + m_ui->m_spinAutoUpdateInterval->setValue(settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateInterval)).toInt()); + m_ui->m_spinFeedUpdateTimeout->setValue(settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt()); + m_ui->m_checkUpdateAllFeedsOnStartup->setChecked(settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateOnStartup)).toBool()); + m_ui->m_cmbCountsFeedList->addItems(QStringList() << "(%unread)" << "[%unread]" << "%unread/%all" << "%unread-%all" << "[%unread|%all]"); + m_ui->m_cmbCountsFeedList->setEditText(settings()->value(GROUP(Feeds), SETTING(Feeds::CountFormat)).toString()); + m_ui->m_spinHeightImageAttachments->setValue(settings()->value(GROUP(Messages), SETTING(Messages::MessageHeadImageHeight)).toInt()); + initializeMessageDateFormats(); + m_ui->m_checkMessagesDateTimeFormat->setChecked(settings()->value(GROUP(Messages), SETTING(Messages::UseCustomDate)).toBool()); + const int index_format = m_ui->m_cmbMessagesDateTimeFormat->findData(settings()->value(GROUP(Messages), SETTING(Messages::CustomDateFormat)).toString()); + + if (index_format >= 0) { + m_ui->m_cmbMessagesDateTimeFormat->setCurrentIndex(index_format); + } + + m_ui->m_lblMessagesFont->setText(tr("Font preview")); + QFont fon; + fon.fromString(settings()->value(GROUP(Messages), + SETTING(Messages::PreviewerFontStandard)).toString()); + m_ui->m_lblMessagesFont->setFont(fon); + onEndLoadSettings(); +} + +void SettingsFeedsMessages::saveSettings() { + onBeginSaveSettings(); + settings()->setValue(GROUP(Feeds), Feeds::EnableAutoUpdateNotification, m_ui->m_checkAutoUpdateNotification->isChecked()); + settings()->setValue(GROUP(Messages), Messages::KeepCursorInCenter, m_ui->m_checkKeppMessagesInTheMiddle->isChecked()); + settings()->setValue(GROUP(Messages), Messages::ClearReadOnExit, m_ui->m_checkRemoveReadMessagesOnExit->isChecked()); + settings()->setValue(GROUP(Feeds), Feeds::AutoUpdateEnabled, m_ui->m_checkAutoUpdate->isChecked()); + settings()->setValue(GROUP(Feeds), Feeds::AutoUpdateInterval, m_ui->m_spinAutoUpdateInterval->value()); + settings()->setValue(GROUP(Feeds), Feeds::UpdateTimeout, m_ui->m_spinFeedUpdateTimeout->value()); + settings()->setValue(GROUP(Feeds), Feeds::FeedsUpdateOnStartup, m_ui->m_checkUpdateAllFeedsOnStartup->isChecked()); + settings()->setValue(GROUP(Feeds), Feeds::CountFormat, m_ui->m_cmbCountsFeedList->currentText()); + settings()->setValue(GROUP(Messages), Messages::UseCustomDate, m_ui->m_checkMessagesDateTimeFormat->isChecked()); + settings()->setValue(GROUP(Messages), Messages::MessageHeadImageHeight, m_ui->m_spinHeightImageAttachments->value()); + settings()->setValue(GROUP(Messages), Messages::CustomDateFormat, + m_ui->m_cmbMessagesDateTimeFormat->itemData(m_ui->m_cmbMessagesDateTimeFormat->currentIndex()).toString()); + // Save fonts. + settings()->setValue(GROUP(Messages), Messages::PreviewerFontStandard, m_ui->m_lblMessagesFont->font().toString()); + qApp->mainForm()->tabWidget()->feedMessageViewer()->loadMessageViewerFonts(); + qApp->feedReader()->updateAutoUpdateStatus(); + qApp->feedReader()->feedsModel()->reloadWholeLayout(); + qApp->feedReader()->messagesModel()->updateDateFormat(); + qApp->feedReader()->messagesModel()->reloadWholeLayout(); + onEndSaveSettings(); +} diff --git a/src/gui/settings/settingsfeedsmessages.h b/src/gui/settings/settingsfeedsmessages.h index c76f7bbca..0da19867d 100755 --- a/src/gui/settings/settingsfeedsmessages.h +++ b/src/gui/settings/settingsfeedsmessages.h @@ -1,49 +1,49 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SETTINGSFEEDSMESSAGES_H -#define SETTINGSFEEDSMESSAGES_H - -#include "gui/settings/settingspanel.h" - -#include "ui_settingsfeedsmessages.h" - - -class SettingsFeedsMessages : public SettingsPanel { - Q_OBJECT - - public: - explicit SettingsFeedsMessages(Settings *settings, QWidget *parent = 0); - virtual ~SettingsFeedsMessages(); - - inline QString title() const { - return tr("Feeds & messages"); - } - - void loadSettings(); - void saveSettings(); - - private slots: - void changeMessagesFont(); - - private: - void initializeMessageDateFormats(); - - Ui::SettingsFeedsMessages *m_ui; -}; - -#endif // SETTINGSFEEDSMESSAGES_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SETTINGSFEEDSMESSAGES_H +#define SETTINGSFEEDSMESSAGES_H + +#include "gui/settings/settingspanel.h" + +#include "ui_settingsfeedsmessages.h" + + +class SettingsFeedsMessages : public SettingsPanel { + Q_OBJECT + + public: + explicit SettingsFeedsMessages(Settings* settings, QWidget* parent = 0); + virtual ~SettingsFeedsMessages(); + + inline QString title() const { + return tr("Feeds & messages"); + } + + void loadSettings(); + void saveSettings(); + + private slots: + void changeMessagesFont(); + + private: + void initializeMessageDateFormats(); + + Ui::SettingsFeedsMessages* m_ui; +}; + +#endif // SETTINGSFEEDSMESSAGES_H diff --git a/src/gui/settings/settingsgeneral.cpp b/src/gui/settings/settingsgeneral.cpp index 4504a0333..032732fc4 100755 --- a/src/gui/settings/settingsgeneral.cpp +++ b/src/gui/settings/settingsgeneral.cpp @@ -1,86 +1,82 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/settings/settingsgeneral.h" - -#include "miscellaneous/systemfactory.h" -#include "miscellaneous/application.h" - - -SettingsGeneral::SettingsGeneral(Settings *settings, QWidget *parent) - : SettingsPanel(settings, parent), m_ui(new Ui::SettingsGeneral) { - m_ui->setupUi(this); - m_ui->m_checkAutostart->setText(m_ui->m_checkAutostart->text().arg(APP_NAME)); - - connect(m_ui->m_checkAutostart, &QCheckBox::stateChanged, this, &SettingsGeneral::dirtifySettings); - connect(m_ui->m_checkForUpdatesOnStart, &QCheckBox::stateChanged, this, &SettingsGeneral::dirtifySettings); - connect(m_ui->m_checkRemoveTrolltechJunk, &QCheckBox::stateChanged, this, &SettingsGeneral::dirtifySettings); -} - -SettingsGeneral::~SettingsGeneral() { - delete m_ui; -} - -void SettingsGeneral::loadSettings() { - onBeginLoadSettings(); - - m_ui->m_checkForUpdatesOnStart->setChecked(settings()->value(GROUP(General), SETTING(General::UpdateOnStartup)).toBool()); - - // Load auto-start status. - const SystemFactory::AutoStartStatus autostart_status = qApp->system()->getAutoStartStatus(); - - switch (autostart_status) { - case SystemFactory::Enabled: - m_ui->m_checkAutostart->setChecked(true); - break; - - case SystemFactory::Disabled: - m_ui->m_checkAutostart->setChecked(false); - break; - - default: - m_ui->m_checkAutostart->setEnabled(false); - m_ui->m_checkAutostart->setText(m_ui->m_checkAutostart->text() + tr(" (not supported on this platform)")); - break; - } - -#if defined(Q_OS_WIN) - m_ui->m_checkRemoveTrolltechJunk->setVisible(true); - m_ui->m_checkRemoveTrolltechJunk->setChecked(settings()->value(GROUP(General), SETTING(General::RemoveTrolltechJunk)).toBool()); -#else - m_ui->m_checkRemoveTrolltechJunk->setVisible(false); -#endif - - onEndLoadSettings(); -} - -void SettingsGeneral::saveSettings() { - onBeginSaveSettings(); - - // If auto-start feature is available and user wants to turn it on, then turn it on. - if (m_ui->m_checkAutostart->isChecked()) { - qApp->system()->setAutoStartStatus(SystemFactory::Enabled); - } - else { - qApp->system()->setAutoStartStatus(SystemFactory::Disabled); - } - - settings()->setValue(GROUP(General), General::UpdateOnStartup, m_ui->m_checkForUpdatesOnStart->isChecked()); - settings()->setValue(GROUP(General), General::RemoveTrolltechJunk, m_ui->m_checkRemoveTrolltechJunk->isChecked()); - - onEndSaveSettings(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/settings/settingsgeneral.h" + +#include "miscellaneous/systemfactory.h" +#include "miscellaneous/application.h" + + +SettingsGeneral::SettingsGeneral(Settings* settings, QWidget* parent) + : SettingsPanel(settings, parent), m_ui(new Ui::SettingsGeneral) { + m_ui->setupUi(this); + m_ui->m_checkAutostart->setText(m_ui->m_checkAutostart->text().arg(APP_NAME)); + connect(m_ui->m_checkAutostart, &QCheckBox::stateChanged, this, &SettingsGeneral::dirtifySettings); + connect(m_ui->m_checkForUpdatesOnStart, &QCheckBox::stateChanged, this, &SettingsGeneral::dirtifySettings); + connect(m_ui->m_checkRemoveTrolltechJunk, &QCheckBox::stateChanged, this, &SettingsGeneral::dirtifySettings); +} + +SettingsGeneral::~SettingsGeneral() { + delete m_ui; +} + +void SettingsGeneral::loadSettings() { + onBeginLoadSettings(); + m_ui->m_checkForUpdatesOnStart->setChecked(settings()->value(GROUP(General), SETTING(General::UpdateOnStartup)).toBool()); + // Load auto-start status. + const SystemFactory::AutoStartStatus autostart_status = qApp->system()->getAutoStartStatus(); + + switch (autostart_status) { + case SystemFactory::Enabled: + m_ui->m_checkAutostart->setChecked(true); + break; + + case SystemFactory::Disabled: + m_ui->m_checkAutostart->setChecked(false); + break; + + default: + m_ui->m_checkAutostart->setEnabled(false); + m_ui->m_checkAutostart->setText(m_ui->m_checkAutostart->text() + tr(" (not supported on this platform)")); + break; + } + +#if defined(Q_OS_WIN) + m_ui->m_checkRemoveTrolltechJunk->setVisible(true); + m_ui->m_checkRemoveTrolltechJunk->setChecked(settings()->value(GROUP(General), SETTING(General::RemoveTrolltechJunk)).toBool()); +#else + m_ui->m_checkRemoveTrolltechJunk->setVisible(false); +#endif + onEndLoadSettings(); +} + +void SettingsGeneral::saveSettings() { + onBeginSaveSettings(); + + // If auto-start feature is available and user wants to turn it on, then turn it on. + if (m_ui->m_checkAutostart->isChecked()) { + qApp->system()->setAutoStartStatus(SystemFactory::Enabled); + } + + else { + qApp->system()->setAutoStartStatus(SystemFactory::Disabled); + } + + settings()->setValue(GROUP(General), General::UpdateOnStartup, m_ui->m_checkForUpdatesOnStart->isChecked()); + settings()->setValue(GROUP(General), General::RemoveTrolltechJunk, m_ui->m_checkRemoveTrolltechJunk->isChecked()); + onEndSaveSettings(); +} diff --git a/src/gui/settings/settingsgeneral.h b/src/gui/settings/settingsgeneral.h index d30c6c9ca..9b230356d 100755 --- a/src/gui/settings/settingsgeneral.h +++ b/src/gui/settings/settingsgeneral.h @@ -1,44 +1,44 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SETTINGSGENERAL_H -#define SETTINGSGENERAL_H - -#include "gui/settings/settingspanel.h" - -#include "ui_settingsgeneral.h" - - -class SettingsGeneral : public SettingsPanel { - Q_OBJECT - - public: - explicit SettingsGeneral(Settings *settings, QWidget *parent = 0); - virtual ~SettingsGeneral(); - - inline QString title() const { - return tr("General"); - } - - void loadSettings(); - void saveSettings(); - - private: - Ui::SettingsGeneral *m_ui; -}; - -#endif // SETTINGSGENERAL_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SETTINGSGENERAL_H +#define SETTINGSGENERAL_H + +#include "gui/settings/settingspanel.h" + +#include "ui_settingsgeneral.h" + + +class SettingsGeneral : public SettingsPanel { + Q_OBJECT + + public: + explicit SettingsGeneral(Settings* settings, QWidget* parent = 0); + virtual ~SettingsGeneral(); + + inline QString title() const { + return tr("General"); + } + + void loadSettings(); + void saveSettings(); + + private: + Ui::SettingsGeneral* m_ui; +}; + +#endif // SETTINGSGENERAL_H diff --git a/src/gui/settings/settingsgui.cpp b/src/gui/settings/settingsgui.cpp index cac8fb05b..a9b7bbe91 100755 --- a/src/gui/settings/settingsgui.cpp +++ b/src/gui/settings/settingsgui.cpp @@ -1,271 +1,257 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/settings/settingsgui.h" - -#include "gui/systemtrayicon.h" -#include "miscellaneous/settings.h" -#include "miscellaneous/application.h" -#include "miscellaneous/iconfactory.h" -#include "gui/dialogs/formmain.h" -#include "gui/tabwidget.h" -#include "gui/feedmessageviewer.h" -#include "gui/feedstoolbar.h" -#include "gui/messagestoolbar.h" -#include "gui/statusbar.h" - -#include -#include - - -SettingsGui::SettingsGui(Settings *settings, QWidget *parent) : SettingsPanel(settings, parent), m_ui(new Ui::SettingsGui) { - m_ui->setupUi(this); - - m_ui->m_editorMessagesToolbar->activeItemsWidget()->viewport()->installEventFilter(this); - m_ui->m_editorFeedsToolbar->activeItemsWidget()->viewport()->installEventFilter(this); - m_ui->m_editorMessagesToolbar->availableItemsWidget()->viewport()->installEventFilter(this); - m_ui->m_editorFeedsToolbar->availableItemsWidget()->viewport()->installEventFilter(this); - - m_ui->m_treeSkins->setColumnCount(4); - m_ui->m_treeSkins->setHeaderHidden(false); - m_ui->m_treeSkins->setHeaderLabels(QStringList() - << /*: Skin list name column. */ tr("Name") - << /*: Version column of skin list. */ tr("Version") - << tr("Author") - << tr("E-mail")); - - // Setup skins. - m_ui->m_treeSkins->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); - m_ui->m_treeSkins->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); - m_ui->m_treeSkins->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); - m_ui->m_treeSkins->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents); - - connect(m_ui->m_cmbIconTheme, static_cast(&QComboBox::currentIndexChanged), this, &SettingsGui::requireRestart); - connect(m_ui->m_cmbIconTheme, static_cast(&QComboBox::currentIndexChanged), this, &SettingsGui::dirtifySettings); - connect(m_ui->m_treeSkins, &QTreeWidget::currentItemChanged, this, &SettingsGui::dirtifySettings); - connect(m_ui->m_grpTray, &QGroupBox::toggled, this, &SettingsGui::dirtifySettings); - connect(m_ui->m_checkEnableNotifications, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); - connect(m_ui->m_checkHidden, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); - connect(m_ui->m_checkHideWhenMinimized, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); - connect(m_ui->m_checkHideTabBarIfOneTabVisible, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); - connect(m_ui->m_checkCloseTabsDoubleClick, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); - connect(m_ui->m_checkCloseTabsMiddleClick, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); - connect(m_ui->m_checkNewTabDoubleClick, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); - connect(m_ui->m_grbCloseTabs, &QGroupBox::toggled, this, &SettingsGui::dirtifySettings); - connect(m_ui->m_cmbToolbarButtonStyle, static_cast(&QComboBox::currentIndexChanged), this, &SettingsGui::dirtifySettings); - connect(m_ui->m_editorFeedsToolbar, &ToolBarEditor::setupChanged, this, &SettingsGui::dirtifySettings); - connect(m_ui->m_editorMessagesToolbar, &ToolBarEditor::setupChanged, this, &SettingsGui::dirtifySettings); - connect(m_ui->m_editorStatusbar, &ToolBarEditor::setupChanged, this, &SettingsGui::dirtifySettings); - connect(m_ui->m_listStyles, &QListWidget::currentItemChanged, this, &SettingsGui::dirtifySettings); - - connect(m_ui->m_cmbSelectToolBar, static_cast(&QComboBox::currentIndexChanged), m_ui->m_stackedToolbars, &QStackedWidget::setCurrentIndex); -} - -SettingsGui::~SettingsGui() { - delete m_ui; -} - -bool SettingsGui::eventFilter(QObject *obj, QEvent *e) { - Q_UNUSED(obj) - - if (e->type() == QEvent::Drop) { - QDropEvent *drop_event = static_cast(e); - - if (drop_event->keyboardModifiers() != Qt::NoModifier) { - drop_event->setDropAction(Qt::MoveAction); - } - } - - return false; -} - - -void SettingsGui::loadSettings() { - onBeginLoadSettings(); - - // Load settings of tray icon. - if (SystemTrayIcon::isSystemTrayAvailable()) { - m_ui->m_grpTray->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::UseTrayIcon)).toBool()); - } - // Tray icon is not supported on this machine. - else { - m_ui->m_grpTray->setTitle(m_ui->m_grpTray->title() + QL1C(' ') + tr("(Tray icon is not available.)")); - m_ui->m_grpTray->setChecked(false); - } - - m_ui->m_checkHidden->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::MainWindowStartsHidden)).toBool()); - m_ui->m_checkHideWhenMinimized->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::HideMainWindowWhenMinimized)).toBool()); - - // Load fancy notification settings. - m_ui->m_checkEnableNotifications->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::EnableNotifications)).toBool()); - - // Load settings of icon theme. - const QString current_theme = qApp->icons()->currentIconTheme(); - - foreach (const QString &icon_theme_name, qApp->icons()->installedIconThemes()) { - if (icon_theme_name == APP_NO_THEME) { - // Add just "no theme" on other systems. - //: Label for disabling icon theme. - m_ui->m_cmbIconTheme->addItem(tr("no icon theme/system icon theme"), APP_NO_THEME); - } - else { - m_ui->m_cmbIconTheme->addItem(icon_theme_name, icon_theme_name); - } - } - - // Mark active theme. - if (current_theme == QSL(APP_NO_THEME)) { - // Because "no icon theme" lies at the index 0. - m_ui->m_cmbIconTheme->setCurrentIndex(0); - } - else { - m_ui->m_cmbIconTheme->setCurrentText(current_theme); - } - - // Load skin. - const QString selected_skin = qApp->skins()->selectedSkinName(); - - foreach (const Skin &skin, qApp->skins()->installedSkins()) { - QTreeWidgetItem *new_item = new QTreeWidgetItem(QStringList() << - skin.m_visibleName << - skin.m_version << - skin.m_author << - skin.m_email); - new_item->setData(0, Qt::UserRole, QVariant::fromValue(skin)); - - // Add this skin and mark it as active if its active now. - m_ui->m_treeSkins->addTopLevelItem(new_item); - - if (skin.m_baseName == selected_skin) { - m_ui->m_treeSkins->setCurrentItem(new_item); - } - } - - if (m_ui->m_treeSkins->currentItem() == nullptr && - m_ui->m_treeSkins->topLevelItemCount() > 0) { - // Currently active skin is NOT available, select another one as selected - // if possible. - m_ui->m_treeSkins->setCurrentItem(m_ui->m_treeSkins->topLevelItem(0)); - } - - // Load styles. - foreach (const QString &style_name, QStyleFactory::keys()) { - m_ui->m_listStyles->addItem(style_name); - } - - QList items = m_ui->m_listStyles->findItems(settings()->value(GROUP(GUI), SETTING(GUI::Style)).toString(), - Qt::MatchFixedString); - - if (!items.isEmpty()) { - m_ui->m_listStyles->setCurrentItem(items.at(0)); - } - - // Load tab settings. - m_ui->m_checkCloseTabsMiddleClick->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::TabCloseMiddleClick)).toBool()); - m_ui->m_checkCloseTabsDoubleClick->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::TabCloseDoubleClick)).toBool()); - m_ui->m_checkNewTabDoubleClick->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::TabNewDoubleClick)).toBool()); - m_ui->m_checkHideTabBarIfOneTabVisible->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::HideTabBarIfOnlyOneTab)).toBool()); - - // Load toolbar button style. - m_ui->m_cmbToolbarButtonStyle->addItem(tr("Icon only"), Qt::ToolButtonIconOnly); - m_ui->m_cmbToolbarButtonStyle->addItem(tr("Text only"), Qt::ToolButtonTextOnly); - m_ui->m_cmbToolbarButtonStyle->addItem(tr("Text beside icon"), Qt::ToolButtonTextBesideIcon); - m_ui->m_cmbToolbarButtonStyle->addItem(tr("Text under icon"), Qt::ToolButtonTextUnderIcon); - m_ui->m_cmbToolbarButtonStyle->addItem(tr("Follow OS style"), Qt::ToolButtonFollowStyle); - - m_ui->m_cmbToolbarButtonStyle->setCurrentIndex(m_ui->m_cmbToolbarButtonStyle->findData(settings()->value(GROUP(GUI), - SETTING(GUI::ToolbarStyle)).toInt())); - - // Load toolbars. - m_ui->m_editorFeedsToolbar->loadFromToolBar(qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsToolBar()); - m_ui->m_editorMessagesToolbar->loadFromToolBar(qApp->mainForm()->tabWidget()->feedMessageViewer()->messagesToolBar()); - m_ui->m_editorStatusbar->loadFromToolBar(qApp->mainForm()->statusBar()); - - onEndLoadSettings(); -} - -void SettingsGui::saveSettings() { - onBeginSaveSettings(); - - // Save toolbar. - settings()->setValue(GROUP(GUI), GUI::ToolbarStyle, m_ui->m_cmbToolbarButtonStyle->itemData(m_ui->m_cmbToolbarButtonStyle->currentIndex())); - - // Save tray icon. - if (SystemTrayIcon::isSystemTrayAvailable()) { - settings()->setValue(GROUP(GUI), GUI::UseTrayIcon, m_ui->m_grpTray->isChecked()); - - if (m_ui->m_grpTray->isChecked()) { - qApp->showTrayIcon(); - } - else { - qApp->deleteTrayIcon(); - } - } - - settings()->setValue(GROUP(GUI), GUI::MainWindowStartsHidden, m_ui->m_checkHidden->isChecked()); - settings()->setValue(GROUP(GUI), GUI::HideMainWindowWhenMinimized, m_ui->m_checkHideWhenMinimized->isChecked()); - - // Save notifications. - settings()->setValue(GROUP(GUI), GUI::EnableNotifications, m_ui->m_checkEnableNotifications->isChecked()); - - // Save selected icon theme. - QString selected_icon_theme = m_ui->m_cmbIconTheme->itemData(m_ui->m_cmbIconTheme->currentIndex()).toString(); - QString original_icon_theme = qApp->icons()->currentIconTheme(); - qApp->icons()->setCurrentIconTheme(selected_icon_theme); - - // Check if icon theme was changed. - if (selected_icon_theme != original_icon_theme) { - requireRestart(); - } - - // Save and activate new skin. - if (!m_ui->m_treeSkins->selectedItems().isEmpty()) { - const Skin active_skin = m_ui->m_treeSkins->currentItem()->data(0, Qt::UserRole).value(); - - if (qApp->skins()->selectedSkinName() != active_skin.m_baseName) { - qApp->skins()->setCurrentSkinName(active_skin.m_baseName); - requireRestart(); - } - } - - // Set new style. - if (!m_ui->m_listStyles->selectedItems().isEmpty()) { - const QString new_style = m_ui->m_listStyles->currentItem()->text(); - const QString old_style = qApp->settings()->value(GROUP(GUI), SETTING(GUI::Style)).toString(); - - if (old_style != new_style) { - requireRestart(); - } - - qApp->settings()->setValue(GROUP(GUI), GUI::Style, new_style); - } - - // Save tab settings. - settings()->setValue(GROUP(GUI), GUI::TabCloseMiddleClick, m_ui->m_checkCloseTabsMiddleClick->isChecked()); - settings()->setValue(GROUP(GUI), GUI::TabCloseDoubleClick, m_ui->m_checkCloseTabsDoubleClick->isChecked()); - settings()->setValue(GROUP(GUI), GUI::TabNewDoubleClick, m_ui->m_checkNewTabDoubleClick->isChecked()); - settings()->setValue(GROUP(GUI), GUI::HideTabBarIfOnlyOneTab, m_ui->m_checkHideTabBarIfOneTabVisible->isChecked()); - - m_ui->m_editorFeedsToolbar->saveToolBar(); - m_ui->m_editorMessagesToolbar->saveToolBar(); - m_ui->m_editorStatusbar->saveToolBar(); - - qApp->mainForm()->tabWidget()->checkTabBarVisibility(); - qApp->mainForm()->tabWidget()->feedMessageViewer()->refreshVisualProperties(); - - onEndSaveSettings(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/settings/settingsgui.h" + +#include "gui/systemtrayicon.h" +#include "miscellaneous/settings.h" +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" +#include "gui/dialogs/formmain.h" +#include "gui/tabwidget.h" +#include "gui/feedmessageviewer.h" +#include "gui/feedstoolbar.h" +#include "gui/messagestoolbar.h" +#include "gui/statusbar.h" + +#include +#include + + +SettingsGui::SettingsGui(Settings* settings, QWidget* parent) : SettingsPanel(settings, parent), m_ui(new Ui::SettingsGui) { + m_ui->setupUi(this); + m_ui->m_editorMessagesToolbar->activeItemsWidget()->viewport()->installEventFilter(this); + m_ui->m_editorFeedsToolbar->activeItemsWidget()->viewport()->installEventFilter(this); + m_ui->m_editorMessagesToolbar->availableItemsWidget()->viewport()->installEventFilter(this); + m_ui->m_editorFeedsToolbar->availableItemsWidget()->viewport()->installEventFilter(this); + m_ui->m_treeSkins->setColumnCount(4); + m_ui->m_treeSkins->setHeaderHidden(false); + m_ui->m_treeSkins->setHeaderLabels(QStringList() + << /*: Skin list name column. */ tr("Name") + << /*: Version column of skin list. */ tr("Version") + << tr("Author") + << tr("E-mail")); + // Setup skins. + m_ui->m_treeSkins->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + m_ui->m_treeSkins->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + m_ui->m_treeSkins->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); + m_ui->m_treeSkins->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents); + connect(m_ui->m_cmbIconTheme, static_cast(&QComboBox::currentIndexChanged), this, &SettingsGui::requireRestart); + connect(m_ui->m_cmbIconTheme, static_cast(&QComboBox::currentIndexChanged), this, &SettingsGui::dirtifySettings); + connect(m_ui->m_treeSkins, &QTreeWidget::currentItemChanged, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_grpTray, &QGroupBox::toggled, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_checkEnableNotifications, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_checkHidden, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_checkHideWhenMinimized, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_checkHideTabBarIfOneTabVisible, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_checkCloseTabsDoubleClick, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_checkCloseTabsMiddleClick, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_checkNewTabDoubleClick, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_grbCloseTabs, &QGroupBox::toggled, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_cmbToolbarButtonStyle, static_cast(&QComboBox::currentIndexChanged), this, &SettingsGui::dirtifySettings); + connect(m_ui->m_editorFeedsToolbar, &ToolBarEditor::setupChanged, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_editorMessagesToolbar, &ToolBarEditor::setupChanged, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_editorStatusbar, &ToolBarEditor::setupChanged, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_listStyles, &QListWidget::currentItemChanged, this, &SettingsGui::dirtifySettings); + connect(m_ui->m_cmbSelectToolBar, static_cast(&QComboBox::currentIndexChanged), m_ui->m_stackedToolbars, &QStackedWidget::setCurrentIndex); +} + +SettingsGui::~SettingsGui() { + delete m_ui; +} + +bool SettingsGui::eventFilter(QObject* obj, QEvent* e) { + Q_UNUSED(obj) + + if (e->type() == QEvent::Drop) { + QDropEvent* drop_event = static_cast(e); + + if (drop_event->keyboardModifiers() != Qt::NoModifier) { + drop_event->setDropAction(Qt::MoveAction); + } + } + + return false; +} + + +void SettingsGui::loadSettings() { + onBeginLoadSettings(); + + // Load settings of tray icon. + if (SystemTrayIcon::isSystemTrayAvailable()) { + m_ui->m_grpTray->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::UseTrayIcon)).toBool()); + } + + // Tray icon is not supported on this machine. + else { + m_ui->m_grpTray->setTitle(m_ui->m_grpTray->title() + QL1C(' ') + tr("(Tray icon is not available.)")); + m_ui->m_grpTray->setChecked(false); + } + + m_ui->m_checkHidden->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::MainWindowStartsHidden)).toBool()); + m_ui->m_checkHideWhenMinimized->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::HideMainWindowWhenMinimized)).toBool()); + // Load fancy notification settings. + m_ui->m_checkEnableNotifications->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::EnableNotifications)).toBool()); + // Load settings of icon theme. + const QString current_theme = qApp->icons()->currentIconTheme(); + + foreach (const QString& icon_theme_name, qApp->icons()->installedIconThemes()) { + if (icon_theme_name == APP_NO_THEME) { + // Add just "no theme" on other systems. + //: Label for disabling icon theme. + m_ui->m_cmbIconTheme->addItem(tr("no icon theme/system icon theme"), APP_NO_THEME); + } + + else { + m_ui->m_cmbIconTheme->addItem(icon_theme_name, icon_theme_name); + } + } + + // Mark active theme. + if (current_theme == QSL(APP_NO_THEME)) { + // Because "no icon theme" lies at the index 0. + m_ui->m_cmbIconTheme->setCurrentIndex(0); + } + + else { + m_ui->m_cmbIconTheme->setCurrentText(current_theme); + } + + // Load skin. + const QString selected_skin = qApp->skins()->selectedSkinName(); + + foreach (const Skin& skin, qApp->skins()->installedSkins()) { + QTreeWidgetItem* new_item = new QTreeWidgetItem(QStringList() << + skin.m_visibleName << + skin.m_version << + skin.m_author << + skin.m_email); + new_item->setData(0, Qt::UserRole, QVariant::fromValue(skin)); + // Add this skin and mark it as active if its active now. + m_ui->m_treeSkins->addTopLevelItem(new_item); + + if (skin.m_baseName == selected_skin) { + m_ui->m_treeSkins->setCurrentItem(new_item); + } + } + + if (m_ui->m_treeSkins->currentItem() == nullptr && + m_ui->m_treeSkins->topLevelItemCount() > 0) { + // Currently active skin is NOT available, select another one as selected + // if possible. + m_ui->m_treeSkins->setCurrentItem(m_ui->m_treeSkins->topLevelItem(0)); + } + + // Load styles. + foreach (const QString& style_name, QStyleFactory::keys()) { + m_ui->m_listStyles->addItem(style_name); + } + + QList items = m_ui->m_listStyles->findItems(settings()->value(GROUP(GUI), SETTING(GUI::Style)).toString(), + Qt::MatchFixedString); + + if (!items.isEmpty()) { + m_ui->m_listStyles->setCurrentItem(items.at(0)); + } + + // Load tab settings. + m_ui->m_checkCloseTabsMiddleClick->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::TabCloseMiddleClick)).toBool()); + m_ui->m_checkCloseTabsDoubleClick->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::TabCloseDoubleClick)).toBool()); + m_ui->m_checkNewTabDoubleClick->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::TabNewDoubleClick)).toBool()); + m_ui->m_checkHideTabBarIfOneTabVisible->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::HideTabBarIfOnlyOneTab)).toBool()); + // Load toolbar button style. + m_ui->m_cmbToolbarButtonStyle->addItem(tr("Icon only"), Qt::ToolButtonIconOnly); + m_ui->m_cmbToolbarButtonStyle->addItem(tr("Text only"), Qt::ToolButtonTextOnly); + m_ui->m_cmbToolbarButtonStyle->addItem(tr("Text beside icon"), Qt::ToolButtonTextBesideIcon); + m_ui->m_cmbToolbarButtonStyle->addItem(tr("Text under icon"), Qt::ToolButtonTextUnderIcon); + m_ui->m_cmbToolbarButtonStyle->addItem(tr("Follow OS style"), Qt::ToolButtonFollowStyle); + m_ui->m_cmbToolbarButtonStyle->setCurrentIndex(m_ui->m_cmbToolbarButtonStyle->findData(settings()->value(GROUP(GUI), + SETTING(GUI::ToolbarStyle)).toInt())); + // Load toolbars. + m_ui->m_editorFeedsToolbar->loadFromToolBar(qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsToolBar()); + m_ui->m_editorMessagesToolbar->loadFromToolBar(qApp->mainForm()->tabWidget()->feedMessageViewer()->messagesToolBar()); + m_ui->m_editorStatusbar->loadFromToolBar(qApp->mainForm()->statusBar()); + onEndLoadSettings(); +} + +void SettingsGui::saveSettings() { + onBeginSaveSettings(); + // Save toolbar. + settings()->setValue(GROUP(GUI), GUI::ToolbarStyle, m_ui->m_cmbToolbarButtonStyle->itemData(m_ui->m_cmbToolbarButtonStyle->currentIndex())); + + // Save tray icon. + if (SystemTrayIcon::isSystemTrayAvailable()) { + settings()->setValue(GROUP(GUI), GUI::UseTrayIcon, m_ui->m_grpTray->isChecked()); + + if (m_ui->m_grpTray->isChecked()) { + qApp->showTrayIcon(); + } + + else { + qApp->deleteTrayIcon(); + } + } + + settings()->setValue(GROUP(GUI), GUI::MainWindowStartsHidden, m_ui->m_checkHidden->isChecked()); + settings()->setValue(GROUP(GUI), GUI::HideMainWindowWhenMinimized, m_ui->m_checkHideWhenMinimized->isChecked()); + // Save notifications. + settings()->setValue(GROUP(GUI), GUI::EnableNotifications, m_ui->m_checkEnableNotifications->isChecked()); + // Save selected icon theme. + QString selected_icon_theme = m_ui->m_cmbIconTheme->itemData(m_ui->m_cmbIconTheme->currentIndex()).toString(); + QString original_icon_theme = qApp->icons()->currentIconTheme(); + qApp->icons()->setCurrentIconTheme(selected_icon_theme); + + // Check if icon theme was changed. + if (selected_icon_theme != original_icon_theme) { + requireRestart(); + } + + // Save and activate new skin. + if (!m_ui->m_treeSkins->selectedItems().isEmpty()) { + const Skin active_skin = m_ui->m_treeSkins->currentItem()->data(0, Qt::UserRole).value(); + + if (qApp->skins()->selectedSkinName() != active_skin.m_baseName) { + qApp->skins()->setCurrentSkinName(active_skin.m_baseName); + requireRestart(); + } + } + + // Set new style. + if (!m_ui->m_listStyles->selectedItems().isEmpty()) { + const QString new_style = m_ui->m_listStyles->currentItem()->text(); + const QString old_style = qApp->settings()->value(GROUP(GUI), SETTING(GUI::Style)).toString(); + + if (old_style != new_style) { + requireRestart(); + } + + qApp->settings()->setValue(GROUP(GUI), GUI::Style, new_style); + } + + // Save tab settings. + settings()->setValue(GROUP(GUI), GUI::TabCloseMiddleClick, m_ui->m_checkCloseTabsMiddleClick->isChecked()); + settings()->setValue(GROUP(GUI), GUI::TabCloseDoubleClick, m_ui->m_checkCloseTabsDoubleClick->isChecked()); + settings()->setValue(GROUP(GUI), GUI::TabNewDoubleClick, m_ui->m_checkNewTabDoubleClick->isChecked()); + settings()->setValue(GROUP(GUI), GUI::HideTabBarIfOnlyOneTab, m_ui->m_checkHideTabBarIfOneTabVisible->isChecked()); + m_ui->m_editorFeedsToolbar->saveToolBar(); + m_ui->m_editorMessagesToolbar->saveToolBar(); + m_ui->m_editorStatusbar->saveToolBar(); + qApp->mainForm()->tabWidget()->checkTabBarVisibility(); + qApp->mainForm()->tabWidget()->feedMessageViewer()->refreshVisualProperties(); + onEndSaveSettings(); +} diff --git a/src/gui/settings/settingsgui.h b/src/gui/settings/settingsgui.h index 74b2c8d35..0da52c753 100755 --- a/src/gui/settings/settingsgui.h +++ b/src/gui/settings/settingsgui.h @@ -1,48 +1,48 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SETTINGSGUI_H -#define SETTINGSGUI_H - -#include "gui/settings/settingspanel.h" - -#include "ui_settingsgui.h" - - -class SettingsGui : public SettingsPanel { - Q_OBJECT - - public: - explicit SettingsGui(Settings *settings, QWidget *parent = 0); - virtual ~SettingsGui(); - - inline QString title() const { - return tr("User interface"); - } - - void loadSettings(); - void saveSettings(); - - protected: - // Does check of controls before dialog can be submitted. - bool eventFilter(QObject *obj, QEvent *e); - - private: - Ui::SettingsGui *m_ui; -}; - -#endif // SETTINGSGUI_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SETTINGSGUI_H +#define SETTINGSGUI_H + +#include "gui/settings/settingspanel.h" + +#include "ui_settingsgui.h" + + +class SettingsGui : public SettingsPanel { + Q_OBJECT + + public: + explicit SettingsGui(Settings* settings, QWidget* parent = 0); + virtual ~SettingsGui(); + + inline QString title() const { + return tr("User interface"); + } + + void loadSettings(); + void saveSettings(); + + protected: + // Does check of controls before dialog can be submitted. + bool eventFilter(QObject* obj, QEvent* e); + + private: + Ui::SettingsGui* m_ui; +}; + +#endif // SETTINGSGUI_H diff --git a/src/gui/settings/settingslocalization.cpp b/src/gui/settings/settingslocalization.cpp index c961449ef..140a6de7f 100755 --- a/src/gui/settings/settingslocalization.cpp +++ b/src/gui/settings/settingslocalization.cpp @@ -1,90 +1,86 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/settings/settingslocalization.h" - -#include "miscellaneous/localization.h" -#include "miscellaneous/settings.h" -#include "miscellaneous/application.h" -#include "miscellaneous/iconfactory.h" - - -SettingsLocalization::SettingsLocalization(Settings *settings, QWidget *parent) - : SettingsPanel(settings, parent), m_ui(new Ui::SettingsLocalization) { - m_ui->setupUi(this); - - m_ui->m_treeLanguages->setColumnCount(3); - m_ui->m_treeLanguages->setHeaderHidden(false); - m_ui->m_treeLanguages->setHeaderLabels(QStringList() - << /*: Language column of language list. */ tr("Language") - << /*: Lang. code column of language list. */ tr("Code") - << tr("Author")); - - // Setup languages. - m_ui->m_treeLanguages->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); - m_ui->m_treeLanguages->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); - m_ui->m_treeLanguages->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); - - connect(m_ui->m_treeLanguages, &QTreeWidget::currentItemChanged, this, &SettingsLocalization::requireRestart); - connect(m_ui->m_treeLanguages, &QTreeWidget::currentItemChanged, this, &SettingsLocalization::dirtifySettings); -} - -SettingsLocalization::~SettingsLocalization() { - delete m_ui; -} - -void SettingsLocalization::loadSettings() { - onBeginLoadSettings(); - - foreach (const Language &language, qApp->localization()->installedLanguages()) { - QTreeWidgetItem *item = new QTreeWidgetItem(m_ui->m_treeLanguages); - item->setText(0, language.m_name); - item->setText(1, language.m_code); - item->setText(2, language.m_author); - item->setIcon(0, qApp->icons()->miscIcon(QString(FLAG_ICON_SUBFOLDER) + QDir::separator() + language.m_code)); - } - - m_ui->m_treeLanguages->sortByColumn(0, Qt::AscendingOrder); - - QList matching_items = m_ui->m_treeLanguages->findItems(qApp->localization()->loadedLanguage(), Qt::MatchContains, 1); - - if (!matching_items.isEmpty()) { - m_ui->m_treeLanguages->setCurrentItem(matching_items[0]); - } - - onEndLoadSettings(); -} - -void SettingsLocalization::saveSettings() { - onBeginSaveSettings(); - - if (m_ui->m_treeLanguages->currentItem() == nullptr) { - qDebug("No localizations loaded in settings dialog, so no saving for them."); - return; - } - - const QString actual_lang = qApp->localization()->loadedLanguage(); - const QString new_lang = m_ui->m_treeLanguages->currentItem()->text(1); - - // Save prompt for restart if language has changed. - if (new_lang != actual_lang) { - requireRestart(); - settings()->setValue(GROUP(General), General::Language, new_lang); - } - - onEndSaveSettings(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/settings/settingslocalization.h" + +#include "miscellaneous/localization.h" +#include "miscellaneous/settings.h" +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" + + +SettingsLocalization::SettingsLocalization(Settings* settings, QWidget* parent) + : SettingsPanel(settings, parent), m_ui(new Ui::SettingsLocalization) { + m_ui->setupUi(this); + m_ui->m_treeLanguages->setColumnCount(3); + m_ui->m_treeLanguages->setHeaderHidden(false); + m_ui->m_treeLanguages->setHeaderLabels(QStringList() + << /*: Language column of language list. */ tr("Language") + << /*: Lang. code column of language list. */ tr("Code") + << tr("Author")); + // Setup languages. + m_ui->m_treeLanguages->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + m_ui->m_treeLanguages->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + m_ui->m_treeLanguages->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); + connect(m_ui->m_treeLanguages, &QTreeWidget::currentItemChanged, this, &SettingsLocalization::requireRestart); + connect(m_ui->m_treeLanguages, &QTreeWidget::currentItemChanged, this, &SettingsLocalization::dirtifySettings); +} + +SettingsLocalization::~SettingsLocalization() { + delete m_ui; +} + +void SettingsLocalization::loadSettings() { + onBeginLoadSettings(); + + foreach (const Language& language, qApp->localization()->installedLanguages()) { + QTreeWidgetItem* item = new QTreeWidgetItem(m_ui->m_treeLanguages); + item->setText(0, language.m_name); + item->setText(1, language.m_code); + item->setText(2, language.m_author); + item->setIcon(0, qApp->icons()->miscIcon(QString(FLAG_ICON_SUBFOLDER) + QDir::separator() + language.m_code)); + } + + m_ui->m_treeLanguages->sortByColumn(0, Qt::AscendingOrder); + QList matching_items = m_ui->m_treeLanguages->findItems(qApp->localization()->loadedLanguage(), Qt::MatchContains, 1); + + if (!matching_items.isEmpty()) { + m_ui->m_treeLanguages->setCurrentItem(matching_items[0]); + } + + onEndLoadSettings(); +} + +void SettingsLocalization::saveSettings() { + onBeginSaveSettings(); + + if (m_ui->m_treeLanguages->currentItem() == nullptr) { + qDebug("No localizations loaded in settings dialog, so no saving for them."); + return; + } + + const QString actual_lang = qApp->localization()->loadedLanguage(); + const QString new_lang = m_ui->m_treeLanguages->currentItem()->text(1); + + // Save prompt for restart if language has changed. + if (new_lang != actual_lang) { + requireRestart(); + settings()->setValue(GROUP(General), General::Language, new_lang); + } + + onEndSaveSettings(); +} diff --git a/src/gui/settings/settingslocalization.h b/src/gui/settings/settingslocalization.h index 55a684c2e..1ca672579 100755 --- a/src/gui/settings/settingslocalization.h +++ b/src/gui/settings/settingslocalization.h @@ -1,44 +1,44 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SETTINGSLOCALIZATION_H -#define SETTINGSLOCALIZATION_H - -#include "gui/settings/settingspanel.h" - -#include "ui_settingslocalization.h" - - -class SettingsLocalization : public SettingsPanel { - Q_OBJECT - - public: - explicit SettingsLocalization(Settings *settings, QWidget *parent = 0); - virtual ~SettingsLocalization(); - - inline QString title() const { - return tr("Language"); - } - - void loadSettings(); - void saveSettings(); - - private: - Ui::SettingsLocalization *m_ui; -}; - -#endif // SETTINGSLOCALIZATION_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SETTINGSLOCALIZATION_H +#define SETTINGSLOCALIZATION_H + +#include "gui/settings/settingspanel.h" + +#include "ui_settingslocalization.h" + + +class SettingsLocalization : public SettingsPanel { + Q_OBJECT + + public: + explicit SettingsLocalization(Settings* settings, QWidget* parent = 0); + virtual ~SettingsLocalization(); + + inline QString title() const { + return tr("Language"); + } + + void loadSettings(); + void saveSettings(); + + private: + Ui::SettingsLocalization* m_ui; +}; + +#endif // SETTINGSLOCALIZATION_H diff --git a/src/gui/settings/settingspanel.cpp b/src/gui/settings/settingspanel.cpp index 44c813084..e6969ad7e 100755 --- a/src/gui/settings/settingspanel.cpp +++ b/src/gui/settings/settingspanel.cpp @@ -1,74 +1,73 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/settings/settingspanel.h" - -#include "miscellaneous/settings.h" - - -SettingsPanel::SettingsPanel(Settings *settings, QWidget *parent) - : QWidget(parent), m_requiresRestart(false), m_isDirty(false), m_isLoading(false), m_settings(settings) { -} - -void SettingsPanel::onBeginLoadSettings() { - m_isLoading = true; -} - -void SettingsPanel::onEndLoadSettings() { - m_isLoading = false; - - setRequiresRestart(false); - setIsDirty(false); -} - -void SettingsPanel::onBeginSaveSettings() { -} - -void SettingsPanel::onEndSaveSettings() { - setIsDirty(false); -} - -void SettingsPanel::dirtifySettings() { - if (!m_isLoading) { - setIsDirty(true); - emit settingsChanged(); - } -} - -bool SettingsPanel::requiresRestart() const { - return m_requiresRestart; -} - -void SettingsPanel::setRequiresRestart(bool requiresRestart) { - m_requiresRestart = requiresRestart; -} - -void SettingsPanel::requireRestart() { - setRequiresRestart(true); -} - -bool SettingsPanel::isDirty() const { - return m_isDirty; -} - -void SettingsPanel::setIsDirty(bool is_dirty) { - m_isDirty = is_dirty; -} - -Settings *SettingsPanel::settings() const { - return m_settings; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/settings/settingspanel.h" + +#include "miscellaneous/settings.h" + + +SettingsPanel::SettingsPanel(Settings* settings, QWidget* parent) + : QWidget(parent), m_requiresRestart(false), m_isDirty(false), m_isLoading(false), m_settings(settings) { +} + +void SettingsPanel::onBeginLoadSettings() { + m_isLoading = true; +} + +void SettingsPanel::onEndLoadSettings() { + m_isLoading = false; + setRequiresRestart(false); + setIsDirty(false); +} + +void SettingsPanel::onBeginSaveSettings() { +} + +void SettingsPanel::onEndSaveSettings() { + setIsDirty(false); +} + +void SettingsPanel::dirtifySettings() { + if (!m_isLoading) { + setIsDirty(true); + emit settingsChanged(); + } +} + +bool SettingsPanel::requiresRestart() const { + return m_requiresRestart; +} + +void SettingsPanel::setRequiresRestart(bool requiresRestart) { + m_requiresRestart = requiresRestart; +} + +void SettingsPanel::requireRestart() { + setRequiresRestart(true); +} + +bool SettingsPanel::isDirty() const { + return m_isDirty; +} + +void SettingsPanel::setIsDirty(bool is_dirty) { + m_isDirty = is_dirty; +} + +Settings* SettingsPanel::settings() const { + return m_settings; +} diff --git a/src/gui/settings/settingspanel.h b/src/gui/settings/settingspanel.h index 617b4b65d..cac661df8 100755 --- a/src/gui/settings/settingspanel.h +++ b/src/gui/settings/settingspanel.h @@ -1,69 +1,69 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SETTINGSPANEL_H -#define SETTINGSPANEL_H - -#include - - -class Settings; - -class SettingsPanel : public QWidget { - Q_OBJECT - - public: - explicit SettingsPanel(Settings *settings, QWidget *parent = 0); - - virtual QString title() const = 0; - - virtual void loadSettings() = 0; - virtual void saveSettings() = 0; - - bool requiresRestart() const; - bool isDirty() const; - - void setIsDirty(bool is_dirty); - void setRequiresRestart(bool requiresRestart); - - protected: - void onBeginLoadSettings(); - void onEndLoadSettings(); - void onBeginSaveSettings(); - void onEndSaveSettings(); - - // Settings to use to save/load. - Settings *settings() const; - - protected slots: - // Sets this settings panel as dirty (some settings are changed) and emits the signal. - // NOTE: This will be probably called by subclasses when user changes some stuff. - void dirtifySettings(); - - void requireRestart(); - - signals: - void settingsChanged(); - - private: - bool m_requiresRestart; - bool m_isDirty; - bool m_isLoading; - Settings *m_settings; -}; - -#endif // SETTINGSPANEL_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SETTINGSPANEL_H +#define SETTINGSPANEL_H + +#include + + +class Settings; + +class SettingsPanel : public QWidget { + Q_OBJECT + + public: + explicit SettingsPanel(Settings* settings, QWidget* parent = 0); + + virtual QString title() const = 0; + + virtual void loadSettings() = 0; + virtual void saveSettings() = 0; + + bool requiresRestart() const; + bool isDirty() const; + + void setIsDirty(bool is_dirty); + void setRequiresRestart(bool requiresRestart); + + protected: + void onBeginLoadSettings(); + void onEndLoadSettings(); + void onBeginSaveSettings(); + void onEndSaveSettings(); + + // Settings to use to save/load. + Settings* settings() const; + + protected slots: + // Sets this settings panel as dirty (some settings are changed) and emits the signal. + // NOTE: This will be probably called by subclasses when user changes some stuff. + void dirtifySettings(); + + void requireRestart(); + + signals: + void settingsChanged(); + + private: + bool m_requiresRestart; + bool m_isDirty; + bool m_isLoading; + Settings* m_settings; +}; + +#endif // SETTINGSPANEL_H diff --git a/src/gui/settings/settingsshortcuts.cpp b/src/gui/settings/settingsshortcuts.cpp index 5cf31512f..b77f922ba 100755 --- a/src/gui/settings/settingsshortcuts.cpp +++ b/src/gui/settings/settingsshortcuts.cpp @@ -1,50 +1,45 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/settings/settingsshortcuts.h" - -#include "miscellaneous/application.h" -#include "dynamic-shortcuts/dynamicshortcuts.h" - - -SettingsShortcuts::SettingsShortcuts(Settings *settings, QWidget *parent) - : SettingsPanel(settings, parent), m_ui(new Ui::SettingsShortcuts) { - m_ui->setupUi(this); - - connect(m_ui->m_shortcuts, &DynamicShortcutsWidget::setupChanged, this, &SettingsShortcuts::dirtifySettings); -} - -SettingsShortcuts::~SettingsShortcuts() { - delete m_ui; -} - -void SettingsShortcuts::loadSettings() { - onBeginLoadSettings(); - - m_ui->m_shortcuts->populate(qApp->userActions()); - - onEndLoadSettings(); -} - -void SettingsShortcuts::saveSettings() { - onBeginSaveSettings(); - - m_ui->m_shortcuts->updateShortcuts(); - DynamicShortcuts::save(qApp->userActions()); - - onEndSaveSettings(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/settings/settingsshortcuts.h" + +#include "miscellaneous/application.h" +#include "dynamic-shortcuts/dynamicshortcuts.h" + + +SettingsShortcuts::SettingsShortcuts(Settings* settings, QWidget* parent) + : SettingsPanel(settings, parent), m_ui(new Ui::SettingsShortcuts) { + m_ui->setupUi(this); + connect(m_ui->m_shortcuts, &DynamicShortcutsWidget::setupChanged, this, &SettingsShortcuts::dirtifySettings); +} + +SettingsShortcuts::~SettingsShortcuts() { + delete m_ui; +} + +void SettingsShortcuts::loadSettings() { + onBeginLoadSettings(); + m_ui->m_shortcuts->populate(qApp->userActions()); + onEndLoadSettings(); +} + +void SettingsShortcuts::saveSettings() { + onBeginSaveSettings(); + m_ui->m_shortcuts->updateShortcuts(); + DynamicShortcuts::save(qApp->userActions()); + onEndSaveSettings(); +} diff --git a/src/gui/settings/settingsshortcuts.h b/src/gui/settings/settingsshortcuts.h index e59ea5a5e..7a87961f5 100755 --- a/src/gui/settings/settingsshortcuts.h +++ b/src/gui/settings/settingsshortcuts.h @@ -1,44 +1,44 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SETTINGSSHORTCUTS_H -#define SETTINGSSHORTCUTS_H - -#include "gui/settings/settingspanel.h" - -#include "ui_settingsshortcuts.h" - - -class SettingsShortcuts : public SettingsPanel { - Q_OBJECT - - public: - explicit SettingsShortcuts(Settings *settings, QWidget *parent = 0); - virtual ~SettingsShortcuts(); - - inline QString title() const { - return tr("Keyboard shortcuts"); - } - - void loadSettings(); - void saveSettings(); - - private: - Ui::SettingsShortcuts *m_ui; -}; - -#endif // SETTINGSSHORTCUTS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SETTINGSSHORTCUTS_H +#define SETTINGSSHORTCUTS_H + +#include "gui/settings/settingspanel.h" + +#include "ui_settingsshortcuts.h" + + +class SettingsShortcuts : public SettingsPanel { + Q_OBJECT + + public: + explicit SettingsShortcuts(Settings* settings, QWidget* parent = 0); + virtual ~SettingsShortcuts(); + + inline QString title() const { + return tr("Keyboard shortcuts"); + } + + void loadSettings(); + void saveSettings(); + + private: + Ui::SettingsShortcuts* m_ui; +}; + +#endif // SETTINGSSHORTCUTS_H diff --git a/src/gui/squeezelabel.cpp b/src/gui/squeezelabel.cpp index ec72073ae..2aef12f81 100755 --- a/src/gui/squeezelabel.cpp +++ b/src/gui/squeezelabel.cpp @@ -18,18 +18,18 @@ #include "gui/squeezelabel.h" -SqueezeLabel::SqueezeLabel(QWidget *parent) : QLabel(parent) { +SqueezeLabel::SqueezeLabel(QWidget* parent) : QLabel(parent) { } -void SqueezeLabel::paintEvent(QPaintEvent *event) { - if (m_squeezedTextCache != text()) { - m_squeezedTextCache = text(); - QFontMetrics fm = fontMetrics(); +void SqueezeLabel::paintEvent(QPaintEvent* event) { + if (m_squeezedTextCache != text()) { + m_squeezedTextCache = text(); + QFontMetrics fm = fontMetrics(); - if (fm.width(m_squeezedTextCache) > contentsRect().width()) { - setText(fm.elidedText(text(), Qt::ElideMiddle, width())); - } - } + if (fm.width(m_squeezedTextCache) > contentsRect().width()) { + setText(fm.elidedText(text(), Qt::ElideMiddle, width())); + } + } - QLabel::paintEvent(event); + QLabel::paintEvent(event); } diff --git a/src/gui/squeezelabel.h b/src/gui/squeezelabel.h index e76da0fea..b2c17027d 100755 --- a/src/gui/squeezelabel.h +++ b/src/gui/squeezelabel.h @@ -22,16 +22,16 @@ class SqueezeLabel : public QLabel { - Q_OBJECT + Q_OBJECT - public: - explicit SqueezeLabel(QWidget *parent = 0); + public: + explicit SqueezeLabel(QWidget* parent = 0); - protected: - void paintEvent(QPaintEvent *event); + protected: + void paintEvent(QPaintEvent* event); - private: - QString m_squeezedTextCache; + private: + QString m_squeezedTextCache; }; #endif // SQUEEZELABEL_H diff --git a/src/gui/statusbar.cpp b/src/gui/statusbar.cpp index 0e66dec77..1d7b4e760 100755 --- a/src/gui/statusbar.cpp +++ b/src/gui/statusbar.cpp @@ -1,284 +1,268 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/statusbar.h" - -#include "gui/dialogs/formmain.h" -#include "gui/tabwidget.h" -#include "gui/plaintoolbutton.h" -#include "miscellaneous/mutex.h" -#include "miscellaneous/iconfactory.h" - -#include -#include -#include - - -StatusBar::StatusBar(QWidget *parent) : QStatusBar(parent), m_mutex(new Mutex(QMutex::NonRecursive, this)) { - setSizeGripEnabled(false); - setContentsMargins(2, 0, 2, 2); - - m_barProgressFeeds = new QProgressBar(this); - m_barProgressFeeds->setTextVisible(false); - m_barProgressFeeds->setFixedWidth(100); - m_barProgressFeeds->setVisible(false); - m_barProgressFeeds->setObjectName(QSL("m_barProgressFeeds")); - - m_barProgressFeedsAction = new QAction(qApp->icons()->fromTheme(QSL("application-rss+xml")), tr("Feed update progress bar"), this); - m_barProgressFeedsAction->setObjectName(QSL("m_barProgressFeedsAction")); - - m_lblProgressFeeds = new QLabel(this); - m_lblProgressFeeds->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - m_lblProgressFeeds->setVisible(false); - m_lblProgressFeeds->setObjectName(QSL("m_lblProgressFeeds")); - - m_lblProgressFeedsAction = new QAction(qApp->icons()->fromTheme(QSL("application-rss+xml")), tr("Feed update label"), this); - m_lblProgressFeedsAction->setObjectName(QSL("m_lblProgressFeedsAction")); - - m_barProgressDownload = new QProgressBar(this); - m_barProgressDownload->setTextVisible(true); - m_barProgressDownload->setFixedWidth(100); - m_barProgressDownload->setVisible(false); - m_barProgressDownload->setObjectName(QSL("m_barProgressDownload")); - - m_barProgressDownloadAction = new QAction(qApp->icons()->fromTheme(QSL("emblem-downloads")), tr("File download progress bar"), this); - m_barProgressDownloadAction->setObjectName(QSL("m_barProgressDownloadAction")); - - m_lblProgressDownload = new QLabel(this); - m_lblProgressDownload->setText("Downloading files in background"); - m_lblProgressDownload->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - m_lblProgressDownload->setVisible(false); - m_lblProgressDownload->setObjectName(QSL("m_lblProgressDownload")); - - m_lblProgressDownloadAction = new QAction(qApp->icons()->fromTheme(QSL("emblem-downloads")), tr("File download label"), this); - m_lblProgressDownloadAction->setObjectName(QSL("m_lblProgressDownloadAction")); - - m_lblProgressDownload->installEventFilter(this); - m_barProgressDownload->installEventFilter(this); -} - -StatusBar::~StatusBar() { - clear(); - qDebug("Destroying StatusBar instance."); -} - -QList StatusBar::availableActions() const { - QList actions = qApp->userActions(); - - // Now, add placeholder actions for custom stuff. - actions << m_barProgressDownloadAction << m_barProgressFeedsAction << - m_lblProgressDownloadAction << m_lblProgressFeedsAction; - - return actions; -} - -QList StatusBar::changeableActions() const { - return actions(); -} - -void StatusBar::saveChangeableActions(const QStringList &actions) { - QMutexLocker locker(*m_mutex); - - qApp->settings()->setValue(GROUP(GUI), GUI::StatusbarActions, actions.join(QSL(","))); - loadSpecificActions(getSpecificActions(actions)); -} - -QStringList StatusBar::defaultActions() const { - return QString(GUI::StatusbarActionsDef).split(',', QString::SkipEmptyParts); -} - -QStringList StatusBar::savedActions() const { - return qApp->settings()->value(GROUP(GUI), SETTING(GUI::StatusbarActions)).toString().split(',', QString::SkipEmptyParts); -} - -QList StatusBar::getSpecificActions(const QStringList &actions) { - bool progress_visible = this->actions().contains(m_barProgressFeedsAction) && - m_lblProgressFeeds->isVisible() && - m_barProgressFeeds->isVisible(); - QList available_actions = availableActions(); - QList spec_actions; - - // Iterate action names and add respectable actions into the toolbar. - foreach (const QString &action_name, actions) { - QAction *matching_action = findMatchingAction(action_name, available_actions); - QAction *action_to_add; - QWidget *widget_to_add; - - if (matching_action == m_barProgressDownloadAction) { - widget_to_add = m_barProgressDownload; - action_to_add = m_barProgressDownloadAction; - - widget_to_add->setVisible(false); - } - else if (matching_action == m_barProgressFeedsAction) { - widget_to_add = m_barProgressFeeds; - action_to_add = m_barProgressFeedsAction; - - widget_to_add->setVisible(progress_visible); - } - else if (matching_action == m_lblProgressDownloadAction) { - widget_to_add = m_lblProgressDownload; - action_to_add = m_lblProgressDownloadAction; - - widget_to_add->setVisible(false); - } - else if (matching_action == m_lblProgressFeedsAction) { - widget_to_add = m_lblProgressFeeds; - action_to_add = m_lblProgressFeedsAction; - - widget_to_add->setVisible(progress_visible); - } - else { - if (action_name == SEPARATOR_ACTION_NAME) { - QLabel *lbl = new QLabel(QString::fromUtf8("•")); - widget_to_add = lbl; - - action_to_add = new QAction(this); - action_to_add->setSeparator(true); - action_to_add->setProperty("should_remove_action", true); - } - else if (action_name == SPACER_ACTION_NAME) { - QLabel *lbl = new QLabel(QSL("\t\t")); - widget_to_add = lbl; - - action_to_add = new QAction(this); - action_to_add->setProperty("should_remove_action", true); - action_to_add->setIcon(qApp->icons()->fromTheme(QSL("system-search"))); - action_to_add->setProperty("type", SPACER_ACTION_NAME); - action_to_add->setProperty("name", tr("Toolbar spacer")); - } - else if (matching_action != nullptr) { - // Add originally toolbar action. - PlainToolButton *tool_button = new PlainToolButton(this); - tool_button->reactOnActionChange(matching_action); - - widget_to_add = tool_button; - action_to_add = matching_action; - - connect(tool_button, &PlainToolButton::clicked, matching_action, &QAction::trigger); - connect(matching_action, &QAction::changed, tool_button, &PlainToolButton::reactOnSenderActionChange); - } - else { - action_to_add = nullptr; - widget_to_add = nullptr; - } - - if (action_to_add != nullptr) { - action_to_add->setProperty("should_remove_widget", true); - } - } - - if (action_to_add != nullptr && widget_to_add != nullptr) { - action_to_add->setProperty("widget", QVariant::fromValue((void*) widget_to_add)); - spec_actions.append(action_to_add); - } - } - - return spec_actions; -} - -void StatusBar::loadSpecificActions(const QList &actions) { - foreach (QAction *act, this->actions()) { - QWidget *widget = act->property("widget").isValid() ? static_cast(act->property("widget").value()) : nullptr; - - if (widget != nullptr) { - removeWidget(widget); - } - } - - removeWidget(m_barProgressDownload); - removeWidget(m_barProgressFeeds); - removeWidget(m_lblProgressDownload); - removeWidget(m_lblProgressFeeds); - clear(); - - foreach (QAction *act, actions) { - QWidget *widget = act->property("widget").isValid() ? static_cast(act->property("widget").value()) : nullptr; - - addAction(act); - - // And also add widget. - if (widget != nullptr) { - addPermanentWidget(widget); - } - } -} - -bool StatusBar::eventFilter(QObject *watched, QEvent *event) { - if (watched == m_lblProgressDownload || watched == m_barProgressDownload) { - if (event->type() == QEvent::MouseButtonPress) { - qApp->mainForm()->tabWidget()->showDownloadManager(); - } - } - - return false; -} - -void StatusBar::clear() { - while (!actions().isEmpty()) { - QAction *act = actions().at(0); - QWidget *widget = act->property("widget").isValid() ? static_cast(act->property("widget").value()) : nullptr; - bool should_remove_widget = act->property("should_remove_widget").isValid(); - bool should_remove_action = act->property("should_remove_action").isValid(); - - removeAction(act); - - if (widget != nullptr) { - removeWidget(widget); - widget->setVisible(false); - - if (should_remove_widget) { - widget->deleteLater(); - } - - if (should_remove_action) { - act->deleteLater(); - } - } - } -} - -void StatusBar::showProgressFeeds(int progress, const QString &label) { - if (actions().contains(m_barProgressFeedsAction)) { - m_lblProgressFeeds->setVisible(true); - m_barProgressFeeds->setVisible(true); - - m_lblProgressFeeds->setText(label); - m_barProgressFeeds->setValue(progress); - } -} - -void StatusBar::clearProgressFeeds() { - m_lblProgressFeeds->setVisible(false); - m_barProgressFeeds->setVisible(false); -} - -void StatusBar::showProgressDownload(int progress, const QString &tooltip) { - if (actions().contains(m_barProgressDownloadAction)) { - m_lblProgressDownload->setVisible(true); - m_barProgressDownload->setVisible(true); - m_barProgressDownload->setValue(progress); - m_barProgressDownload->setToolTip(tooltip); - m_lblProgressDownload->setToolTip(tooltip); - } -} - -void StatusBar::clearProgressDownload() { - m_lblProgressDownload->setVisible(false); - m_barProgressDownload->setVisible(false); - m_barProgressDownload->setValue(0); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/statusbar.h" + +#include "gui/dialogs/formmain.h" +#include "gui/tabwidget.h" +#include "gui/plaintoolbutton.h" +#include "miscellaneous/mutex.h" +#include "miscellaneous/iconfactory.h" + +#include +#include +#include + + +StatusBar::StatusBar(QWidget* parent) : QStatusBar(parent), m_mutex(new Mutex(QMutex::NonRecursive, this)) { + setSizeGripEnabled(false); + setContentsMargins(2, 0, 2, 2); + m_barProgressFeeds = new QProgressBar(this); + m_barProgressFeeds->setTextVisible(false); + m_barProgressFeeds->setFixedWidth(100); + m_barProgressFeeds->setVisible(false); + m_barProgressFeeds->setObjectName(QSL("m_barProgressFeeds")); + m_barProgressFeedsAction = new QAction(qApp->icons()->fromTheme(QSL("application-rss+xml")), tr("Feed update progress bar"), this); + m_barProgressFeedsAction->setObjectName(QSL("m_barProgressFeedsAction")); + m_lblProgressFeeds = new QLabel(this); + m_lblProgressFeeds->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + m_lblProgressFeeds->setVisible(false); + m_lblProgressFeeds->setObjectName(QSL("m_lblProgressFeeds")); + m_lblProgressFeedsAction = new QAction(qApp->icons()->fromTheme(QSL("application-rss+xml")), tr("Feed update label"), this); + m_lblProgressFeedsAction->setObjectName(QSL("m_lblProgressFeedsAction")); + m_barProgressDownload = new QProgressBar(this); + m_barProgressDownload->setTextVisible(true); + m_barProgressDownload->setFixedWidth(100); + m_barProgressDownload->setVisible(false); + m_barProgressDownload->setObjectName(QSL("m_barProgressDownload")); + m_barProgressDownloadAction = new QAction(qApp->icons()->fromTheme(QSL("emblem-downloads")), tr("File download progress bar"), this); + m_barProgressDownloadAction->setObjectName(QSL("m_barProgressDownloadAction")); + m_lblProgressDownload = new QLabel(this); + m_lblProgressDownload->setText("Downloading files in background"); + m_lblProgressDownload->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + m_lblProgressDownload->setVisible(false); + m_lblProgressDownload->setObjectName(QSL("m_lblProgressDownload")); + m_lblProgressDownloadAction = new QAction(qApp->icons()->fromTheme(QSL("emblem-downloads")), tr("File download label"), this); + m_lblProgressDownloadAction->setObjectName(QSL("m_lblProgressDownloadAction")); + m_lblProgressDownload->installEventFilter(this); + m_barProgressDownload->installEventFilter(this); +} + +StatusBar::~StatusBar() { + clear(); + qDebug("Destroying StatusBar instance."); +} + +QList StatusBar::availableActions() const { + QList actions = qApp->userActions(); + // Now, add placeholder actions for custom stuff. + actions << m_barProgressDownloadAction << m_barProgressFeedsAction << + m_lblProgressDownloadAction << m_lblProgressFeedsAction; + return actions; +} + +QList StatusBar::changeableActions() const { + return actions(); +} + +void StatusBar::saveChangeableActions(const QStringList& actions) { + QMutexLocker locker(*m_mutex); + qApp->settings()->setValue(GROUP(GUI), GUI::StatusbarActions, actions.join(QSL(","))); + loadSpecificActions(getSpecificActions(actions)); +} + +QStringList StatusBar::defaultActions() const { + return QString(GUI::StatusbarActionsDef).split(',', QString::SkipEmptyParts); +} + +QStringList StatusBar::savedActions() const { + return qApp->settings()->value(GROUP(GUI), SETTING(GUI::StatusbarActions)).toString().split(',', QString::SkipEmptyParts); +} + +QList StatusBar::getSpecificActions(const QStringList& actions) { + bool progress_visible = this->actions().contains(m_barProgressFeedsAction) && + m_lblProgressFeeds->isVisible() && + m_barProgressFeeds->isVisible(); + QList available_actions = availableActions(); + QList spec_actions; + + // Iterate action names and add respectable actions into the toolbar. + foreach (const QString& action_name, actions) { + QAction* matching_action = findMatchingAction(action_name, available_actions); + QAction* action_to_add; + QWidget* widget_to_add; + + if (matching_action == m_barProgressDownloadAction) { + widget_to_add = m_barProgressDownload; + action_to_add = m_barProgressDownloadAction; + widget_to_add->setVisible(false); + } + + else if (matching_action == m_barProgressFeedsAction) { + widget_to_add = m_barProgressFeeds; + action_to_add = m_barProgressFeedsAction; + widget_to_add->setVisible(progress_visible); + } + + else if (matching_action == m_lblProgressDownloadAction) { + widget_to_add = m_lblProgressDownload; + action_to_add = m_lblProgressDownloadAction; + widget_to_add->setVisible(false); + } + + else if (matching_action == m_lblProgressFeedsAction) { + widget_to_add = m_lblProgressFeeds; + action_to_add = m_lblProgressFeedsAction; + widget_to_add->setVisible(progress_visible); + } + + else { + if (action_name == SEPARATOR_ACTION_NAME) { + QLabel* lbl = new QLabel(QString::fromUtf8("•")); + widget_to_add = lbl; + action_to_add = new QAction(this); + action_to_add->setSeparator(true); + action_to_add->setProperty("should_remove_action", true); + } + + else if (action_name == SPACER_ACTION_NAME) { + QLabel* lbl = new QLabel(QSL("\t\t")); + widget_to_add = lbl; + action_to_add = new QAction(this); + action_to_add->setProperty("should_remove_action", true); + action_to_add->setIcon(qApp->icons()->fromTheme(QSL("system-search"))); + action_to_add->setProperty("type", SPACER_ACTION_NAME); + action_to_add->setProperty("name", tr("Toolbar spacer")); + } + + else if (matching_action != nullptr) { + // Add originally toolbar action. + PlainToolButton* tool_button = new PlainToolButton(this); + tool_button->reactOnActionChange(matching_action); + widget_to_add = tool_button; + action_to_add = matching_action; + connect(tool_button, &PlainToolButton::clicked, matching_action, &QAction::trigger); + connect(matching_action, &QAction::changed, tool_button, &PlainToolButton::reactOnSenderActionChange); + } + + else { + action_to_add = nullptr; + widget_to_add = nullptr; + } + + if (action_to_add != nullptr) { + action_to_add->setProperty("should_remove_widget", true); + } + } + + if (action_to_add != nullptr && widget_to_add != nullptr) { + action_to_add->setProperty("widget", QVariant::fromValue((void*) widget_to_add)); + spec_actions.append(action_to_add); + } + } + + return spec_actions; +} + +void StatusBar::loadSpecificActions(const QList& actions) { + foreach (QAction* act, this->actions()) { + QWidget* widget = act->property("widget").isValid() ? static_cast(act->property("widget").value()) : nullptr; + + if (widget != nullptr) { + removeWidget(widget); + } + } + + removeWidget(m_barProgressDownload); + removeWidget(m_barProgressFeeds); + removeWidget(m_lblProgressDownload); + removeWidget(m_lblProgressFeeds); + clear(); + + foreach (QAction* act, actions) { + QWidget* widget = act->property("widget").isValid() ? static_cast(act->property("widget").value()) : nullptr; + addAction(act); + + // And also add widget. + if (widget != nullptr) { + addPermanentWidget(widget); + } + } +} + +bool StatusBar::eventFilter(QObject* watched, QEvent* event) { + if (watched == m_lblProgressDownload || watched == m_barProgressDownload) { + if (event->type() == QEvent::MouseButtonPress) { + qApp->mainForm()->tabWidget()->showDownloadManager(); + } + } + + return false; +} + +void StatusBar::clear() { + while (!actions().isEmpty()) { + QAction* act = actions().at(0); + QWidget* widget = act->property("widget").isValid() ? static_cast(act->property("widget").value()) : nullptr; + bool should_remove_widget = act->property("should_remove_widget").isValid(); + bool should_remove_action = act->property("should_remove_action").isValid(); + removeAction(act); + + if (widget != nullptr) { + removeWidget(widget); + widget->setVisible(false); + + if (should_remove_widget) { + widget->deleteLater(); + } + + if (should_remove_action) { + act->deleteLater(); + } + } + } +} + +void StatusBar::showProgressFeeds(int progress, const QString& label) { + if (actions().contains(m_barProgressFeedsAction)) { + m_lblProgressFeeds->setVisible(true); + m_barProgressFeeds->setVisible(true); + m_lblProgressFeeds->setText(label); + m_barProgressFeeds->setValue(progress); + } +} + +void StatusBar::clearProgressFeeds() { + m_lblProgressFeeds->setVisible(false); + m_barProgressFeeds->setVisible(false); +} + +void StatusBar::showProgressDownload(int progress, const QString& tooltip) { + if (actions().contains(m_barProgressDownloadAction)) { + m_lblProgressDownload->setVisible(true); + m_barProgressDownload->setVisible(true); + m_barProgressDownload->setValue(progress); + m_barProgressDownload->setToolTip(tooltip); + m_lblProgressDownload->setToolTip(tooltip); + } +} + +void StatusBar::clearProgressDownload() { + m_lblProgressDownload->setVisible(false); + m_barProgressDownload->setVisible(false); + m_barProgressDownload->setValue(0); +} diff --git a/src/gui/statusbar.h b/src/gui/statusbar.h index 671480f20..341a54199 100755 --- a/src/gui/statusbar.h +++ b/src/gui/statusbar.h @@ -1,76 +1,76 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef STATUSBAR_H -#define STATUSBAR_H - -#include - -#include "gui/basetoolbar.h" - - -class QProgressBar; -class PlainToolButton; -class QLabel; -class Mutex; - -class StatusBar : public QStatusBar, public BaseBar { - Q_OBJECT - - public: - // Constructors and destructors. - explicit StatusBar(QWidget *parent = 0); - virtual ~StatusBar(); - - QList availableActions() const; - QList changeableActions() const; - void saveChangeableActions(const QStringList &actions); - QStringList defaultActions() const; - QStringList savedActions() const; - QList getSpecificActions(const QStringList &actions); - void loadSpecificActions(const QList &actions); - - public slots: - // Progress bar operations - void showProgressFeeds(int progress, const QString &label); - void clearProgressFeeds(); - - void showProgressDownload(int progress, const QString &tooltip); - void clearProgressDownload(); - - protected: - bool eventFilter(QObject *watched, QEvent *event); - - private: - void clear(); - - Mutex *m_mutex; - - QProgressBar *m_barProgressFeeds; - QAction *m_barProgressFeedsAction; - - QLabel *m_lblProgressFeeds; - QAction *m_lblProgressFeedsAction; - - QProgressBar *m_barProgressDownload; - QAction *m_barProgressDownloadAction; - - QLabel *m_lblProgressDownload; - QAction *m_lblProgressDownloadAction; -}; - -#endif // STATUSBAR_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef STATUSBAR_H +#define STATUSBAR_H + +#include + +#include "gui/basetoolbar.h" + + +class QProgressBar; +class PlainToolButton; +class QLabel; +class Mutex; + +class StatusBar : public QStatusBar, public BaseBar { + Q_OBJECT + + public: + // Constructors and destructors. + explicit StatusBar(QWidget* parent = 0); + virtual ~StatusBar(); + + QList availableActions() const; + QList changeableActions() const; + void saveChangeableActions(const QStringList& actions); + QStringList defaultActions() const; + QStringList savedActions() const; + QList getSpecificActions(const QStringList& actions); + void loadSpecificActions(const QList& actions); + + public slots: + // Progress bar operations + void showProgressFeeds(int progress, const QString& label); + void clearProgressFeeds(); + + void showProgressDownload(int progress, const QString& tooltip); + void clearProgressDownload(); + + protected: + bool eventFilter(QObject* watched, QEvent* event); + + private: + void clear(); + + Mutex* m_mutex; + + QProgressBar* m_barProgressFeeds; + QAction* m_barProgressFeedsAction; + + QLabel* m_lblProgressFeeds; + QAction* m_lblProgressFeedsAction; + + QProgressBar* m_barProgressDownload; + QAction* m_barProgressDownloadAction; + + QLabel* m_lblProgressDownload; + QAction* m_lblProgressDownloadAction; +}; + +#endif // STATUSBAR_H diff --git a/src/gui/styleditemdelegatewithoutfocus.cpp b/src/gui/styleditemdelegatewithoutfocus.cpp index f571fcb3f..01b3f3945 100755 --- a/src/gui/styleditemdelegatewithoutfocus.cpp +++ b/src/gui/styleditemdelegatewithoutfocus.cpp @@ -1,36 +1,36 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/styleditemdelegatewithoutfocus.h" - - -StyledItemDelegateWithoutFocus::StyledItemDelegateWithoutFocus(QObject *parent) : QStyledItemDelegate(parent) { -} - -StyledItemDelegateWithoutFocus::~StyledItemDelegateWithoutFocus() { -} - -void StyledItemDelegateWithoutFocus::paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const { - QStyleOptionViewItem itemOption(option); - - if (itemOption.state & QStyle::State_HasFocus) { - itemOption.state = itemOption.state ^ QStyle::State_HasFocus; - } - - QStyledItemDelegate::paint(painter, itemOption, index); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/styleditemdelegatewithoutfocus.h" + + +StyledItemDelegateWithoutFocus::StyledItemDelegateWithoutFocus(QObject* parent) : QStyledItemDelegate(parent) { +} + +StyledItemDelegateWithoutFocus::~StyledItemDelegateWithoutFocus() { +} + +void StyledItemDelegateWithoutFocus::paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const { + QStyleOptionViewItem itemOption(option); + + if (itemOption.state & QStyle::State_HasFocus) { + itemOption.state = itemOption.state ^ QStyle::State_HasFocus; + } + + QStyledItemDelegate::paint(painter, itemOption, index); +} diff --git a/src/gui/styleditemdelegatewithoutfocus.h b/src/gui/styleditemdelegatewithoutfocus.h index ec7474909..fe4fb0055 100755 --- a/src/gui/styleditemdelegatewithoutfocus.h +++ b/src/gui/styleditemdelegatewithoutfocus.h @@ -1,37 +1,37 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef STYLEDITEMDELEGATEWITHOUTFOCUS_H -#define STYLEDITEMDELEGATEWITHOUTFOCUS_H - -#include -#include - - -class StyledItemDelegateWithoutFocus : public QStyledItemDelegate { - Q_OBJECT - - public: - // Constructors. - explicit StyledItemDelegateWithoutFocus(QObject *parent = 0); - virtual ~StyledItemDelegateWithoutFocus(); - - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; - -}; - -#endif // STYLEDITEMDELEGATEWITHOUTFOCUS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef STYLEDITEMDELEGATEWITHOUTFOCUS_H +#define STYLEDITEMDELEGATEWITHOUTFOCUS_H + +#include +#include + + +class StyledItemDelegateWithoutFocus : public QStyledItemDelegate { + Q_OBJECT + + public: + // Constructors. + explicit StyledItemDelegateWithoutFocus(QObject* parent = 0); + virtual ~StyledItemDelegateWithoutFocus(); + + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + +}; + +#endif // STYLEDITEMDELEGATEWITHOUTFOCUS_H diff --git a/src/gui/systemtrayicon.cpp b/src/gui/systemtrayicon.cpp index 610d52b8f..81a41b1e5 100755 --- a/src/gui/systemtrayicon.cpp +++ b/src/gui/systemtrayicon.cpp @@ -1,185 +1,183 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/systemtrayicon.h" - -#include "definitions/definitions.h" -#include "miscellaneous/application.h" -#include "miscellaneous/settings.h" -#include "gui/dialogs/formmain.h" -#include "gui/dialogs/formsettings.h" - -#include -#include - - -#if defined(Q_OS_WIN) -TrayIconMenu::TrayIconMenu(const QString &title, QWidget *parent) : QMenu(title, parent) { -} - -TrayIconMenu::~TrayIconMenu() { -} - -bool TrayIconMenu::event(QEvent *event) { - if (event->type() == QEvent::Show && Application::activeModalWidget() != nullptr) { - QTimer::singleShot(0, this, SLOT(hide())); - qApp->showGuiMessage(QSL(APP_LONG_NAME), - tr("Close opened modal dialogs first."), - QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); - } - - return QMenu::event(event); -} -#endif - -SystemTrayIcon::SystemTrayIcon(const QString &normal_icon, const QString &plain_icon, FormMain *parent) - : QSystemTrayIcon(parent), - m_normalIcon(normal_icon), - m_plainPixmap(plain_icon), - m_font(QFont()), - m_bubbleClickTarget(nullptr), - m_bubbleClickSlot(nullptr) { - qDebug("Creating SystemTrayIcon instance."); - - m_font.setBold(true); - - // Initialize icon. - setNumber(); - setContextMenu(parent->trayMenu()); - - // Create necessary connections. - connect(this, &SystemTrayIcon::activated, this, &SystemTrayIcon::onActivated); -} - -SystemTrayIcon::~SystemTrayIcon() { - qDebug("Destroying SystemTrayIcon instance."); - hide(); -} - -void SystemTrayIcon::onActivated(const QSystemTrayIcon::ActivationReason &reason) { - switch (reason) { - case SystemTrayIcon::Trigger: - case SystemTrayIcon::DoubleClick: - case SystemTrayIcon::MiddleClick: - static_cast(parent())->switchVisibility(); - break; - - default: - break; - } -} - -bool SystemTrayIcon::isSystemTrayAvailable() { - return QSystemTrayIcon::isSystemTrayAvailable() && QSystemTrayIcon::supportsMessages(); -} - -bool SystemTrayIcon::isSystemTrayActivated() { - return SystemTrayIcon::isSystemTrayAvailable() && qApp->settings()->value(GROUP(GUI), SETTING(GUI::UseTrayIcon)).toBool(); -} - -bool SystemTrayIcon::areNotificationsEnabled() { - return qApp->settings()->value(GROUP(GUI), SETTING(GUI::EnableNotifications)).toBool(); -} - -void SystemTrayIcon::showPrivate() { - // Make sure that application does not exit some window (for example - // the settings window) gets closed. Behavior for main window - // is handled explicitly by FormMain::closeEvent() method. - qApp->setQuitOnLastWindowClosed(false); - - // Display the tray icon. - QSystemTrayIcon::show(); - emit shown(); - qDebug("Tray icon displayed."); -} - -void SystemTrayIcon::show() { -#if defined(Q_OS_WIN) - // Show immediately. - qDebug("Showing tray icon immediately."); - showPrivate(); -#else - // Delay avoids race conditions and tray icon is properly displayed. - qDebug("Showing tray icon with 1000 ms delay."); - QTimer::singleShot(1000, this, SLOT(showPrivate())); -#endif -} - -void SystemTrayIcon::setNumber(int number, bool any_new_message) { - if (number <= 0) { - setToolTip(QSL(APP_LONG_NAME)); - QSystemTrayIcon::setIcon(QIcon(m_normalIcon)); - } - else { - setToolTip(tr("%1\nUnread news: %2").arg(QSL(APP_LONG_NAME), QString::number(number))); - - QPixmap background(m_plainPixmap); - QPainter tray_painter; - - // FIXME: Here draw different background instead of different color of number. - tray_painter.begin(&background); - tray_painter.setPen(any_new_message ? Qt::black : Qt::black); - tray_painter.setRenderHint(QPainter::SmoothPixmapTransform, true); - tray_painter.setRenderHint(QPainter::TextAntialiasing, true); - - // Numbers with more than 2 digits won't be readable, display - // infinity symbol in that case. - if (number > 999) { - m_font.setPixelSize(100); - tray_painter.setFont(m_font); - tray_painter.drawText(QRect(0, 0, 128, 128), Qt::AlignVCenter | Qt::AlignCenter, QChar(8734)); - } - else { - // Smaller number if it has 3 digits. - if (number > 99) { - m_font.setPixelSize(55); - } - else if (number > 9) { - m_font.setPixelSize(80); - } - // Bigger number if it has just one digit. - else { - m_font.setPixelSize(100); - } - - tray_painter.setFont(m_font); - tray_painter.drawText(QRect(0, 0, 128, 128), Qt::AlignVCenter | Qt::AlignCenter, QString::number(number)); - } - - tray_painter.end(); - QSystemTrayIcon::setIcon(QIcon(background)); - } -} - -void SystemTrayIcon::showMessage(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon icon, - int milliseconds_timeout_hint, QObject *click_target, const char *click_slot) { - if (m_bubbleClickTarget != nullptr && m_bubbleClickSlot != nullptr) { - // Disconnect previous bubble click signalling. - disconnect(this, SIGNAL(messageClicked()), m_bubbleClickTarget, m_bubbleClickSlot); - } - - m_bubbleClickSlot = (char*) click_slot; - m_bubbleClickTarget = click_target; - - if (click_target != nullptr && click_slot != nullptr) { - // Establish new connection for bubble click. - connect(this, SIGNAL(messageClicked()), click_target, click_slot); - } - - // NOTE: If connections do not work, then use QMetaObject::invokeMethod(...). - QSystemTrayIcon::showMessage(title, message, icon, milliseconds_timeout_hint); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/systemtrayicon.h" + +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "miscellaneous/settings.h" +#include "gui/dialogs/formmain.h" +#include "gui/dialogs/formsettings.h" + +#include +#include + + +#if defined(Q_OS_WIN) +TrayIconMenu::TrayIconMenu(const QString& title, QWidget* parent) : QMenu(title, parent) { +} + +TrayIconMenu::~TrayIconMenu() { +} + +bool TrayIconMenu::event(QEvent* event) { + if (event->type() == QEvent::Show && Application::activeModalWidget() != nullptr) { + QTimer::singleShot(0, this, SLOT(hide())); + qApp->showGuiMessage(QSL(APP_LONG_NAME), + tr("Close opened modal dialogs first."), + QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); + } + + return QMenu::event(event); +} +#endif + +SystemTrayIcon::SystemTrayIcon(const QString& normal_icon, const QString& plain_icon, FormMain* parent) + : QSystemTrayIcon(parent), + m_normalIcon(normal_icon), + m_plainPixmap(plain_icon), + m_font(QFont()), + m_bubbleClickTarget(nullptr), + m_bubbleClickSlot(nullptr) { + qDebug("Creating SystemTrayIcon instance."); + m_font.setBold(true); + // Initialize icon. + setNumber(); + setContextMenu(parent->trayMenu()); + // Create necessary connections. + connect(this, &SystemTrayIcon::activated, this, &SystemTrayIcon::onActivated); +} + +SystemTrayIcon::~SystemTrayIcon() { + qDebug("Destroying SystemTrayIcon instance."); + hide(); +} + +void SystemTrayIcon::onActivated(const QSystemTrayIcon::ActivationReason& reason) { + switch (reason) { + case SystemTrayIcon::Trigger: + case SystemTrayIcon::DoubleClick: + case SystemTrayIcon::MiddleClick: + static_cast(parent())->switchVisibility(); + break; + + default: + break; + } +} + +bool SystemTrayIcon::isSystemTrayAvailable() { + return QSystemTrayIcon::isSystemTrayAvailable() && QSystemTrayIcon::supportsMessages(); +} + +bool SystemTrayIcon::isSystemTrayActivated() { + return SystemTrayIcon::isSystemTrayAvailable() && qApp->settings()->value(GROUP(GUI), SETTING(GUI::UseTrayIcon)).toBool(); +} + +bool SystemTrayIcon::areNotificationsEnabled() { + return qApp->settings()->value(GROUP(GUI), SETTING(GUI::EnableNotifications)).toBool(); +} + +void SystemTrayIcon::showPrivate() { + // Make sure that application does not exit some window (for example + // the settings window) gets closed. Behavior for main window + // is handled explicitly by FormMain::closeEvent() method. + qApp->setQuitOnLastWindowClosed(false); + // Display the tray icon. + QSystemTrayIcon::show(); + emit shown(); + qDebug("Tray icon displayed."); +} + +void SystemTrayIcon::show() { +#if defined(Q_OS_WIN) + // Show immediately. + qDebug("Showing tray icon immediately."); + showPrivate(); +#else + // Delay avoids race conditions and tray icon is properly displayed. + qDebug("Showing tray icon with 1000 ms delay."); + QTimer::singleShot(1000, this, SLOT(showPrivate())); +#endif +} + +void SystemTrayIcon::setNumber(int number, bool any_new_message) { + if (number <= 0) { + setToolTip(QSL(APP_LONG_NAME)); + QSystemTrayIcon::setIcon(QIcon(m_normalIcon)); + } + + else { + setToolTip(tr("%1\nUnread news: %2").arg(QSL(APP_LONG_NAME), QString::number(number))); + QPixmap background(m_plainPixmap); + QPainter tray_painter; + // FIXME: Here draw different background instead of different color of number. + tray_painter.begin(&background); + tray_painter.setPen(any_new_message ? Qt::black : Qt::black); + tray_painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + tray_painter.setRenderHint(QPainter::TextAntialiasing, true); + + // Numbers with more than 2 digits won't be readable, display + // infinity symbol in that case. + if (number > 999) { + m_font.setPixelSize(100); + tray_painter.setFont(m_font); + tray_painter.drawText(QRect(0, 0, 128, 128), Qt::AlignVCenter | Qt::AlignCenter, QChar(8734)); + } + + else { + // Smaller number if it has 3 digits. + if (number > 99) { + m_font.setPixelSize(55); + } + + else if (number > 9) { + m_font.setPixelSize(80); + } + + // Bigger number if it has just one digit. + else { + m_font.setPixelSize(100); + } + + tray_painter.setFont(m_font); + tray_painter.drawText(QRect(0, 0, 128, 128), Qt::AlignVCenter | Qt::AlignCenter, QString::number(number)); + } + + tray_painter.end(); + QSystemTrayIcon::setIcon(QIcon(background)); + } +} + +void SystemTrayIcon::showMessage(const QString& title, const QString& message, QSystemTrayIcon::MessageIcon icon, + int milliseconds_timeout_hint, QObject* click_target, const char* click_slot) { + if (m_bubbleClickTarget != nullptr && m_bubbleClickSlot != nullptr) { + // Disconnect previous bubble click signalling. + disconnect(this, SIGNAL(messageClicked()), m_bubbleClickTarget, m_bubbleClickSlot); + } + + m_bubbleClickSlot = (char*) click_slot; + m_bubbleClickTarget = click_target; + + if (click_target != nullptr && click_slot != nullptr) { + // Establish new connection for bubble click. + connect(this, SIGNAL(messageClicked()), click_target, click_slot); + } + + // NOTE: If connections do not work, then use QMetaObject::invokeMethod(...). + QSystemTrayIcon::showMessage(title, message, icon, milliseconds_timeout_hint); +} diff --git a/src/gui/systemtrayicon.h b/src/gui/systemtrayicon.h index 8f4bf3e7b..8aa6f2014 100755 --- a/src/gui/systemtrayicon.h +++ b/src/gui/systemtrayicon.h @@ -1,92 +1,92 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SYSTEMTRAYICON_H -#define SYSTEMTRAYICON_H - -#include - -#include "definitions/definitions.h" - -#include -#include - - -class FormMain; -class QEvent; - -#if defined(Q_OS_WIN) -class TrayIconMenu : public QMenu { - Q_OBJECT - - public: - // Constructors and destructors. - explicit TrayIconMenu(const QString &title, QWidget *parent); - virtual ~TrayIconMenu(); - - protected: - bool event(QEvent *event); -}; -#endif - -class SystemTrayIcon : public QSystemTrayIcon { - Q_OBJECT - - public: - // Constructors and destructors. - explicit SystemTrayIcon(const QString &normal_icon, - const QString &plain_icon, - FormMain *parent = 0); - virtual ~SystemTrayIcon(); - - // Sets the number to be visible in the tray icon, number <= 0 removes it. - void setNumber(int number = -1, bool any_new_message = false); - - void showMessage(const QString &title, const QString &message, MessageIcon icon = Information, - int milliseconds_timeout_hint = TRAY_ICON_BUBBLE_TIMEOUT, QObject *click_target = nullptr, - const char *click_slot = nullptr); - - // Returns true if tray icon CAN be constructed on this machine. - static bool isSystemTrayAvailable(); - - // Returns true if tray icon CAN be costructed and IS enabled in - // application settings. - static bool isSystemTrayActivated(); - - // Determines whether balloon tips are enabled or not on tray icons. - static bool areNotificationsEnabled(); - - public slots: - void show(); - - private slots: - void showPrivate(); - void onActivated(const QSystemTrayIcon::ActivationReason &reason); - - signals: - void shown(); - - private: - QIcon m_normalIcon; - QPixmap m_plainPixmap; - QFont m_font; - - QObject *m_bubbleClickTarget; - char *m_bubbleClickSlot; -}; - -#endif // SYSTEMTRAYICON_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SYSTEMTRAYICON_H +#define SYSTEMTRAYICON_H + +#include + +#include "definitions/definitions.h" + +#include +#include + + +class FormMain; +class QEvent; + +#if defined(Q_OS_WIN) +class TrayIconMenu : public QMenu { + Q_OBJECT + + public: + // Constructors and destructors. + explicit TrayIconMenu(const QString& title, QWidget* parent); + virtual ~TrayIconMenu(); + + protected: + bool event(QEvent* event); +}; +#endif + +class SystemTrayIcon : public QSystemTrayIcon { + Q_OBJECT + + public: + // Constructors and destructors. + explicit SystemTrayIcon(const QString& normal_icon, + const QString& plain_icon, + FormMain* parent = 0); + virtual ~SystemTrayIcon(); + + // Sets the number to be visible in the tray icon, number <= 0 removes it. + void setNumber(int number = -1, bool any_new_message = false); + + void showMessage(const QString& title, const QString& message, MessageIcon icon = Information, + int milliseconds_timeout_hint = TRAY_ICON_BUBBLE_TIMEOUT, QObject* click_target = nullptr, + const char* click_slot = nullptr); + + // Returns true if tray icon CAN be constructed on this machine. + static bool isSystemTrayAvailable(); + + // Returns true if tray icon CAN be costructed and IS enabled in + // application settings. + static bool isSystemTrayActivated(); + + // Determines whether balloon tips are enabled or not on tray icons. + static bool areNotificationsEnabled(); + + public slots: + void show(); + + private slots: + void showPrivate(); + void onActivated(const QSystemTrayIcon::ActivationReason& reason); + + signals: + void shown(); + + private: + QIcon m_normalIcon; + QPixmap m_plainPixmap; + QFont m_font; + + QObject* m_bubbleClickTarget; + char* m_bubbleClickSlot; +}; + +#endif // SYSTEMTRAYICON_H diff --git a/src/gui/tabbar.cpp b/src/gui/tabbar.cpp index de8a216ef..dc373a808 100755 --- a/src/gui/tabbar.cpp +++ b/src/gui/tabbar.cpp @@ -25,123 +25,120 @@ #include -TabBar::TabBar(QWidget *parent) : QTabBar(parent) { - setDocumentMode(false); - setUsesScrollButtons(true); - setContextMenuPolicy(Qt::CustomContextMenu); +TabBar::TabBar(QWidget* parent) : QTabBar(parent) { + setDocumentMode(false); + setUsesScrollButtons(true); + setContextMenuPolicy(Qt::CustomContextMenu); } TabBar::~TabBar() { - qDebug("Destroying TabBar instance."); + qDebug("Destroying TabBar instance."); } -void TabBar::setTabType(int index, const TabBar::TabType &type) { - const QTabBar::ButtonPosition button_position = static_cast(style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, - 0, - this)); +void TabBar::setTabType(int index, const TabBar::TabType& type) { + const QTabBar::ButtonPosition button_position = static_cast(style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, + 0, + this)); - switch (type) { - case TabBar::DownloadManager: - case TabBar::Closable: { - PlainToolButton *close_button = new PlainToolButton(this); + switch (type) { + case TabBar::DownloadManager: + case TabBar::Closable: { + PlainToolButton* close_button = new PlainToolButton(this); + close_button->setIcon(qApp->icons()->fromTheme(QSL("application-exit"))); + close_button->setToolTip(tr("Close this tab.")); + close_button->setText(tr("Close tab")); + close_button->setFixedSize(iconSize()); + // Close underlying tab when button is clicked. + connect(close_button, &PlainToolButton::clicked, this, &TabBar::closeTabViaButton); + setTabButton(index, button_position, close_button); + break; + } - close_button->setIcon(qApp->icons()->fromTheme(QSL("application-exit"))); - close_button->setToolTip(tr("Close this tab.")); - close_button->setText(tr("Close tab")); - close_button->setFixedSize(iconSize()); + case TabBar::NonClosable: + case TabBar::FeedReader: + default: + setTabButton(index, button_position, 0); + break; + } - // Close underlying tab when button is clicked. - connect(close_button, &PlainToolButton::clicked, this, &TabBar::closeTabViaButton); - setTabButton(index, button_position, close_button); - break; - } - - case TabBar::NonClosable: - case TabBar::FeedReader: - default: - setTabButton(index, button_position, 0); - break; - } - - setTabData(index, QVariant(type)); + setTabData(index, QVariant(type)); } void TabBar::closeTabViaButton() { - const QAbstractButton *close_button = qobject_cast(sender()); - const QTabBar::ButtonPosition button_position = static_cast(style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, - 0, - this)); + const QAbstractButton* close_button = qobject_cast(sender()); + const QTabBar::ButtonPosition button_position = static_cast(style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, + 0, + this)); - if (close_button != nullptr) { - // Find index of tab for this close button. - for (int i = 0; i < count(); i++) { - if (tabButton(i, button_position) == close_button) { - emit tabCloseRequested(i); - - return; - } - } - } + if (close_button != nullptr) { + // Find index of tab for this close button. + for (int i = 0; i < count(); i++) { + if (tabButton(i, button_position) == close_button) { + emit tabCloseRequested(i); + return; + } + } + } } -void TabBar::wheelEvent(QWheelEvent *event) { - const int current_index = currentIndex(); - const int number_of_tabs = count(); +void TabBar::wheelEvent(QWheelEvent* event) { + const int current_index = currentIndex(); + const int number_of_tabs = count(); - // Make sure rotating works. - if (number_of_tabs > 1) { - if (event->delta() > 0) { - // Scroll to the LEFT tab. - setCurrentIndex(current_index == 0 ? - number_of_tabs - 1 : - current_index - 1); - } - else if (event->delta() < 0) { - // Scroll to the RIGHT tab. - setCurrentIndex(current_index == number_of_tabs - 1 ? - 0 : - current_index + 1); - } - } + // Make sure rotating works. + if (number_of_tabs > 1) { + if (event->delta() > 0) { + // Scroll to the LEFT tab. + setCurrentIndex(current_index == 0 ? + number_of_tabs - 1 : + current_index - 1); + } + + else if (event->delta() < 0) { + // Scroll to the RIGHT tab. + setCurrentIndex(current_index == number_of_tabs - 1 ? + 0 : + current_index + 1); + } + } } -void TabBar::mousePressEvent(QMouseEvent *event) { - QTabBar::mousePressEvent(event); +void TabBar::mousePressEvent(QMouseEvent* event) { + QTabBar::mousePressEvent(event); + const int tab_index = tabAt(event->pos()); - const int tab_index = tabAt(event->pos()); - - // Check if user clicked on some tab or on empty space. - if (tab_index >= 0) { - // Check if user clicked tab with middle button. - // NOTE: This needs to be done here because - // destination does not know the original event. - if (event->button() & Qt::MiddleButton && qApp->settings()->value(GROUP(GUI), SETTING(GUI::TabCloseMiddleClick)).toBool()) { - if (tabType(tab_index) == TabBar::Closable || tabType(tab_index) == TabBar::DownloadManager) { - // This tab is closable, so we can close it. - emit tabCloseRequested(tab_index); - } - } - } + // Check if user clicked on some tab or on empty space. + if (tab_index >= 0) { + // Check if user clicked tab with middle button. + // NOTE: This needs to be done here because + // destination does not know the original event. + if (event->button() & Qt::MiddleButton && qApp->settings()->value(GROUP(GUI), SETTING(GUI::TabCloseMiddleClick)).toBool()) { + if (tabType(tab_index) == TabBar::Closable || tabType(tab_index) == TabBar::DownloadManager) { + // This tab is closable, so we can close it. + emit tabCloseRequested(tab_index); + } + } + } } -void TabBar::mouseDoubleClickEvent(QMouseEvent *event) { - QTabBar::mouseDoubleClickEvent(event); +void TabBar::mouseDoubleClickEvent(QMouseEvent* event) { + QTabBar::mouseDoubleClickEvent(event); + const int tab_index = tabAt(event->pos()); - const int tab_index = tabAt(event->pos()); + // Check if user clicked on some tab or on empty space. + if (tab_index >= 0) { + // Check if user clicked tab with middle button. + // NOTE: This needs to be done here because + // destination does not know the original event. + if (event->button() & Qt::LeftButton && qApp->settings()->value(GROUP(GUI), SETTING(GUI::TabCloseDoubleClick)).toBool()) { + if ((tabType(tab_index) & (TabBar::Closable | TabBar::DownloadManager)) > 0) { + // This tab is closable, so we can close it. + emit tabCloseRequested(tab_index); + } + } + } - // Check if user clicked on some tab or on empty space. - if (tab_index >= 0) { - // Check if user clicked tab with middle button. - // NOTE: This needs to be done here because - // destination does not know the original event. - if (event->button() & Qt::LeftButton && qApp->settings()->value(GROUP(GUI), SETTING(GUI::TabCloseDoubleClick)).toBool()) { - if ((tabType(tab_index) & (TabBar::Closable | TabBar::DownloadManager)) > 0) { - // This tab is closable, so we can close it. - emit tabCloseRequested(tab_index); - } - } - } - else { - emit emptySpaceDoubleClicked(); - } + else { + emit emptySpaceDoubleClicked(); + } } diff --git a/src/gui/tabbar.h b/src/gui/tabbar.h index ab66537a0..33528bd56 100755 --- a/src/gui/tabbar.h +++ b/src/gui/tabbar.h @@ -25,40 +25,40 @@ class TabBar : public QTabBar { - Q_OBJECT + Q_OBJECT - public: - enum TabType { - FeedReader = 1, - DownloadManager = 2, - NonClosable = 4, - Closable = 8 - }; + public: + enum TabType { + FeedReader = 1, + DownloadManager = 2, + NonClosable = 4, + Closable = 8 + }; - // Constructors. - explicit TabBar(QWidget *parent = 0); - virtual ~TabBar(); + // Constructors. + explicit TabBar(QWidget* parent = 0); + virtual ~TabBar(); - // Getter/setter for tab type. - void setTabType(int index, const TabBar::TabType &type); + // Getter/setter for tab type. + void setTabType(int index, const TabBar::TabType& type); - inline TabBar::TabType tabType(int index) const { - return static_cast(tabData(index).value()); - } + inline TabBar::TabType tabType(int index) const { + return static_cast(tabData(index).value()); + } - private slots: - // Called when user selects to close tab via close button. - void closeTabViaButton(); + private slots: + // Called when user selects to close tab via close button. + void closeTabViaButton(); - private: - // Reimplementations. - void mouseDoubleClickEvent(QMouseEvent *event); - void mousePressEvent(QMouseEvent *event); - void wheelEvent(QWheelEvent *event); + private: + // Reimplementations. + void mouseDoubleClickEvent(QMouseEvent* event); + void mousePressEvent(QMouseEvent* event); + void wheelEvent(QWheelEvent* event); - signals: - // Emmited if empty space on tab bar is double clicked. - void emptySpaceDoubleClicked(); + signals: + // Emmited if empty space on tab bar is double clicked. + void emptySpaceDoubleClicked(); }; #endif // TABBAR_H diff --git a/src/gui/tabcontent.cpp b/src/gui/tabcontent.cpp index 6b27a1a9b..a86ab4884 100755 --- a/src/gui/tabcontent.cpp +++ b/src/gui/tabcontent.cpp @@ -18,7 +18,7 @@ #include "gui/tabcontent.h" -TabContent::TabContent(QWidget *parent) : QWidget(parent), m_index(-1) { +TabContent::TabContent(QWidget* parent) : QWidget(parent), m_index(-1) { } TabContent::~TabContent() { diff --git a/src/gui/tabcontent.h b/src/gui/tabcontent.h index 34c988afc..13d5a1477 100755 --- a/src/gui/tabcontent.h +++ b/src/gui/tabcontent.h @@ -26,32 +26,32 @@ class WebBrowser; // Base class for all widgets which are placed inside tabs of TabWidget class TabContent : public QWidget { - Q_OBJECT + Q_OBJECT - public: - // Contructors. - explicit TabContent(QWidget *parent = 0); - virtual ~TabContent(); + public: + // Contructors. + explicit TabContent(QWidget* parent = 0); + virtual ~TabContent(); - // Gets/sets current index of this TabContent. - // NOTE: This is the index under which this object lies - // in parent tab widget. - inline virtual int index() const { - return m_index; - } + // Gets/sets current index of this TabContent. + // NOTE: This is the index under which this object lies + // in parent tab widget. + inline virtual int index() const { + return m_index; + } - inline virtual void setIndex(int index) { - m_index = index; - } + inline virtual void setIndex(int index) { + m_index = index; + } #if defined(USE_WEBENGINE) - // Obtains instance contained in this TabContent or nullptr. - // This can be used for obtaining the menu from the instance and so on. - virtual WebBrowser *webBrowser() const = 0; + // Obtains instance contained in this TabContent or nullptr. + // This can be used for obtaining the menu from the instance and so on. + virtual WebBrowser* webBrowser() const = 0; #endif - protected: - int m_index; + protected: + int m_index; }; #endif // TABCONTENT_H diff --git a/src/gui/tabwidget.cpp b/src/gui/tabwidget.cpp index dccee9cc3..2a64cb1ef 100755 --- a/src/gui/tabwidget.cpp +++ b/src/gui/tabwidget.cpp @@ -41,323 +41,310 @@ #include -TabWidget::TabWidget(QWidget *parent) : QTabWidget(parent), m_menuMain(nullptr) { - setTabBar(new TabBar(this)); - setupMainMenuButton(); - initializeTabs(); - createConnections(); +TabWidget::TabWidget(QWidget* parent) : QTabWidget(parent), m_menuMain(nullptr) { + setTabBar(new TabBar(this)); + setupMainMenuButton(); + initializeTabs(); + createConnections(); } TabWidget::~TabWidget() { - qDebug("Destroying TabWidget instance."); + qDebug("Destroying TabWidget instance."); } void TabWidget::setupMainMenuButton() { - m_btnMainMenu = new PlainToolButton(this); - m_btnMainMenu->setAutoRaise(true); - m_btnMainMenu->setPadding(3); - m_btnMainMenu->setToolTip(tr("Displays main menu.")); - m_btnMainMenu->setIcon(qApp->icons()->fromTheme(QSL("go-home"))); - m_btnMainMenu->setPopupMode(QToolButton::InstantPopup); - - connect(m_btnMainMenu, &PlainToolButton::clicked, this, &TabWidget::openMainMenu); + m_btnMainMenu = new PlainToolButton(this); + m_btnMainMenu->setAutoRaise(true); + m_btnMainMenu->setPadding(3); + m_btnMainMenu->setToolTip(tr("Displays main menu.")); + m_btnMainMenu->setIcon(qApp->icons()->fromTheme(QSL("go-home"))); + m_btnMainMenu->setPopupMode(QToolButton::InstantPopup); + connect(m_btnMainMenu, &PlainToolButton::clicked, this, &TabWidget::openMainMenu); } void TabWidget::openMainMenu() { - if (m_menuMain == nullptr) { - m_menuMain = new QMenu(tr("Main menu"), this); - m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuFile); - m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuView); - m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuAccounts); - m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuFeeds); - m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuMessages); - m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuWebBrowserTabs); - m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuTools); - m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuHelp); - } + if (m_menuMain == nullptr) { + m_menuMain = new QMenu(tr("Main menu"), this); + m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuFile); + m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuView); + m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuAccounts); + m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuFeeds); + m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuMessages); + m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuWebBrowserTabs); + m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuTools); + m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuHelp); + } - QPoint button_position = m_btnMainMenu->pos(); - const QSize target_size = m_btnMainMenu->size() / 2.0; - - button_position.setX(button_position.x() + target_size.width()); - button_position.setY(button_position.y() + target_size.height()); - - m_menuMain->exec(mapToGlobal(button_position)); + QPoint button_position = m_btnMainMenu->pos(); + const QSize target_size = m_btnMainMenu->size() / 2.0; + button_position.setX(button_position.x() + target_size.width()); + button_position.setY(button_position.y() + target_size.height()); + m_menuMain->exec(mapToGlobal(button_position)); } void TabWidget::showDownloadManager() { - for (int i = 0; i < count(); i++) { - if (widget(i)->metaObject()->className() == QSL("DownloadManager")) { - setCurrentIndex(i); - return; - } - } + for (int i = 0; i < count(); i++) { + if (widget(i)->metaObject()->className() == QSL("DownloadManager")) { + setCurrentIndex(i); + return; + } + } - // Download manager is not opened. Create tab with it. - qApp->downloadManager()->setParent(this); - addTab(qApp->downloadManager(), qApp->icons()->fromTheme(QSL("emblem-downloads")), tr("Downloads"), TabBar::DownloadManager); - setCurrentIndex(count() - 1); + // Download manager is not opened. Create tab with it. + qApp->downloadManager()->setParent(this); + addTab(qApp->downloadManager(), qApp->icons()->fromTheme(QSL("emblem-downloads")), tr("Downloads"), TabBar::DownloadManager); + setCurrentIndex(count() - 1); } void TabWidget::checkTabBarVisibility() { - const bool should_be_visible = count() > 1 || !qApp->settings()->value(GROUP(GUI), SETTING(GUI::HideTabBarIfOnlyOneTab)).toBool(); + const bool should_be_visible = count() > 1 || !qApp->settings()->value(GROUP(GUI), SETTING(GUI::HideTabBarIfOnlyOneTab)).toBool(); - if (should_be_visible) { - setCornerWidget(m_btnMainMenu, Qt::TopLeftCorner); - m_btnMainMenu->setVisible(true); - } - else { - setCornerWidget(0, Qt::TopLeftCorner); - setCornerWidget(0, Qt::TopRightCorner); - m_btnMainMenu->setVisible(false); - } + if (should_be_visible) { + setCornerWidget(m_btnMainMenu, Qt::TopLeftCorner); + m_btnMainMenu->setVisible(true); + } - tabBar()->setVisible(should_be_visible); + else { + setCornerWidget(0, Qt::TopLeftCorner); + setCornerWidget(0, Qt::TopRightCorner); + m_btnMainMenu->setVisible(false); + } + + tabBar()->setVisible(should_be_visible); } void TabWidget::tabInserted(int index) { - QTabWidget::tabInserted(index); - checkTabBarVisibility(); + QTabWidget::tabInserted(index); + checkTabBarVisibility(); + const int count_of_tabs = count(); - const int count_of_tabs = count(); - - if (index < count_of_tabs - 1 && count_of_tabs > 1) { - // New tab was inserted and the tab is not the last one. - fixContentsAfterMove(index, count_of_tabs - 1); - } + if (index < count_of_tabs - 1 && count_of_tabs > 1) { + // New tab was inserted and the tab is not the last one. + fixContentsAfterMove(index, count_of_tabs - 1); + } } void TabWidget::tabRemoved(int index) { - QTabWidget::tabRemoved(index); - checkTabBarVisibility(); + QTabWidget::tabRemoved(index); + checkTabBarVisibility(); + const int count_of_tabs = count(); - const int count_of_tabs = count(); - - if (index < count_of_tabs && count_of_tabs > 1) { - // Some tab was removed and the tab was not the last one. - fixContentsAfterMove(index, count_of_tabs - 1); - } + if (index < count_of_tabs && count_of_tabs > 1) { + // Some tab was removed and the tab was not the last one. + fixContentsAfterMove(index, count_of_tabs - 1); + } } void TabWidget::createConnections() { - connect(tabBar(), &TabBar::tabCloseRequested, this, &TabWidget::closeTab); - connect(tabBar(), &TabBar::emptySpaceDoubleClicked, this, &TabWidget::addEmptyBrowser); - connect(tabBar(), &TabBar::tabMoved, this, &TabWidget::fixContentsAfterMove); - - connect(feedMessageViewer()->messagesView(), &MessagesView::openMessagesInNewspaperView, this, &TabWidget::addNewspaperView); - connect(feedMessageViewer()->feedsView(), &FeedsView::openMessagesInNewspaperView, this, &TabWidget::addNewspaperView); + connect(tabBar(), &TabBar::tabCloseRequested, this, &TabWidget::closeTab); + connect(tabBar(), &TabBar::emptySpaceDoubleClicked, this, &TabWidget::addEmptyBrowser); + connect(tabBar(), &TabBar::tabMoved, this, &TabWidget::fixContentsAfterMove); + connect(feedMessageViewer()->messagesView(), &MessagesView::openMessagesInNewspaperView, this, &TabWidget::addNewspaperView); + connect(feedMessageViewer()->feedsView(), &FeedsView::openMessagesInNewspaperView, this, &TabWidget::addNewspaperView); } void TabWidget::initializeTabs() { - // Create widget for "Feeds" page and add it. - m_feedMessageViewer = new FeedMessageViewer(this); - const int index_of_browser = addTab(m_feedMessageViewer, QIcon(), tr("Feeds"), TabBar::FeedReader); - setTabToolTip(index_of_browser, tr("Browse your feeds and messages")); + // Create widget for "Feeds" page and add it. + m_feedMessageViewer = new FeedMessageViewer(this); + const int index_of_browser = addTab(m_feedMessageViewer, QIcon(), tr("Feeds"), TabBar::FeedReader); + setTabToolTip(index_of_browser, tr("Browse your feeds and messages")); } void TabWidget::setupIcons() { - // Iterate through all tabs and update icons - // accordingly. - for (int index = 0; index < count(); index++) { - // Index 0 usually contains widget which displays feeds & messages. - if (tabBar()->tabType(index) == TabBar::FeedReader) { - setTabIcon(index, qApp->icons()->fromTheme(QSL("application-rss+xml"))); - } - } + // Iterate through all tabs and update icons + // accordingly. + for (int index = 0; index < count(); index++) { + // Index 0 usually contains widget which displays feeds & messages. + if (tabBar()->tabType(index) == TabBar::FeedReader) { + setTabIcon(index, qApp->icons()->fromTheme(QSL("application-rss+xml"))); + } + } } bool TabWidget::closeTab(int index) { - if (tabBar()->tabType(index) == TabBar::Closable) { - removeTab(index, true); - return true; - } - else if (tabBar()->tabType(index) == TabBar::DownloadManager) { - removeTab(index, false); - return true; - } - else { - return false; - } + if (tabBar()->tabType(index) == TabBar::Closable) { + removeTab(index, true); + return true; + } + + else if (tabBar()->tabType(index) == TabBar::DownloadManager) { + removeTab(index, false); + return true; + } + + else { + return false; + } } void TabWidget::closeAllTabsExceptCurrent() { - // Close tabs after active tab. - int index_of_active = currentIndex(); + // Close tabs after active tab. + int index_of_active = currentIndex(); - for (int i = count() - 1; i >= 0; i--) { - if (i != index_of_active) { - if (i < index_of_active) { - index_of_active--; - } + for (int i = count() - 1; i >= 0; i--) { + if (i != index_of_active) { + if (i < index_of_active) { + index_of_active--; + } - closeTab(i); - } - } + closeTab(i); + } + } } void TabWidget::closeAllTabs() { - for (int i = count() - 1; i >= 0; i--) { - closeTab(i); - } + for (int i = count() - 1; i >= 0; i--) { + closeTab(i); + } } -int TabWidget::addNewspaperView(RootItem *root, const QList &messages) { +int TabWidget::addNewspaperView(RootItem* root, const QList& messages) { #if defined(USE_WEBENGINE) - WebBrowser *prev = new WebBrowser(this); + WebBrowser* prev = new WebBrowser(this); #else - NewspaperPreviewer *prev = new NewspaperPreviewer(root, messages, this); + NewspaperPreviewer* prev = new NewspaperPreviewer(root, messages, this); #endif - - int index = addTab(prev, qApp->icons()->fromTheme(QSL("format-justify-fill")), tr("Newspaper view"), TabBar::Closable); - - setCurrentIndex(index); - + int index = addTab(prev, qApp->icons()->fromTheme(QSL("format-justify-fill")), tr("Newspaper view"), TabBar::Closable); + setCurrentIndex(index); #if defined(USE_WEBENGINE) - prev->loadMessages(messages, root); + prev->loadMessages(messages, root); #endif - - return index; + return index; } int TabWidget::addEmptyBrowser() { - return addBrowser(false, true); + return addBrowser(false, true); } -int TabWidget::addLinkedBrowser(const QUrl &initial_url) { - return addBrowser(false, false, initial_url); +int TabWidget::addLinkedBrowser(const QUrl& initial_url) { + return addBrowser(false, false, initial_url); } -int TabWidget::addLinkedBrowser(const QString &initial_url) { - return addLinkedBrowser(QUrl(initial_url)); +int TabWidget::addLinkedBrowser(const QString& initial_url) { + return addLinkedBrowser(QUrl(initial_url)); } -int TabWidget::addBrowser(bool move_after_current, bool make_active, const QUrl &initial_url) { +int TabWidget::addBrowser(bool move_after_current, bool make_active, const QUrl& initial_url) { #if defined(USE_WEBENGINE) - - // Create new WebBrowser. - WebBrowser *browser = new WebBrowser(this); - int final_index; - + // Create new WebBrowser. + WebBrowser* browser = new WebBrowser(this); + int final_index; #if defined (Q_OS_MACOS) - const QString browser_tab_name = tr(" Web browser"); + const QString browser_tab_name = tr(" Web browser"); #else - const QString browser_tab_name = tr("Web browser"); + const QString browser_tab_name = tr("Web browser"); #endif - if (move_after_current) { - // Insert web browser after current tab. - final_index = insertTab(currentIndex() + 1, browser, qApp->icons()->fromTheme(QSL("text-html")), - browser_tab_name, TabBar::Closable); - } - else { - // Add new browser as the last tab. - final_index = addTab(browser, qApp->icons()->fromTheme(QSL("text-html")), - //: Web browser default tab title. - browser_tab_name, - TabBar::Closable); - } + if (move_after_current) { + // Insert web browser after current tab. + final_index = insertTab(currentIndex() + 1, browser, qApp->icons()->fromTheme(QSL("text-html")), + browser_tab_name, TabBar::Closable); + } - // Make connections. - connect(browser, &WebBrowser::titleChanged, this, &TabWidget::changeTitle); - connect(browser, &WebBrowser::iconChanged, this, &TabWidget::changeIcon); + else { + // Add new browser as the last tab. + final_index = addTab(browser, qApp->icons()->fromTheme(QSL("text-html")), + //: Web browser default tab title. + browser_tab_name, + TabBar::Closable); + } - // Setup the tab index. - browser->setIndex(final_index); + // Make connections. + connect(browser, &WebBrowser::titleChanged, this, &TabWidget::changeTitle); + connect(browser, &WebBrowser::iconChanged, this, &TabWidget::changeIcon); + // Setup the tab index. + browser->setIndex(final_index); - // Load initial web page if desired. - if (initial_url.isValid()) { - browser->loadUrl(initial_url); - } + // Load initial web page if desired. + if (initial_url.isValid()) { + browser->loadUrl(initial_url); + } - // Make new web browser active if desired. - if (make_active) { - setCurrentIndex(final_index); - browser->setFocus(Qt::OtherFocusReason); - } + // Make new web browser active if desired. + if (make_active) { + setCurrentIndex(final_index); + browser->setFocus(Qt::OtherFocusReason); + } - return final_index; + return final_index; #else - Q_UNUSED(move_after_current) - Q_UNUSED(make_active) - - WebFactory::instance()->openUrlInExternalBrowser(initial_url.toString()); - - return -1; + Q_UNUSED(move_after_current) + Q_UNUSED(make_active) + WebFactory::instance()->openUrlInExternalBrowser(initial_url.toString()); + return -1; #endif } -void TabWidget::indentTabText(int index){ +void TabWidget::indentTabText(int index) { #if defined (Q_OS_MACOS) - if (tabBar()->tabType(index) != TabBar::FeedReader && !tabIcon(index).isNull()) { - // We have closable tab with some icon, fix the title. - const QString text = tabText(index); - if (!text.startsWith(QSL(" "))) { - setTabText(index, QSL(" ") + text); - } - } + if (tabBar()->tabType(index) != TabBar::FeedReader && !tabIcon(index).isNull()) { + // We have closable tab with some icon, fix the title. + const QString text = tabText(index); + + if (!text.startsWith(QSL(" "))) { + setTabText(index, QSL(" ") + text); + } + } + #else - Q_UNUSED(index) + Q_UNUSED(index) #endif } void TabWidget::removeTab(int index, bool clear_from_memory) { - if (clear_from_memory) { - widget(index)->deleteLater(); - } + if (clear_from_memory) { + widget(index)->deleteLater(); + } - QTabWidget::removeTab(index); + QTabWidget::removeTab(index); } -int TabWidget::addTab(TabContent *widget, const QIcon &icon, const QString &label, const TabBar::TabType &type) { - const int index = QTabWidget::addTab(widget, icon, label); - tabBar()->setTabType(index, type); - indentTabText(index); - - return index; +int TabWidget::addTab(TabContent* widget, const QIcon& icon, const QString& label, const TabBar::TabType& type) { + const int index = QTabWidget::addTab(widget, icon, label); + tabBar()->setTabType(index, type); + indentTabText(index); + return index; } -int TabWidget::addTab(TabContent *widget, const QString &label, const TabBar::TabType &type) { - const int index = QTabWidget::addTab(widget, label); - tabBar()->setTabType(index, type); - indentTabText(index); - - return index; +int TabWidget::addTab(TabContent* widget, const QString& label, const TabBar::TabType& type) { + const int index = QTabWidget::addTab(widget, label); + tabBar()->setTabType(index, type); + indentTabText(index); + return index; } -int TabWidget::insertTab(int index, QWidget *widget, const QIcon &icon, const QString &label, const TabBar::TabType &type) { - const int tab_index = QTabWidget::insertTab(index, widget, icon, label); - tabBar()->setTabType(tab_index, type); - indentTabText(index); - - return tab_index; +int TabWidget::insertTab(int index, QWidget* widget, const QIcon& icon, const QString& label, const TabBar::TabType& type) { + const int tab_index = QTabWidget::insertTab(index, widget, icon, label); + tabBar()->setTabType(tab_index, type); + indentTabText(index); + return tab_index; } -int TabWidget::insertTab(int index, QWidget *widget, const QString &label, const TabBar::TabType &type) { - const int tab_index = QTabWidget::insertTab(index, widget, label); - tabBar()->setTabType(tab_index, type); - indentTabText(index); - - return tab_index; +int TabWidget::insertTab(int index, QWidget* widget, const QString& label, const TabBar::TabType& type) { + const int tab_index = QTabWidget::insertTab(index, widget, label); + tabBar()->setTabType(tab_index, type); + indentTabText(index); + return tab_index; } -void TabWidget::changeIcon(int index, const QIcon &new_icon) { - setTabIcon(index, new_icon); - indentTabText(index); +void TabWidget::changeIcon(int index, const QIcon& new_icon) { + setTabIcon(index, new_icon); + indentTabText(index); } -void TabWidget::changeTitle(int index, const QString &new_title) { - setTabText(index, TextFactory::shorten(new_title)); - setTabToolTip(index, new_title); - indentTabText(index); +void TabWidget::changeTitle(int index, const QString& new_title) { + setTabText(index, TextFactory::shorten(new_title)); + setTabToolTip(index, new_title); + indentTabText(index); } void TabWidget::fixContentsAfterMove(int from, int to) { - from = qMin(from, to); - to = qMax(from, to); + from = qMin(from, to); + to = qMax(from, to); - for ( ; from <= to; from++) { - TabContent *content = static_cast(widget(from)); - content->setIndex(from); - } + for (; from <= to; from++) { + TabContent* content = static_cast(widget(from)); + content->setIndex(from); + } } diff --git a/src/gui/tabwidget.h b/src/gui/tabwidget.h index e7bdfb640..949468e12 100755 --- a/src/gui/tabwidget.h +++ b/src/gui/tabwidget.h @@ -33,103 +33,103 @@ class RootItem; class FeedMessageViewer; class TabWidget : public QTabWidget { - Q_OBJECT + Q_OBJECT - public: - // Constructors and destructors. - explicit TabWidget(QWidget *parent = 0); - virtual ~TabWidget(); + public: + // Constructors and destructors. + explicit TabWidget(QWidget* parent = 0); + virtual ~TabWidget(); - // Manimulators for tabs. - int addTab(TabContent *widget, const QString &, - const TabBar::TabType &type = TabBar::NonClosable); - int addTab(TabContent *widget, const QIcon &icon, - const QString &label, const TabBar::TabType &type = TabBar::NonClosable); - int insertTab(int index, QWidget *widget, const QString &label, - const TabBar::TabType &type = TabBar::Closable); - int insertTab(int index, QWidget *widget, const QIcon &icon, - const QString &label, const TabBar::TabType &type = TabBar::NonClosable); - void removeTab(int index, bool clear_from_memory); + // Manimulators for tabs. + int addTab(TabContent* widget, const QString&, + const TabBar::TabType& type = TabBar::NonClosable); + int addTab(TabContent* widget, const QIcon& icon, + const QString& label, const TabBar::TabType& type = TabBar::NonClosable); + int insertTab(int index, QWidget* widget, const QString& label, + const TabBar::TabType& type = TabBar::Closable); + int insertTab(int index, QWidget* widget, const QIcon& icon, + const QString& label, const TabBar::TabType& type = TabBar::NonClosable); + void removeTab(int index, bool clear_from_memory); - // Returns tab bar. - inline TabBar *tabBar() const { - return static_cast(QTabWidget::tabBar()); - } + // Returns tab bar. + inline TabBar* tabBar() const { + return static_cast(QTabWidget::tabBar()); + } - // Returns the central widget of this tab. - inline TabContent *widget(int index) const { - return static_cast(QTabWidget::widget(index)); - } + // Returns the central widget of this tab. + inline TabContent* widget(int index) const { + return static_cast(QTabWidget::widget(index)); + } - inline TabContent *currentWidget() const { - return static_cast(QTabWidget::currentWidget()); - } + inline TabContent* currentWidget() const { + return static_cast(QTabWidget::currentWidget()); + } - // Initializes TabWidget with tabs, this includes initialization - // of main "Feeds" widget. - void initializeTabs(); + // Initializes TabWidget with tabs, this includes initialization + // of main "Feeds" widget. + void initializeTabs(); - // Sets up icons for this TabWidget. - void setupIcons(); + // Sets up icons for this TabWidget. + void setupIcons(); - // Accessor to feed/message viewer. - inline FeedMessageViewer *feedMessageViewer() const { - return m_feedMessageViewer; - } + // Accessor to feed/message viewer. + inline FeedMessageViewer* feedMessageViewer() const { + return m_feedMessageViewer; + } - protected: - // Creates necesary connections. - void createConnections(); + protected: + // Creates necesary connections. + void createConnections(); - // Sets up properties of custom corner button. - void setupMainMenuButton(); + // Sets up properties of custom corner button. + void setupMainMenuButton(); - // Handlers of insertin/removing of tabs. - void tabInserted(int index); - void tabRemoved(int index); + // Handlers of insertin/removing of tabs. + void tabInserted(int index); + void tabRemoved(int index); - public slots: - // Called when number of tab pages changes. - void checkTabBarVisibility(); + public slots: + // Called when number of tab pages changes. + void checkTabBarVisibility(); - // Tab closing. - bool closeTab(int index); - void closeAllTabsExceptCurrent(); - void closeAllTabs(); + // Tab closing. + bool closeTab(int index); + void closeAllTabsExceptCurrent(); + void closeAllTabs(); - // Displays download manager. - void showDownloadManager(); + // Displays download manager. + void showDownloadManager(); - int addNewspaperView(RootItem *root, const QList &messages); + int addNewspaperView(RootItem* root, const QList& messages); - // Adds new WebBrowser tab to global TabWidget. - int addEmptyBrowser(); + // Adds new WebBrowser tab to global TabWidget. + int addEmptyBrowser(); - // Adds new WebBrowser with link. This is used when user - // selects to "Open link in new tab.". - int addLinkedBrowser(const QUrl &initial_url = QUrl()); - int addLinkedBrowser(const QString &initial_url); + // Adds new WebBrowser with link. This is used when user + // selects to "Open link in new tab.". + int addLinkedBrowser(const QUrl& initial_url = QUrl()); + int addLinkedBrowser(const QString& initial_url); - // General method for adding WebBrowsers. - int addBrowser(bool move_after_current, bool make_active, const QUrl &initial_url = QUrl()); + // General method for adding WebBrowsers. + int addBrowser(bool move_after_current, bool make_active, const QUrl& initial_url = QUrl()); - private slots: - // Fixes tabs indexes. - void fixContentsAfterMove(int from, int to); + private slots: + // Fixes tabs indexes. + void fixContentsAfterMove(int from, int to); - // Changes icon/text of the tab. - void changeTitle(int index, const QString &new_title); - void changeIcon(int index, const QIcon &new_icon); + // Changes icon/text of the tab. + void changeTitle(int index, const QString& new_title); + void changeIcon(int index, const QIcon& new_icon); - // Opens main menu. - void openMainMenu(); + // Opens main menu. + void openMainMenu(); - private: - void indentTabText(int index); + private: + void indentTabText(int index); - PlainToolButton *m_btnMainMenu; - QMenu *m_menuMain; - FeedMessageViewer *m_feedMessageViewer; + PlainToolButton* m_btnMainMenu; + QMenu* m_menuMain; + FeedMessageViewer* m_feedMessageViewer; }; #endif // TABWIDGET_H diff --git a/src/gui/timespinbox.cpp b/src/gui/timespinbox.cpp old mode 100644 new mode 100755 index 1bd4c5dfa..490c9ffc7 --- a/src/gui/timespinbox.cpp +++ b/src/gui/timespinbox.cpp @@ -1,88 +1,87 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/timespinbox.h" - -#include - - -TimeSpinBox::TimeSpinBox(QWidget *parent) : QDoubleSpinBox(parent) { - setMinimum(5.0); - setAccelerated(true); - setDecimals(0); - setMaximum(10000000.0); -} - -TimeSpinBox::~TimeSpinBox() { -} - -double TimeSpinBox::valueFromText(const QString &text) const { - bool ok; - double value = text.toDouble(&ok); - - if (ok) { - return value; - } - else { - QRegExp rx("\\b[0-9]{1,}\\b"); - QStringList numbers; - int pos = 0; - int count = 0; - - while ((pos = rx.indexIn(text, pos)) != -1) { - numbers.append(rx.cap(0)); - - if (pos >= 0) { - ++pos; - ++count; - } - } - - if (numbers.size() == 2) { - return (numbers.at(0).toDouble() * 60.0) + numbers.at(1).toDouble(); - } - else { - return -1.0; - } - } -} - -QString TimeSpinBox::textFromValue(double val) const { - int minutes_total = (int)val; - int minutes_val = minutes_total % 60; - int hours_val = (minutes_total - minutes_val) / 60; - - QString hours = tr("%n hour(s)", "", hours_val); - QString minutes = tr("%n minute(s)", "", minutes_val); - - return hours + tr(" and ") + minutes; -} - -void TimeSpinBox::fixup(QString &input) const { - bool ok; - double value = input.toDouble(&ok); - - if (ok) { - input = textFromValue(value); - } -} - -QValidator::State TimeSpinBox::validate(QString &input, int &pos) const { - Q_UNUSED(pos) - - return (valueFromText(input) != -1.0) ? QValidator::Acceptable : QValidator::Intermediate; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/timespinbox.h" + +#include + + +TimeSpinBox::TimeSpinBox(QWidget* parent) : QDoubleSpinBox(parent) { + setMinimum(5.0); + setAccelerated(true); + setDecimals(0); + setMaximum(10000000.0); +} + +TimeSpinBox::~TimeSpinBox() { +} + +double TimeSpinBox::valueFromText(const QString& text) const { + bool ok; + double value = text.toDouble(&ok); + + if (ok) { + return value; + } + + else { + QRegExp rx("\\b[0-9]{1,}\\b"); + QStringList numbers; + int pos = 0; + int count = 0; + + while ((pos = rx.indexIn(text, pos)) != -1) { + numbers.append(rx.cap(0)); + + if (pos >= 0) { + ++pos; + ++count; + } + } + + if (numbers.size() == 2) { + return (numbers.at(0).toDouble() * 60.0) + numbers.at(1).toDouble(); + } + + else { + return -1.0; + } + } +} + +QString TimeSpinBox::textFromValue(double val) const { + int minutes_total = (int)val; + int minutes_val = minutes_total % 60; + int hours_val = (minutes_total - minutes_val) / 60; + QString hours = tr("%n hour(s)", "", hours_val); + QString minutes = tr("%n minute(s)", "", minutes_val); + return hours + tr(" and ") + minutes; +} + +void TimeSpinBox::fixup(QString& input) const { + bool ok; + double value = input.toDouble(&ok); + + if (ok) { + input = textFromValue(value); + } +} + +QValidator::State TimeSpinBox::validate(QString& input, int& pos) const { + Q_UNUSED(pos) + return (valueFromText(input) != -1.0) ? QValidator::Acceptable : QValidator::Intermediate; +} diff --git a/src/gui/timespinbox.h b/src/gui/timespinbox.h index 01da14238..7b5619571 100755 --- a/src/gui/timespinbox.h +++ b/src/gui/timespinbox.h @@ -1,37 +1,37 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef TIMESPINBOX_H -#define TIMESPINBOX_H - -#include - - -class TimeSpinBox : public QDoubleSpinBox { - Q_OBJECT - - public: - explicit TimeSpinBox(QWidget *parent = 0); - virtual ~TimeSpinBox(); - - double valueFromText(const QString &text) const; - QString textFromValue(double val) const; - void fixup(QString &input) const; - QValidator::State validate(QString &input, int &pos) const; -}; - -#endif // TIMESPINBOX_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef TIMESPINBOX_H +#define TIMESPINBOX_H + +#include + + +class TimeSpinBox : public QDoubleSpinBox { + Q_OBJECT + + public: + explicit TimeSpinBox(QWidget* parent = 0); + virtual ~TimeSpinBox(); + + double valueFromText(const QString& text) const; + QString textFromValue(double val) const; + void fixup(QString& input) const; + QValidator::State validate(QString& input, int& pos) const; +}; + +#endif // TIMESPINBOX_H diff --git a/src/gui/toolbareditor.cpp b/src/gui/toolbareditor.cpp index 6d512a048..6256d0606 100755 --- a/src/gui/toolbareditor.cpp +++ b/src/gui/toolbareditor.cpp @@ -1,282 +1,269 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/toolbareditor.h" - -#include "gui/basetoolbar.h" -#include "gui/dialogs/formmain.h" - -#include - - -ToolBarEditor::ToolBarEditor(QWidget *parent) - : QWidget(parent), m_ui(new Ui::ToolBarEditor) { - m_ui->setupUi(this); - - // Create connections. - connect(m_ui->m_btnInsertSeparator, &QToolButton::clicked, this, &ToolBarEditor::insertSeparator); - connect(m_ui->m_btnInsertSpacer, &QToolButton::clicked, this, &ToolBarEditor::insertSpacer); - - connect(m_ui->m_btnAddSelectedAction, &QToolButton::clicked, this, &ToolBarEditor::addSelectedAction); - connect(m_ui->m_btnDeleteAllActions, &QToolButton::clicked, this, &ToolBarEditor::deleteAllActions); - connect(m_ui->m_btnDeleteSelectedAction, &QToolButton::clicked, this, &ToolBarEditor::deleteSelectedAction); - connect(m_ui->m_btnMoveActionUp, &QToolButton::clicked, this, &ToolBarEditor::moveActionUp); - connect(m_ui->m_btnMoveActionDown, &QToolButton::clicked, this, &ToolBarEditor::moveActionDown); - connect(m_ui->m_btnReset, &QToolButton::clicked, this, &ToolBarEditor::resetToolBar); - - connect(m_ui->m_listAvailableActions, &QListWidget::itemSelectionChanged, this, &ToolBarEditor::updateActionsAvailability); - connect(m_ui->m_listActivatedActions, &QListWidget::itemSelectionChanged, this, &ToolBarEditor::updateActionsAvailability); - connect(m_ui->m_listActivatedActions, &QListWidget::itemDoubleClicked, this, &ToolBarEditor::deleteSelectedAction); - connect(m_ui->m_listAvailableActions, &QListWidget::itemDoubleClicked, this, &ToolBarEditor::addSelectedAction); - - m_ui->m_listActivatedActions->installEventFilter(this); - - m_ui->m_btnInsertSeparator->setIcon(qApp->icons()->fromTheme(QSL("insert-object"))); - m_ui->m_btnInsertSpacer->setIcon(qApp->icons()->fromTheme(QSL("go-jump"))); - m_ui->m_btnAddSelectedAction->setIcon(qApp->icons()->fromTheme(QSL("back"))); - m_ui->m_btnDeleteAllActions->setIcon(qApp->icons()->fromTheme(QSL("application-exit"))); - m_ui->m_btnDeleteSelectedAction->setIcon(qApp->icons()->fromTheme(QSL("forward"))); - m_ui->m_btnMoveActionDown->setIcon(qApp->icons()->fromTheme(QSL("down"))); - m_ui->m_btnMoveActionUp->setIcon(qApp->icons()->fromTheme(QSL("up"))); - m_ui->m_btnReset->setIcon(qApp->icons()->fromTheme(QSL("reload"))); -} - -ToolBarEditor::~ToolBarEditor() { - qDebug("Destroying ToolBarEditor instance."); -} - -void ToolBarEditor::loadFromToolBar(BaseBar *tool_bar) { - m_toolBar = tool_bar; - - QList activated_actions = m_toolBar->changeableActions(); - QList available_actions = m_toolBar->availableActions(); - - loadEditor(activated_actions, available_actions); -} - -void ToolBarEditor::saveToolBar() { - QStringList action_names; - - for (int i = 0; i < m_ui->m_listActivatedActions->count(); i++) { - action_names.append(m_ui->m_listActivatedActions->item(i)->data(Qt::UserRole).toString()); - } - - m_toolBar->saveChangeableActions(action_names); -} - -void ToolBarEditor::resetToolBar() { - if (m_toolBar != nullptr) { - loadEditor(m_toolBar->getSpecificActions(m_toolBar->defaultActions()), m_toolBar->availableActions()); - } -} - -void ToolBarEditor::loadEditor(const QList activated_actions, const QList available_actions) { - m_ui->m_listActivatedActions->clear(); - m_ui->m_listAvailableActions->clear(); - - foreach (const QAction *action, activated_actions) { - QListWidgetItem *action_item = new QListWidgetItem(action->icon(), action->text().replace('&', ""), m_ui->m_listActivatedActions); - - if (action->isSeparator()) { - action_item->setData(Qt::UserRole, SEPARATOR_ACTION_NAME); - action_item->setIcon(qApp->icons()->fromTheme(QSL("insert-object"))); - action_item->setText(tr("Separator")); - action_item->setToolTip(tr("Separator")); - } - else if (action->property("type").isValid()) { - action_item->setData(Qt::UserRole, action->property("type").toString()); - action_item->setText(action->property("name").toString()); - action_item->setToolTip(action_item->text()); - } - else { - action_item->setData(Qt::UserRole, action->objectName()); - action_item->setToolTip(action->toolTip()); - } - } - - foreach (QAction *action, available_actions) { - if (!activated_actions.contains(action)) { - QListWidgetItem *action_item = new QListWidgetItem(action->icon(), action->text().replace('&', ""), m_ui->m_listAvailableActions); - - if (action->isSeparator()) { - action_item->setData(Qt::UserRole, SEPARATOR_ACTION_NAME); - action_item->setText(tr("Separator")); - action_item->setToolTip(tr("Separator")); - action_item->setIcon(qApp->icons()->fromTheme(QSL("insert-object"))); - } - else if (action->property("type").isValid()) { - action_item->setData(Qt::UserRole, action->property("type").toString()); - action_item->setText(action->property("name").toString()); - action_item->setToolTip(action_item->text()); - } - else { - action_item->setData(Qt::UserRole, action->objectName()); - action_item->setToolTip(action->toolTip()); - } - } - } - - m_ui->m_listAvailableActions->sortItems(Qt::AscendingOrder); - m_ui->m_listAvailableActions->setCurrentRow(m_ui->m_listAvailableActions->count() >= 0 ? 0 : -1); - m_ui->m_listActivatedActions->setCurrentRow(m_ui->m_listActivatedActions->count() >= 0 ? 0 : -1); -} - -bool ToolBarEditor::eventFilter(QObject *object, QEvent *event) { - if (object == m_ui->m_listActivatedActions) { - if (event->type() == QEvent::KeyPress) { - const QKeyEvent *key_event = static_cast(event); - - if (key_event->key() == Qt::Key_Delete) { - deleteSelectedAction(); - return true; - } - else if (key_event->key() == Qt::Key_Down && key_event->modifiers() & Qt::ControlModifier) { - moveActionDown(); - return true; - } - else if (key_event->key() == Qt::Key_Up && key_event->modifiers() & Qt::ControlModifier) { - moveActionUp(); - return true; - } - } - } - - return false; -} - -void ToolBarEditor::updateActionsAvailability() { - m_ui->m_btnDeleteAllActions->setEnabled(m_ui->m_listActivatedActions->count() > 0); - m_ui->m_btnDeleteSelectedAction->setEnabled(m_ui->m_listActivatedActions->selectedItems().size() == 1); - m_ui->m_btnMoveActionUp->setEnabled(m_ui->m_listActivatedActions->selectedItems().size() == 1 && - m_ui->m_listActivatedActions->currentRow() > 0); - m_ui->m_btnMoveActionDown->setEnabled(m_ui->m_listActivatedActions->selectedItems().size() == 1 && - m_ui->m_listActivatedActions->currentRow() < m_ui->m_listActivatedActions->count() - 1); - m_ui->m_btnAddSelectedAction->setEnabled(m_ui->m_listAvailableActions->selectedItems().size() > 0); -} - -void ToolBarEditor::insertSpacer() { - const int current_row = m_ui->m_listActivatedActions->currentRow(); - QListWidgetItem *item = new QListWidgetItem(tr("Toolbar spacer")); - - item->setIcon(qApp->icons()->fromTheme(QSL("go-jump"))); - item->setData(Qt::UserRole, SPACER_ACTION_NAME); - - m_ui->m_listActivatedActions->insertItem(current_row + 1, item); - m_ui->m_listActivatedActions->setCurrentRow(current_row + 1); - - emit setupChanged(); -} - -void ToolBarEditor::insertSeparator() { - const int current_row = m_ui->m_listActivatedActions->currentRow(); - QListWidgetItem *item = new QListWidgetItem(tr("Separator")); - - item->setData(Qt::UserRole, SEPARATOR_ACTION_NAME); - item->setToolTip(tr("Separator")); - item->setIcon(qApp->icons()->fromTheme(QSL("insert-object"))); - - m_ui->m_listActivatedActions->insertItem(current_row + 1, item); - m_ui->m_listActivatedActions->setCurrentRow(current_row + 1); - - emit setupChanged(); -} - -void ToolBarEditor::moveActionDown() { - QList items = m_ui->m_listActivatedActions->selectedItems(); - - if (items.size() == 1 && m_ui->m_listActivatedActions->currentRow() < m_ui->m_listActivatedActions->count() - 1) { - QListWidgetItem *selected_item = items.at(0); - int row = m_ui->m_listActivatedActions->row(selected_item); - - m_ui->m_listActivatedActions->takeItem(row++); - m_ui->m_listActivatedActions->insertItem(row, selected_item); - m_ui->m_listActivatedActions->setCurrentRow(row); - - emit setupChanged(); - } -} - -void ToolBarEditor::moveActionUp() { - QList items = m_ui->m_listActivatedActions->selectedItems(); - - if (items.size() == 1 && m_ui->m_listActivatedActions->currentRow() > 0) { - QListWidgetItem *selected_item = items.at(0); - int row = m_ui->m_listActivatedActions->row(selected_item); - - m_ui->m_listActivatedActions->takeItem(row--); - m_ui->m_listActivatedActions->insertItem(row, selected_item); - m_ui->m_listActivatedActions->setCurrentRow(row); - - emit setupChanged(); - } -} - -void ToolBarEditor::addSelectedAction() { - QList items = m_ui->m_listAvailableActions->selectedItems(); - - if (items.size() == 1) { - QListWidgetItem *selected_item = items.at(0); - - m_ui->m_listActivatedActions->insertItem( - m_ui->m_listActivatedActions->currentRow() + 1, - m_ui->m_listAvailableActions->takeItem(m_ui->m_listAvailableActions->row(selected_item))); - m_ui->m_listActivatedActions->setCurrentRow(m_ui->m_listActivatedActions->currentRow() + 1); - - emit setupChanged(); - } -} - -void ToolBarEditor::deleteSelectedAction() { - QList items = m_ui->m_listActivatedActions->selectedItems(); - - if (items.size() == 1) { - QListWidgetItem *selected_item = items.at(0); - const QString data_item = selected_item->data(Qt::UserRole).toString(); - - if (data_item == SEPARATOR_ACTION_NAME || data_item == SPACER_ACTION_NAME) { - m_ui->m_listActivatedActions->takeItem(m_ui->m_listActivatedActions->row(selected_item)); - - updateActionsAvailability(); - } - else { - m_ui->m_listAvailableActions->insertItem( - m_ui->m_listAvailableActions->currentRow() + 1, - m_ui->m_listActivatedActions->takeItem(m_ui->m_listActivatedActions->row(selected_item))); - m_ui->m_listAvailableActions->sortItems(Qt::AscendingOrder); - m_ui->m_listAvailableActions->setCurrentRow(m_ui->m_listAvailableActions->currentRow() + 1); - } - - emit setupChanged(); - } -} - -void ToolBarEditor::deleteAllActions() { - QListWidgetItem *taken_item; - QString data_item; - - while ((taken_item = m_ui->m_listActivatedActions->takeItem(0)) != 0) { - data_item = taken_item->data(Qt::UserRole).toString(); - - if (data_item != SEPARATOR_ACTION_NAME && data_item != SPACER_ACTION_NAME) { - m_ui->m_listAvailableActions->insertItem(m_ui->m_listAvailableActions->currentRow() + 1, taken_item); - } - } - - m_ui->m_listAvailableActions->sortItems(Qt::AscendingOrder); - updateActionsAvailability(); - emit setupChanged(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/toolbareditor.h" + +#include "gui/basetoolbar.h" +#include "gui/dialogs/formmain.h" + +#include + + +ToolBarEditor::ToolBarEditor(QWidget* parent) + : QWidget(parent), m_ui(new Ui::ToolBarEditor) { + m_ui->setupUi(this); + // Create connections. + connect(m_ui->m_btnInsertSeparator, &QToolButton::clicked, this, &ToolBarEditor::insertSeparator); + connect(m_ui->m_btnInsertSpacer, &QToolButton::clicked, this, &ToolBarEditor::insertSpacer); + connect(m_ui->m_btnAddSelectedAction, &QToolButton::clicked, this, &ToolBarEditor::addSelectedAction); + connect(m_ui->m_btnDeleteAllActions, &QToolButton::clicked, this, &ToolBarEditor::deleteAllActions); + connect(m_ui->m_btnDeleteSelectedAction, &QToolButton::clicked, this, &ToolBarEditor::deleteSelectedAction); + connect(m_ui->m_btnMoveActionUp, &QToolButton::clicked, this, &ToolBarEditor::moveActionUp); + connect(m_ui->m_btnMoveActionDown, &QToolButton::clicked, this, &ToolBarEditor::moveActionDown); + connect(m_ui->m_btnReset, &QToolButton::clicked, this, &ToolBarEditor::resetToolBar); + connect(m_ui->m_listAvailableActions, &QListWidget::itemSelectionChanged, this, &ToolBarEditor::updateActionsAvailability); + connect(m_ui->m_listActivatedActions, &QListWidget::itemSelectionChanged, this, &ToolBarEditor::updateActionsAvailability); + connect(m_ui->m_listActivatedActions, &QListWidget::itemDoubleClicked, this, &ToolBarEditor::deleteSelectedAction); + connect(m_ui->m_listAvailableActions, &QListWidget::itemDoubleClicked, this, &ToolBarEditor::addSelectedAction); + m_ui->m_listActivatedActions->installEventFilter(this); + m_ui->m_btnInsertSeparator->setIcon(qApp->icons()->fromTheme(QSL("insert-object"))); + m_ui->m_btnInsertSpacer->setIcon(qApp->icons()->fromTheme(QSL("go-jump"))); + m_ui->m_btnAddSelectedAction->setIcon(qApp->icons()->fromTheme(QSL("back"))); + m_ui->m_btnDeleteAllActions->setIcon(qApp->icons()->fromTheme(QSL("application-exit"))); + m_ui->m_btnDeleteSelectedAction->setIcon(qApp->icons()->fromTheme(QSL("forward"))); + m_ui->m_btnMoveActionDown->setIcon(qApp->icons()->fromTheme(QSL("down"))); + m_ui->m_btnMoveActionUp->setIcon(qApp->icons()->fromTheme(QSL("up"))); + m_ui->m_btnReset->setIcon(qApp->icons()->fromTheme(QSL("reload"))); +} + +ToolBarEditor::~ToolBarEditor() { + qDebug("Destroying ToolBarEditor instance."); +} + +void ToolBarEditor::loadFromToolBar(BaseBar* tool_bar) { + m_toolBar = tool_bar; + QList activated_actions = m_toolBar->changeableActions(); + QList available_actions = m_toolBar->availableActions(); + loadEditor(activated_actions, available_actions); +} + +void ToolBarEditor::saveToolBar() { + QStringList action_names; + + for (int i = 0; i < m_ui->m_listActivatedActions->count(); i++) { + action_names.append(m_ui->m_listActivatedActions->item(i)->data(Qt::UserRole).toString()); + } + + m_toolBar->saveChangeableActions(action_names); +} + +void ToolBarEditor::resetToolBar() { + if (m_toolBar != nullptr) { + loadEditor(m_toolBar->getSpecificActions(m_toolBar->defaultActions()), m_toolBar->availableActions()); + } +} + +void ToolBarEditor::loadEditor(const QList activated_actions, const QList available_actions) { + m_ui->m_listActivatedActions->clear(); + m_ui->m_listAvailableActions->clear(); + + foreach (const QAction* action, activated_actions) { + QListWidgetItem* action_item = new QListWidgetItem(action->icon(), action->text().replace('&', ""), m_ui->m_listActivatedActions); + + if (action->isSeparator()) { + action_item->setData(Qt::UserRole, SEPARATOR_ACTION_NAME); + action_item->setIcon(qApp->icons()->fromTheme(QSL("insert-object"))); + action_item->setText(tr("Separator")); + action_item->setToolTip(tr("Separator")); + } + + else if (action->property("type").isValid()) { + action_item->setData(Qt::UserRole, action->property("type").toString()); + action_item->setText(action->property("name").toString()); + action_item->setToolTip(action_item->text()); + } + + else { + action_item->setData(Qt::UserRole, action->objectName()); + action_item->setToolTip(action->toolTip()); + } + } + + foreach (QAction* action, available_actions) { + if (!activated_actions.contains(action)) { + QListWidgetItem* action_item = new QListWidgetItem(action->icon(), action->text().replace('&', ""), m_ui->m_listAvailableActions); + + if (action->isSeparator()) { + action_item->setData(Qt::UserRole, SEPARATOR_ACTION_NAME); + action_item->setText(tr("Separator")); + action_item->setToolTip(tr("Separator")); + action_item->setIcon(qApp->icons()->fromTheme(QSL("insert-object"))); + } + + else if (action->property("type").isValid()) { + action_item->setData(Qt::UserRole, action->property("type").toString()); + action_item->setText(action->property("name").toString()); + action_item->setToolTip(action_item->text()); + } + + else { + action_item->setData(Qt::UserRole, action->objectName()); + action_item->setToolTip(action->toolTip()); + } + } + } + + m_ui->m_listAvailableActions->sortItems(Qt::AscendingOrder); + m_ui->m_listAvailableActions->setCurrentRow(m_ui->m_listAvailableActions->count() >= 0 ? 0 : -1); + m_ui->m_listActivatedActions->setCurrentRow(m_ui->m_listActivatedActions->count() >= 0 ? 0 : -1); +} + +bool ToolBarEditor::eventFilter(QObject* object, QEvent* event) { + if (object == m_ui->m_listActivatedActions) { + if (event->type() == QEvent::KeyPress) { + const QKeyEvent* key_event = static_cast(event); + + if (key_event->key() == Qt::Key_Delete) { + deleteSelectedAction(); + return true; + } + + else if (key_event->key() == Qt::Key_Down && key_event->modifiers() & Qt::ControlModifier) { + moveActionDown(); + return true; + } + + else if (key_event->key() == Qt::Key_Up && key_event->modifiers() & Qt::ControlModifier) { + moveActionUp(); + return true; + } + } + } + + return false; +} + +void ToolBarEditor::updateActionsAvailability() { + m_ui->m_btnDeleteAllActions->setEnabled(m_ui->m_listActivatedActions->count() > 0); + m_ui->m_btnDeleteSelectedAction->setEnabled(m_ui->m_listActivatedActions->selectedItems().size() == 1); + m_ui->m_btnMoveActionUp->setEnabled(m_ui->m_listActivatedActions->selectedItems().size() == 1 && + m_ui->m_listActivatedActions->currentRow() > 0); + m_ui->m_btnMoveActionDown->setEnabled(m_ui->m_listActivatedActions->selectedItems().size() == 1 && + m_ui->m_listActivatedActions->currentRow() < m_ui->m_listActivatedActions->count() - 1); + m_ui->m_btnAddSelectedAction->setEnabled(m_ui->m_listAvailableActions->selectedItems().size() > 0); +} + +void ToolBarEditor::insertSpacer() { + const int current_row = m_ui->m_listActivatedActions->currentRow(); + QListWidgetItem* item = new QListWidgetItem(tr("Toolbar spacer")); + item->setIcon(qApp->icons()->fromTheme(QSL("go-jump"))); + item->setData(Qt::UserRole, SPACER_ACTION_NAME); + m_ui->m_listActivatedActions->insertItem(current_row + 1, item); + m_ui->m_listActivatedActions->setCurrentRow(current_row + 1); + emit setupChanged(); +} + +void ToolBarEditor::insertSeparator() { + const int current_row = m_ui->m_listActivatedActions->currentRow(); + QListWidgetItem* item = new QListWidgetItem(tr("Separator")); + item->setData(Qt::UserRole, SEPARATOR_ACTION_NAME); + item->setToolTip(tr("Separator")); + item->setIcon(qApp->icons()->fromTheme(QSL("insert-object"))); + m_ui->m_listActivatedActions->insertItem(current_row + 1, item); + m_ui->m_listActivatedActions->setCurrentRow(current_row + 1); + emit setupChanged(); +} + +void ToolBarEditor::moveActionDown() { + QList items = m_ui->m_listActivatedActions->selectedItems(); + + if (items.size() == 1 && m_ui->m_listActivatedActions->currentRow() < m_ui->m_listActivatedActions->count() - 1) { + QListWidgetItem* selected_item = items.at(0); + int row = m_ui->m_listActivatedActions->row(selected_item); + m_ui->m_listActivatedActions->takeItem(row++); + m_ui->m_listActivatedActions->insertItem(row, selected_item); + m_ui->m_listActivatedActions->setCurrentRow(row); + emit setupChanged(); + } +} + +void ToolBarEditor::moveActionUp() { + QList items = m_ui->m_listActivatedActions->selectedItems(); + + if (items.size() == 1 && m_ui->m_listActivatedActions->currentRow() > 0) { + QListWidgetItem* selected_item = items.at(0); + int row = m_ui->m_listActivatedActions->row(selected_item); + m_ui->m_listActivatedActions->takeItem(row--); + m_ui->m_listActivatedActions->insertItem(row, selected_item); + m_ui->m_listActivatedActions->setCurrentRow(row); + emit setupChanged(); + } +} + +void ToolBarEditor::addSelectedAction() { + QList items = m_ui->m_listAvailableActions->selectedItems(); + + if (items.size() == 1) { + QListWidgetItem* selected_item = items.at(0); + m_ui->m_listActivatedActions->insertItem( + m_ui->m_listActivatedActions->currentRow() + 1, + m_ui->m_listAvailableActions->takeItem(m_ui->m_listAvailableActions->row(selected_item))); + m_ui->m_listActivatedActions->setCurrentRow(m_ui->m_listActivatedActions->currentRow() + 1); + emit setupChanged(); + } +} + +void ToolBarEditor::deleteSelectedAction() { + QList items = m_ui->m_listActivatedActions->selectedItems(); + + if (items.size() == 1) { + QListWidgetItem* selected_item = items.at(0); + const QString data_item = selected_item->data(Qt::UserRole).toString(); + + if (data_item == SEPARATOR_ACTION_NAME || data_item == SPACER_ACTION_NAME) { + m_ui->m_listActivatedActions->takeItem(m_ui->m_listActivatedActions->row(selected_item)); + updateActionsAvailability(); + } + + else { + m_ui->m_listAvailableActions->insertItem( + m_ui->m_listAvailableActions->currentRow() + 1, + m_ui->m_listActivatedActions->takeItem(m_ui->m_listActivatedActions->row(selected_item))); + m_ui->m_listAvailableActions->sortItems(Qt::AscendingOrder); + m_ui->m_listAvailableActions->setCurrentRow(m_ui->m_listAvailableActions->currentRow() + 1); + } + + emit setupChanged(); + } +} + +void ToolBarEditor::deleteAllActions() { + QListWidgetItem* taken_item; + QString data_item; + + while ((taken_item = m_ui->m_listActivatedActions->takeItem(0)) != 0) { + data_item = taken_item->data(Qt::UserRole).toString(); + + if (data_item != SEPARATOR_ACTION_NAME && data_item != SPACER_ACTION_NAME) { + m_ui->m_listAvailableActions->insertItem(m_ui->m_listAvailableActions->currentRow() + 1, taken_item); + } + } + + m_ui->m_listAvailableActions->sortItems(Qt::AscendingOrder); + updateActionsAvailability(); + emit setupChanged(); +} diff --git a/src/gui/toolbareditor.h b/src/gui/toolbareditor.h index a1299dd31..1488e23fe 100755 --- a/src/gui/toolbareditor.h +++ b/src/gui/toolbareditor.h @@ -1,81 +1,81 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef TOOLBAREDITOR_H -#define TOOLBAREDITOR_H - -#include - -#include "ui_toolbareditor.h" - - -namespace Ui { - class ToolBarEditor; -} - -class BaseBar; - -class ToolBarEditor : public QWidget { - Q_OBJECT - - public: - // Constructors and destructors. - explicit ToolBarEditor(QWidget *parent = 0); - virtual ~ToolBarEditor(); - - // Toolbar operations. - void loadFromToolBar(BaseBar *tool_bar); - void saveToolBar(); - - inline QListWidget *activeItemsWidget() const { - return m_ui->m_listActivatedActions; - } - - inline QListWidget *availableItemsWidget() const { - return m_ui->m_listAvailableActions; - } - - protected: - bool eventFilter(QObject *object, QEvent *event); - - private slots: - void updateActionsAvailability(); - - // Insert common controls. - void insertSpacer(); - void insertSeparator(); - - void moveActionDown(); - void moveActionUp(); - - void addSelectedAction(); - void deleteSelectedAction(); - void deleteAllActions(); - - void resetToolBar(); - - signals: - void setupChanged(); - - private: - void loadEditor(const QList activated_actions, const QList available_actions); - - QScopedPointer m_ui; - BaseBar *m_toolBar; -}; - -#endif // TOOLBAREDITOR_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef TOOLBAREDITOR_H +#define TOOLBAREDITOR_H + +#include + +#include "ui_toolbareditor.h" + + +namespace Ui { + class ToolBarEditor; +} + +class BaseBar; + +class ToolBarEditor : public QWidget { + Q_OBJECT + + public: + // Constructors and destructors. + explicit ToolBarEditor(QWidget* parent = 0); + virtual ~ToolBarEditor(); + + // Toolbar operations. + void loadFromToolBar(BaseBar* tool_bar); + void saveToolBar(); + + inline QListWidget* activeItemsWidget() const { + return m_ui->m_listActivatedActions; + } + + inline QListWidget* availableItemsWidget() const { + return m_ui->m_listAvailableActions; + } + + protected: + bool eventFilter(QObject* object, QEvent* event); + + private slots: + void updateActionsAvailability(); + + // Insert common controls. + void insertSpacer(); + void insertSeparator(); + + void moveActionDown(); + void moveActionUp(); + + void addSelectedAction(); + void deleteSelectedAction(); + void deleteAllActions(); + + void resetToolBar(); + + signals: + void setupChanged(); + + private: + void loadEditor(const QList activated_actions, const QList available_actions); + + QScopedPointer m_ui; + BaseBar* m_toolBar; +}; + +#endif // TOOLBAREDITOR_H diff --git a/src/gui/treeviewcolumnsmenu.cpp b/src/gui/treeviewcolumnsmenu.cpp index 0e46bbbb9..13761be53 100755 --- a/src/gui/treeviewcolumnsmenu.cpp +++ b/src/gui/treeviewcolumnsmenu.cpp @@ -1,54 +1,50 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/treeviewcolumnsmenu.h" - -#include - - -TreeViewColumnsMenu::TreeViewColumnsMenu(QHeaderView *parent) : QMenu(parent) { - connect(this, &TreeViewColumnsMenu::aboutToShow, this, &TreeViewColumnsMenu::prepareMenu); -} - -TreeViewColumnsMenu::~TreeViewColumnsMenu() { -} - -void TreeViewColumnsMenu::prepareMenu() { - QHeaderView *header_view = header(); - - for (int i = 0; i < header_view->count(); i++) { - QAction *act = addAction(header_view->model()->headerData(i, Qt::Horizontal, Qt::EditRole).toString()); - - act->setData(i); - act->setCheckable(true); - act->setChecked(!header_view->isSectionHidden(i)); - - connect(act, &QAction::toggled, this, &TreeViewColumnsMenu::actionTriggered); - } -} - -void TreeViewColumnsMenu::actionTriggered(bool toggle) { - Q_UNUSED(toggle) - - QAction *send_act = qobject_cast(sender()); - - header()->setSectionHidden(send_act->data().toInt(), !send_act->isChecked()); -} - -QHeaderView *TreeViewColumnsMenu::header() { - return qobject_cast(parent()); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/treeviewcolumnsmenu.h" + +#include + + +TreeViewColumnsMenu::TreeViewColumnsMenu(QHeaderView* parent) : QMenu(parent) { + connect(this, &TreeViewColumnsMenu::aboutToShow, this, &TreeViewColumnsMenu::prepareMenu); +} + +TreeViewColumnsMenu::~TreeViewColumnsMenu() { +} + +void TreeViewColumnsMenu::prepareMenu() { + QHeaderView* header_view = header(); + + for (int i = 0; i < header_view->count(); i++) { + QAction* act = addAction(header_view->model()->headerData(i, Qt::Horizontal, Qt::EditRole).toString()); + act->setData(i); + act->setCheckable(true); + act->setChecked(!header_view->isSectionHidden(i)); + connect(act, &QAction::toggled, this, &TreeViewColumnsMenu::actionTriggered); + } +} + +void TreeViewColumnsMenu::actionTriggered(bool toggle) { + Q_UNUSED(toggle) + QAction* send_act = qobject_cast(sender()); + header()->setSectionHidden(send_act->data().toInt(), !send_act->isChecked()); +} + +QHeaderView* TreeViewColumnsMenu::header() { + return qobject_cast(parent()); +} diff --git a/src/gui/treeviewcolumnsmenu.h b/src/gui/treeviewcolumnsmenu.h index 700ea2554..e09ff854e 100755 --- a/src/gui/treeviewcolumnsmenu.h +++ b/src/gui/treeviewcolumnsmenu.h @@ -1,39 +1,39 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef TREEVIEWCOLUMNSMENU_H -#define TREEVIEWCOLUMNSMENU_H - -#include - - -class QHeaderView; - -class TreeViewColumnsMenu : public QMenu { - public: - explicit TreeViewColumnsMenu(QHeaderView *parent); - virtual ~TreeViewColumnsMenu(); - - private slots: - void prepareMenu(); - void actionTriggered(bool toggle); - - private: - QHeaderView *header(); -}; - -#endif // TREEVIEWCOLUMNSMENU_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef TREEVIEWCOLUMNSMENU_H +#define TREEVIEWCOLUMNSMENU_H + +#include + + +class QHeaderView; + +class TreeViewColumnsMenu : public QMenu { + public: + explicit TreeViewColumnsMenu(QHeaderView* parent); + virtual ~TreeViewColumnsMenu(); + + private slots: + void prepareMenu(); + void actionTriggered(bool toggle); + + private: + QHeaderView* header(); +}; + +#endif // TREEVIEWCOLUMNSMENU_H diff --git a/src/gui/treewidget.cpp b/src/gui/treewidget.cpp index 9e40550d9..93b9e7160 100755 --- a/src/gui/treewidget.cpp +++ b/src/gui/treewidget.cpp @@ -21,177 +21,183 @@ #include -TreeWidget::TreeWidget(QWidget *parent) - : QTreeWidget(parent), m_refreshAllItemsNeeded(true), m_showMode(ItemsCollapsed) { - connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(sheduleRefresh())); +TreeWidget::TreeWidget(QWidget* parent) + : QTreeWidget(parent), m_refreshAllItemsNeeded(true), m_showMode(ItemsCollapsed) { + connect(this, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(sheduleRefresh())); } void TreeWidget::clear() { - QTreeWidget::clear(); - m_allTreeItems.clear(); + QTreeWidget::clear(); + m_allTreeItems.clear(); } void TreeWidget::sheduleRefresh() { - m_refreshAllItemsNeeded = true; + m_refreshAllItemsNeeded = true; } -void TreeWidget::addTopLevelItem(QTreeWidgetItem *item) { - m_allTreeItems.append(item); - QTreeWidget::addTopLevelItem(item); +void TreeWidget::addTopLevelItem(QTreeWidgetItem* item) { + m_allTreeItems.append(item); + QTreeWidget::addTopLevelItem(item); } -void TreeWidget::addTopLevelItems(const QList &items) { - m_allTreeItems.append(items); - QTreeWidget::addTopLevelItems(items); +void TreeWidget::addTopLevelItems(const QList& items) { + m_allTreeItems.append(items); + QTreeWidget::addTopLevelItems(items); } -void TreeWidget::insertTopLevelItem(int index, QTreeWidgetItem *item) { - m_allTreeItems.append(item); - QTreeWidget::insertTopLevelItem(index, item); +void TreeWidget::insertTopLevelItem(int index, QTreeWidgetItem* item) { + m_allTreeItems.append(item); + QTreeWidget::insertTopLevelItem(index, item); } -void TreeWidget::insertTopLevelItems(int index, const QList &items) { - m_allTreeItems.append(items); - QTreeWidget::insertTopLevelItems(index, items); +void TreeWidget::insertTopLevelItems(int index, const QList& items) { + m_allTreeItems.append(items); + QTreeWidget::insertTopLevelItems(index, items); } -void TreeWidget::mousePressEvent(QMouseEvent *event) { - if (event->modifiers() == Qt::ControlModifier) { - emit itemControlClicked(itemAt(event->pos())); - } +void TreeWidget::mousePressEvent(QMouseEvent* event) { + if (event->modifiers() == Qt::ControlModifier) { + emit itemControlClicked(itemAt(event->pos())); + } - if (event->buttons() == Qt::MiddleButton) { - emit itemMiddleButtonClicked(itemAt(event->pos())); - } + if (event->buttons() == Qt::MiddleButton) { + emit itemMiddleButtonClicked(itemAt(event->pos())); + } - QTreeWidget::mousePressEvent(event); + QTreeWidget::mousePressEvent(event); } -void TreeWidget::iterateAllItems(QTreeWidgetItem *parent) { - int count = parent ? parent->childCount() : topLevelItemCount(); +void TreeWidget::iterateAllItems(QTreeWidgetItem* parent) { + int count = parent ? parent->childCount() : topLevelItemCount(); - for (int i = 0; i < count; i++) { - QTreeWidgetItem *item = parent ? parent->child(i) : topLevelItem(i); + for (int i = 0; i < count; i++) { + QTreeWidgetItem* item = parent ? parent->child(i) : topLevelItem(i); - if (item->childCount() == 0) { - m_allTreeItems.append(item); - } + if (item->childCount() == 0) { + m_allTreeItems.append(item); + } - iterateAllItems(item); - } + iterateAllItems(item); + } } QList TreeWidget::allItems() { - if (m_refreshAllItemsNeeded) { - m_allTreeItems.clear(); - iterateAllItems(0); - m_refreshAllItemsNeeded = false; - } + if (m_refreshAllItemsNeeded) { + m_allTreeItems.clear(); + iterateAllItems(0); + m_refreshAllItemsNeeded = false; + } - return m_allTreeItems; + return m_allTreeItems; } -void TreeWidget::filterString(const QString &string) { - QList _allItems = allItems(); - QList parents; - bool stringIsEmpty = string.isEmpty(); - foreach (QTreeWidgetItem *item, _allItems) { - bool containsString = stringIsEmpty || item->text(0).contains(string, Qt::CaseInsensitive); +void TreeWidget::filterString(const QString& string) { + QList _allItems = allItems(); + QList parents; + bool stringIsEmpty = string.isEmpty(); - if (containsString) { - item->setHidden(false); - if (item->parent()) { - if (!parents.contains(item->parent())) { - parents << item->parent(); - } - } - } - else { - item->setHidden(true); - if (item->parent()) { - item->parent()->setHidden(true); - } - } - } + foreach (QTreeWidgetItem* item, _allItems) { + bool containsString = stringIsEmpty || item->text(0).contains(string, Qt::CaseInsensitive); - for (int i = 0; i < parents.size(); ++i) { - QTreeWidgetItem *parentItem = parents.at(i); - parentItem->setHidden(false); + if (containsString) { + item->setHidden(false); - if (stringIsEmpty) { - parentItem->setExpanded(m_showMode == ItemsExpanded); - } - else { - parentItem->setExpanded(true); - } + if (item->parent()) { + if (!parents.contains(item->parent())) { + parents << item->parent(); + } + } + } - if (parentItem->parent() && !parents.contains(parentItem->parent())) { - parents << parentItem->parent(); - } - } + else { + item->setHidden(true); + + if (item->parent()) { + item->parent()->setHidden(true); + } + } + } + + for (int i = 0; i < parents.size(); ++i) { + QTreeWidgetItem* parentItem = parents.at(i); + parentItem->setHidden(false); + + if (stringIsEmpty) { + parentItem->setExpanded(m_showMode == ItemsExpanded); + } + + else { + parentItem->setExpanded(true); + } + + if (parentItem->parent() && !parents.contains(parentItem->parent())) { + parents << parentItem->parent(); + } + } } -bool TreeWidget::appendToParentItem(const QString &parentText, QTreeWidgetItem *item) { - QList list = findItems(parentText, Qt::MatchExactly); +bool TreeWidget::appendToParentItem(const QString& parentText, QTreeWidgetItem* item) { + QList list = findItems(parentText, Qt::MatchExactly); - if (list.count() == 0) { - return false; - } + if (list.count() == 0) { + return false; + } - QTreeWidgetItem *parentItem = list.at(0); + QTreeWidgetItem* parentItem = list.at(0); - if (!parentItem) { - return false; - } + if (!parentItem) { + return false; + } - m_allTreeItems.append(item); - parentItem->addChild(item); - return true; + m_allTreeItems.append(item); + parentItem->addChild(item); + return true; } -bool TreeWidget::appendToParentItem(QTreeWidgetItem *parent, QTreeWidgetItem *item) { - if (!parent || parent->treeWidget() != this) { - return false; - } +bool TreeWidget::appendToParentItem(QTreeWidgetItem* parent, QTreeWidgetItem* item) { + if (!parent || parent->treeWidget() != this) { + return false; + } - m_allTreeItems.append(item); - parent->addChild(item); - return true; + m_allTreeItems.append(item); + parent->addChild(item); + return true; } -bool TreeWidget::prependToParentItem(const QString &parentText, QTreeWidgetItem *item) { - QList list = findItems(parentText, Qt::MatchExactly); - if (list.count() == 0) { - return false; - } +bool TreeWidget::prependToParentItem(const QString& parentText, QTreeWidgetItem* item) { + QList list = findItems(parentText, Qt::MatchExactly); - QTreeWidgetItem *parentItem = list.at(0); + if (list.count() == 0) { + return false; + } - if (!parentItem) { - return false; - } + QTreeWidgetItem* parentItem = list.at(0); - m_allTreeItems.append(item); - parentItem->insertChild(0, item); - return true; + if (!parentItem) { + return false; + } + + m_allTreeItems.append(item); + parentItem->insertChild(0, item); + return true; } -bool TreeWidget::prependToParentItem(QTreeWidgetItem *parent, QTreeWidgetItem *item) { - if (!parent || parent->treeWidget() != this) { - return false; - } +bool TreeWidget::prependToParentItem(QTreeWidgetItem* parent, QTreeWidgetItem* item) { + if (!parent || parent->treeWidget() != this) { + return false; + } - m_allTreeItems.append(item); - parent->insertChild(0, item); - return true; + m_allTreeItems.append(item); + parent->insertChild(0, item); + return true; } void TreeWidget::deleteItem(QTreeWidgetItem* item) { - m_refreshAllItemsNeeded = true; - delete item; + m_refreshAllItemsNeeded = true; + delete item; } -void TreeWidget::deleteItems(const QList &items) { - m_refreshAllItemsNeeded = true; - qDeleteAll(items); +void TreeWidget::deleteItems(const QList& items) { + m_refreshAllItemsNeeded = true; + qDeleteAll(items); } diff --git a/src/gui/treewidget.h b/src/gui/treewidget.h index 711bbccac..5ebdd69d7 100755 --- a/src/gui/treewidget.h +++ b/src/gui/treewidget.h @@ -23,48 +23,52 @@ class TreeWidget : public QTreeWidget { - Q_OBJECT + Q_OBJECT - public: - explicit TreeWidget(QWidget *parent = 0); + public: + explicit TreeWidget(QWidget* parent = 0); - enum ItemShowMode { ItemsCollapsed = 0, ItemsExpanded = 1 }; + enum ItemShowMode { ItemsCollapsed = 0, ItemsExpanded = 1 }; - ItemShowMode defaultItemShowMode() { return m_showMode; } - void setDefaultItemShowMode(ItemShowMode mode) { m_showMode = mode; } - QList allItems(); + ItemShowMode defaultItemShowMode() { + return m_showMode; + } + void setDefaultItemShowMode(ItemShowMode mode) { + m_showMode = mode; + } + QList allItems(); - bool appendToParentItem(const QString &parentText, QTreeWidgetItem *item); - bool appendToParentItem(QTreeWidgetItem* parent, QTreeWidgetItem *item); - bool prependToParentItem(const QString &parentText, QTreeWidgetItem *item); - bool prependToParentItem(QTreeWidgetItem* parent, QTreeWidgetItem *item); + bool appendToParentItem(const QString& parentText, QTreeWidgetItem* item); + bool appendToParentItem(QTreeWidgetItem* parent, QTreeWidgetItem* item); + bool prependToParentItem(const QString& parentText, QTreeWidgetItem* item); + bool prependToParentItem(QTreeWidgetItem* parent, QTreeWidgetItem* item); - void addTopLevelItem(QTreeWidgetItem *item); - void addTopLevelItems(const QList &items); - void insertTopLevelItem(int index, QTreeWidgetItem *item); - void insertTopLevelItems(int index, const QList &items); + void addTopLevelItem(QTreeWidgetItem* item); + void addTopLevelItems(const QList& items); + void insertTopLevelItem(int index, QTreeWidgetItem* item); + void insertTopLevelItems(int index, const QList& items); - void deleteItem(QTreeWidgetItem *item); - void deleteItems(const QList &items); + void deleteItem(QTreeWidgetItem* item); + void deleteItems(const QList& items); - signals: - void itemControlClicked(QTreeWidgetItem *item); - void itemMiddleButtonClicked(QTreeWidgetItem *item); + signals: + void itemControlClicked(QTreeWidgetItem* item); + void itemMiddleButtonClicked(QTreeWidgetItem* item); - public slots: - void filterString(const QString &string); - void clear(); + public slots: + void filterString(const QString& string); + void clear(); - private slots: - void sheduleRefresh(); + private slots: + void sheduleRefresh(); - private: - void mousePressEvent(QMouseEvent* event); - void iterateAllItems(QTreeWidgetItem* parent); + private: + void mousePressEvent(QMouseEvent* event); + void iterateAllItems(QTreeWidgetItem* parent); - bool m_refreshAllItemsNeeded; - QList m_allTreeItems; - ItemShowMode m_showMode; + bool m_refreshAllItemsNeeded; + QList m_allTreeItems; + ItemShowMode m_showMode; }; #endif // BOOKMARKSTREEWIDGET_H diff --git a/src/gui/webbrowser.cpp b/src/gui/webbrowser.cpp index 8d387c037..11b56c813 100755 --- a/src/gui/webbrowser.cpp +++ b/src/gui/webbrowser.cpp @@ -1,300 +1,284 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/webbrowser.h" - -#include "miscellaneous/application.h" -#include "miscellaneous/databasequeries.h" -#include "network-web/networkfactory.h" -#include "network-web/webfactory.h" -#include "gui/messagebox.h" -#include "gui/webviewer.h" -#include "gui/discoverfeedsbutton.h" -#include "gui/locationlineedit.h" -#include "services/abstract/serviceroot.h" - -#include -#include -#include -#include -#include - - -void WebBrowser::createConnections() { - connect(m_webView, &WebViewer::messageStatusChangeRequested, this, &WebBrowser::receiveMessageStatusChangeRequest); - - connect(m_txtLocation, &LocationLineEdit::submitted, - this, static_cast(&WebBrowser::loadUrl)); - connect(m_webView, &WebViewer::urlChanged, this, &WebBrowser::updateUrl); - - // Change location textbox status according to webpage status. - connect(m_webView, &WebViewer::loadStarted, this, &WebBrowser::onLoadingStarted); - connect(m_webView, &WebViewer::loadProgress, this, &WebBrowser::onLoadingProgress); - connect(m_webView, &WebViewer::loadFinished, this, &WebBrowser::onLoadingFinished); - - // Forward title/icon changes. - connect(m_webView, &WebViewer::titleChanged, this, &WebBrowser::onTitleChanged); - -#if QT_VERSION >= 0x050700 - connect(m_webView, &WebViewer::iconChanged, this, &WebBrowser::onIconChanged); -#endif -} - -void WebBrowser::updateUrl(const QUrl &url) { - QString url_string = url.toString(); - - m_txtLocation->setText(url_string); - //setNavigationBarVisible(url_string != INTERNAL_URL_EMPTY && url_string != INTERNAL_URL_NEWSPAPER); -} - -void WebBrowser::loadUrl(const QUrl &url) { - if (url.isValid()) { - m_webView->load(url); - } -} - -WebBrowser::WebBrowser(QWidget *parent) : TabContent(parent), - m_layout(new QVBoxLayout(this)), - m_toolBar(new QToolBar(tr("Navigation panel"), this)), - m_webView(new WebViewer(this)), - m_txtLocation(new LocationLineEdit(this)), - m_btnDiscoverFeeds(new DiscoverFeedsButton(this)), - m_actionBack(m_webView->pageAction(QWebEnginePage::Back)), - m_actionForward(m_webView->pageAction(QWebEnginePage::Forward)), - m_actionReload(m_webView->pageAction(QWebEnginePage::Reload)), - m_actionStop(m_webView->pageAction(QWebEnginePage::Stop)) { - - // Initialize the components and layout. - initializeLayout(); - - setFocusProxy(m_txtLocation); - setTabOrder(m_txtLocation, m_toolBar); - setTabOrder(m_toolBar, m_webView); - - createConnections(); - reloadFontSettings(); -} - -WebBrowser::~WebBrowser() { - // Delete members. Do not use scoped pointers here. - delete m_layout; -} - -void WebBrowser::reloadFontSettings() { - QFont fon; - fon.fromString(qApp->settings()->value(GROUP(Messages), - SETTING(Messages::PreviewerFontStandard)).toString()); - - QWebEngineSettings::globalSettings()->setFontFamily(QWebEngineSettings::StandardFont, fon.family()); - QWebEngineSettings::globalSettings()->setFontSize(QWebEngineSettings::DefaultFontSize, fon.pointSize()); -} - -void WebBrowser::increaseZoom() { - m_webView->increaseWebPageZoom(); -} - -void WebBrowser::decreaseZoom() { - m_webView->decreaseWebPageZoom(); -} - -void WebBrowser::resetZoom() { - m_webView->resetWebPageZoom(); -} - -void WebBrowser::clear() { - m_webView->clear(); - m_messages.clear(); - hide(); -} - -void WebBrowser::loadUrl(const QString &url) { - return loadUrl(QUrl::fromUserInput(url)); -} - -void WebBrowser::loadMessages(const QList &messages, RootItem *root) { - m_messages = messages; - m_root = root; - - if (!m_root.isNull()) { - m_webView->loadMessages(messages); - show(); - } -} - -void WebBrowser::loadMessage(const Message &message, RootItem *root) { - loadMessages(QList() << message, root); -} - -void WebBrowser::receiveMessageStatusChangeRequest(int message_id, WebPage::MessageStatusChange change) { - switch (change) { - case WebPage::MarkRead: - markMessageAsRead(message_id, true); - break; - - case WebPage::MarkUnread: - markMessageAsRead(message_id, false); - break; - - case WebPage::MarkStarred: - switchMessageImportance(message_id, true); - break; - - case WebPage::MarkUnstarred: - switchMessageImportance(message_id, false); - break; - - default: - break; - } -} - -void WebBrowser::onTitleChanged(const QString &new_title) { - if (new_title.isEmpty()) { - //: Webbrowser tab title when no title is available. - emit titleChanged(m_index, tr("No title")); - } - else { - emit titleChanged(m_index, new_title); - } -} - -#if QT_VERSION >= 0x050700 -void WebBrowser::onIconChanged(const QIcon &icon) { - emit iconChanged(m_index, icon); -} -#endif - -void WebBrowser::initializeLayout() { - m_toolBar->setFloatable(false); - m_toolBar->setMovable(false); - m_toolBar->setAllowedAreas(Qt::TopToolBarArea); - - // Modify action texts. - m_actionBack->setText(tr("Back")); - m_actionBack->setToolTip(tr("Go back.")); - m_actionForward->setText(tr("Forward")); - m_actionForward->setToolTip(tr("Go forward.")); - m_actionReload->setText(tr("Reload")); - m_actionReload->setToolTip(tr("Reload current web page.")); - m_actionStop->setText(tr("Stop")); - m_actionStop->setToolTip(tr("Stop web page loading.")); - - QWidgetAction *act_discover = new QWidgetAction(this); - - act_discover->setDefaultWidget(m_btnDiscoverFeeds); - - // Add needed actions into toolbar. - m_toolBar->addAction(m_actionBack); - m_toolBar->addAction(m_actionForward); - m_toolBar->addAction(m_actionReload); - m_toolBar->addAction(m_actionStop); - m_toolBar->addAction(act_discover); - m_toolBar->addWidget(m_txtLocation); - - m_loadingProgress = new QProgressBar(this); - m_loadingProgress->setFixedHeight(5); - m_loadingProgress->setMinimum(0); - m_loadingProgress->setTextVisible(false); - m_loadingProgress->setMaximum(100); - m_loadingProgress->setAttribute(Qt::WA_TranslucentBackground); - - // Setup layout. - m_layout->addWidget(m_toolBar); - m_layout->addWidget(m_webView); - m_layout->addWidget(m_loadingProgress); - m_layout->setMargin(0); - m_layout->setSpacing(0); -} - -void WebBrowser::onLoadingStarted() { - m_btnDiscoverFeeds->clearFeedAddresses(); - m_loadingProgress->show(); -} - -void WebBrowser::onLoadingProgress(int progress) { - m_loadingProgress->setValue(progress); -} - -void WebBrowser::onLoadingFinished(bool success) { - if (success) { - // Let's check if there are any feeds defined on the web and eventually - // display "Add feeds" button. - m_webView->page()->toHtml([this](const QString &result){ - this->m_btnDiscoverFeeds->setFeedAddresses(NetworkFactory::extractFeedLinksFromHtmlPage(m_webView->url(), result)); - }); - } - else { - m_btnDiscoverFeeds->clearFeedAddresses(); - } - - m_loadingProgress->hide(); - m_loadingProgress->setValue(0); -} - -void WebBrowser::markMessageAsRead(int id, bool read) { - if (!m_root.isNull()) { - Message *msg = findMessage(id); - - if (msg != nullptr && m_root->getParentServiceRoot()->onBeforeSetMessagesRead(m_root.data(), - QList() << *msg, - read ? RootItem::Read : RootItem::Unread)) { - DatabaseQueries::markMessagesReadUnread(qApp->database()->connection(objectName(), DatabaseFactory::FromSettings), - QStringList() << QString::number(msg->m_id), - read ? RootItem::Read : RootItem::Unread); - m_root->getParentServiceRoot()->onAfterSetMessagesRead(m_root.data(), - QList() << *msg, - read ? RootItem::Read : RootItem::Unread); - - emit markMessageRead(msg->m_id, read ? RootItem::Read : RootItem::Unread); - msg->m_isRead = read ? RootItem::Read : RootItem::Unread; - } - } -} - -void WebBrowser::switchMessageImportance(int id, bool checked) { - if (!m_root.isNull()) { - Message *msg = findMessage(id); - - if (msg != nullptr && m_root->getParentServiceRoot()->onBeforeSwitchMessageImportance(m_root.data(), - QList() << ImportanceChange(*msg, - msg->m_isImportant ? - RootItem::NotImportant : - RootItem::Important))) { - DatabaseQueries::switchMessagesImportance(qApp->database()->connection(objectName(), DatabaseFactory::FromSettings), - QStringList() << QString::number(msg->m_id)); - - m_root->getParentServiceRoot()->onAfterSwitchMessageImportance(m_root.data(), - QList() << ImportanceChange(*msg, - msg->m_isImportant ? - RootItem::NotImportant : - RootItem::Important)); - - emit markMessageImportant(msg->m_id, msg->m_isImportant ? RootItem::NotImportant : RootItem::Important); - msg->m_isImportant = checked; - } - } -} - -Message *WebBrowser::findMessage(int id) { - for (int i = 0; i < m_messages.size(); i++) { - if (m_messages.at(i).m_id == id) { - return &m_messages[i]; - } - } - - return nullptr; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/webbrowser.h" + +#include "miscellaneous/application.h" +#include "miscellaneous/databasequeries.h" +#include "network-web/networkfactory.h" +#include "network-web/webfactory.h" +#include "gui/messagebox.h" +#include "gui/webviewer.h" +#include "gui/discoverfeedsbutton.h" +#include "gui/locationlineedit.h" +#include "services/abstract/serviceroot.h" + +#include +#include +#include +#include +#include + + +void WebBrowser::createConnections() { + connect(m_webView, &WebViewer::messageStatusChangeRequested, this, &WebBrowser::receiveMessageStatusChangeRequest); + connect(m_txtLocation, &LocationLineEdit::submitted, + this, static_cast(&WebBrowser::loadUrl)); + connect(m_webView, &WebViewer::urlChanged, this, &WebBrowser::updateUrl); + // Change location textbox status according to webpage status. + connect(m_webView, &WebViewer::loadStarted, this, &WebBrowser::onLoadingStarted); + connect(m_webView, &WebViewer::loadProgress, this, &WebBrowser::onLoadingProgress); + connect(m_webView, &WebViewer::loadFinished, this, &WebBrowser::onLoadingFinished); + // Forward title/icon changes. + connect(m_webView, &WebViewer::titleChanged, this, &WebBrowser::onTitleChanged); +#if QT_VERSION >= 0x050700 + connect(m_webView, &WebViewer::iconChanged, this, &WebBrowser::onIconChanged); +#endif +} + +void WebBrowser::updateUrl(const QUrl& url) { + QString url_string = url.toString(); + m_txtLocation->setText(url_string); + //setNavigationBarVisible(url_string != INTERNAL_URL_EMPTY && url_string != INTERNAL_URL_NEWSPAPER); +} + +void WebBrowser::loadUrl(const QUrl& url) { + if (url.isValid()) { + m_webView->load(url); + } +} + +WebBrowser::WebBrowser(QWidget* parent) : TabContent(parent), + m_layout(new QVBoxLayout(this)), + m_toolBar(new QToolBar(tr("Navigation panel"), this)), + m_webView(new WebViewer(this)), + m_txtLocation(new LocationLineEdit(this)), + m_btnDiscoverFeeds(new DiscoverFeedsButton(this)), + m_actionBack(m_webView->pageAction(QWebEnginePage::Back)), + m_actionForward(m_webView->pageAction(QWebEnginePage::Forward)), + m_actionReload(m_webView->pageAction(QWebEnginePage::Reload)), + m_actionStop(m_webView->pageAction(QWebEnginePage::Stop)) { + // Initialize the components and layout. + initializeLayout(); + setFocusProxy(m_txtLocation); + setTabOrder(m_txtLocation, m_toolBar); + setTabOrder(m_toolBar, m_webView); + createConnections(); + reloadFontSettings(); +} + +WebBrowser::~WebBrowser() { + // Delete members. Do not use scoped pointers here. + delete m_layout; +} + +void WebBrowser::reloadFontSettings() { + QFont fon; + fon.fromString(qApp->settings()->value(GROUP(Messages), + SETTING(Messages::PreviewerFontStandard)).toString()); + QWebEngineSettings::globalSettings()->setFontFamily(QWebEngineSettings::StandardFont, fon.family()); + QWebEngineSettings::globalSettings()->setFontSize(QWebEngineSettings::DefaultFontSize, fon.pointSize()); +} + +void WebBrowser::increaseZoom() { + m_webView->increaseWebPageZoom(); +} + +void WebBrowser::decreaseZoom() { + m_webView->decreaseWebPageZoom(); +} + +void WebBrowser::resetZoom() { + m_webView->resetWebPageZoom(); +} + +void WebBrowser::clear() { + m_webView->clear(); + m_messages.clear(); + hide(); +} + +void WebBrowser::loadUrl(const QString& url) { + return loadUrl(QUrl::fromUserInput(url)); +} + +void WebBrowser::loadMessages(const QList& messages, RootItem* root) { + m_messages = messages; + m_root = root; + + if (!m_root.isNull()) { + m_webView->loadMessages(messages); + show(); + } +} + +void WebBrowser::loadMessage(const Message& message, RootItem* root) { + loadMessages(QList() << message, root); +} + +void WebBrowser::receiveMessageStatusChangeRequest(int message_id, WebPage::MessageStatusChange change) { + switch (change) { + case WebPage::MarkRead: + markMessageAsRead(message_id, true); + break; + + case WebPage::MarkUnread: + markMessageAsRead(message_id, false); + break; + + case WebPage::MarkStarred: + switchMessageImportance(message_id, true); + break; + + case WebPage::MarkUnstarred: + switchMessageImportance(message_id, false); + break; + + default: + break; + } +} + +void WebBrowser::onTitleChanged(const QString& new_title) { + if (new_title.isEmpty()) { + //: Webbrowser tab title when no title is available. + emit titleChanged(m_index, tr("No title")); + } + + else { + emit titleChanged(m_index, new_title); + } +} + +#if QT_VERSION >= 0x050700 +void WebBrowser::onIconChanged(const QIcon& icon) { + emit iconChanged(m_index, icon); +} +#endif + +void WebBrowser::initializeLayout() { + m_toolBar->setFloatable(false); + m_toolBar->setMovable(false); + m_toolBar->setAllowedAreas(Qt::TopToolBarArea); + // Modify action texts. + m_actionBack->setText(tr("Back")); + m_actionBack->setToolTip(tr("Go back.")); + m_actionForward->setText(tr("Forward")); + m_actionForward->setToolTip(tr("Go forward.")); + m_actionReload->setText(tr("Reload")); + m_actionReload->setToolTip(tr("Reload current web page.")); + m_actionStop->setText(tr("Stop")); + m_actionStop->setToolTip(tr("Stop web page loading.")); + QWidgetAction* act_discover = new QWidgetAction(this); + act_discover->setDefaultWidget(m_btnDiscoverFeeds); + // Add needed actions into toolbar. + m_toolBar->addAction(m_actionBack); + m_toolBar->addAction(m_actionForward); + m_toolBar->addAction(m_actionReload); + m_toolBar->addAction(m_actionStop); + m_toolBar->addAction(act_discover); + m_toolBar->addWidget(m_txtLocation); + m_loadingProgress = new QProgressBar(this); + m_loadingProgress->setFixedHeight(5); + m_loadingProgress->setMinimum(0); + m_loadingProgress->setTextVisible(false); + m_loadingProgress->setMaximum(100); + m_loadingProgress->setAttribute(Qt::WA_TranslucentBackground); + // Setup layout. + m_layout->addWidget(m_toolBar); + m_layout->addWidget(m_webView); + m_layout->addWidget(m_loadingProgress); + m_layout->setMargin(0); + m_layout->setSpacing(0); +} + +void WebBrowser::onLoadingStarted() { + m_btnDiscoverFeeds->clearFeedAddresses(); + m_loadingProgress->show(); +} + +void WebBrowser::onLoadingProgress(int progress) { + m_loadingProgress->setValue(progress); +} + +void WebBrowser::onLoadingFinished(bool success) { + if (success) { + // Let's check if there are any feeds defined on the web and eventually + // display "Add feeds" button. + m_webView->page()->toHtml([this](const QString & result) { + this->m_btnDiscoverFeeds->setFeedAddresses(NetworkFactory::extractFeedLinksFromHtmlPage(m_webView->url(), result)); + }); + } + + else { + m_btnDiscoverFeeds->clearFeedAddresses(); + } + + m_loadingProgress->hide(); + m_loadingProgress->setValue(0); +} + +void WebBrowser::markMessageAsRead(int id, bool read) { + if (!m_root.isNull()) { + Message* msg = findMessage(id); + + if (msg != nullptr && m_root->getParentServiceRoot()->onBeforeSetMessagesRead(m_root.data(), + QList() << *msg, + read ? RootItem::Read : RootItem::Unread)) { + DatabaseQueries::markMessagesReadUnread(qApp->database()->connection(objectName(), DatabaseFactory::FromSettings), + QStringList() << QString::number(msg->m_id), + read ? RootItem::Read : RootItem::Unread); + m_root->getParentServiceRoot()->onAfterSetMessagesRead(m_root.data(), + QList() << *msg, + read ? RootItem::Read : RootItem::Unread); + emit markMessageRead(msg->m_id, read ? RootItem::Read : RootItem::Unread); + msg->m_isRead = read ? RootItem::Read : RootItem::Unread; + } + } +} + +void WebBrowser::switchMessageImportance(int id, bool checked) { + if (!m_root.isNull()) { + Message* msg = findMessage(id); + + if (msg != nullptr && m_root->getParentServiceRoot()->onBeforeSwitchMessageImportance(m_root.data(), + QList() << ImportanceChange(*msg, + msg->m_isImportant ? + RootItem::NotImportant : + RootItem::Important))) { + DatabaseQueries::switchMessagesImportance(qApp->database()->connection(objectName(), DatabaseFactory::FromSettings), + QStringList() << QString::number(msg->m_id)); + m_root->getParentServiceRoot()->onAfterSwitchMessageImportance(m_root.data(), + QList() << ImportanceChange(*msg, + msg->m_isImportant ? + RootItem::NotImportant : + RootItem::Important)); + emit markMessageImportant(msg->m_id, msg->m_isImportant ? RootItem::NotImportant : RootItem::Important); + msg->m_isImportant = checked; + } + } +} + +Message* WebBrowser::findMessage(int id) { + for (int i = 0; i < m_messages.size(); i++) { + if (m_messages.at(i).m_id == id) { + return &m_messages[i]; + } + } + + return nullptr; +} diff --git a/src/gui/webbrowser.h b/src/gui/webbrowser.h index f5a8e7ebb..7fedbdc20 100755 --- a/src/gui/webbrowser.h +++ b/src/gui/webbrowser.h @@ -1,122 +1,122 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef WEBBROWSER_H -#define WEBBROWSER_H - -#include "gui/tabcontent.h" - -#include "core/message.h" -#include "network-web/webpage.h" -#include "services/abstract/rootitem.h" - -#include -#include - - -class QToolButton; -class QVBoxLayout; -class QHBoxLayout; -class QProgressBar; -class QMenu; -class QLabel; -class TabWidget; -class WebViewer; -class LocationLineEdit; -class DiscoverFeedsButton; - -class WebBrowser : public TabContent { - Q_OBJECT - - public: - explicit WebBrowser(QWidget *parent = 0); - virtual ~WebBrowser(); - - WebBrowser *webBrowser() const { - return const_cast(this); - } - - WebViewer *viewer() const { - return m_webView; - } - - void reloadFontSettings(); - - public slots: - void increaseZoom(); - void decreaseZoom(); - void resetZoom(); - - void clear(); - void loadUrl(const QString &url); - void loadUrl(const QUrl &url); - void loadMessages(const QList &messages, RootItem *root); - void loadMessage(const Message &message, RootItem *root); - - // Switches visibility of navigation bar. - inline void setNavigationBarVisible(bool visible) { - m_toolBar->setVisible(visible); - } - - private slots: - void updateUrl(const QUrl &url); - - void onLoadingStarted(); - void onLoadingProgress(int progress); - void onLoadingFinished(bool success); - - void receiveMessageStatusChangeRequest(int message_id, WebPage::MessageStatusChange change); - - void onTitleChanged(const QString &new_title); - -#if QT_VERSION >= 0x050700 - void onIconChanged(const QIcon &icon); -#endif - - signals: - // Title/icon is changed. - void iconChanged(int index, const QIcon &icon); - void titleChanged(int index, const QString &title); - - void markMessageRead(int id, RootItem::ReadStatus read); - void markMessageImportant(int id, RootItem::Importance important); - void requestMessageListReload(bool mark_current_as_read); - - private: - void initializeLayout(); - Message *findMessage(int id); - void markMessageAsRead(int id, bool read); - void switchMessageImportance(int id, bool checked); - void createConnections(); - - QVBoxLayout *m_layout; - QToolBar *m_toolBar; - WebViewer *m_webView; - LocationLineEdit *m_txtLocation; - DiscoverFeedsButton *m_btnDiscoverFeeds; - QProgressBar *m_loadingProgress; - - QAction *m_actionBack; - QAction *m_actionForward; - QAction *m_actionReload; - QAction *m_actionStop; - - QList m_messages; - QPointer m_root; -}; - -#endif // WEBBROWSER_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef WEBBROWSER_H +#define WEBBROWSER_H + +#include "gui/tabcontent.h" + +#include "core/message.h" +#include "network-web/webpage.h" +#include "services/abstract/rootitem.h" + +#include +#include + + +class QToolButton; +class QVBoxLayout; +class QHBoxLayout; +class QProgressBar; +class QMenu; +class QLabel; +class TabWidget; +class WebViewer; +class LocationLineEdit; +class DiscoverFeedsButton; + +class WebBrowser : public TabContent { + Q_OBJECT + + public: + explicit WebBrowser(QWidget* parent = 0); + virtual ~WebBrowser(); + + WebBrowser* webBrowser() const { + return const_cast(this); + } + + WebViewer* viewer() const { + return m_webView; + } + + void reloadFontSettings(); + + public slots: + void increaseZoom(); + void decreaseZoom(); + void resetZoom(); + + void clear(); + void loadUrl(const QString& url); + void loadUrl(const QUrl& url); + void loadMessages(const QList& messages, RootItem* root); + void loadMessage(const Message& message, RootItem* root); + + // Switches visibility of navigation bar. + inline void setNavigationBarVisible(bool visible) { + m_toolBar->setVisible(visible); + } + + private slots: + void updateUrl(const QUrl& url); + + void onLoadingStarted(); + void onLoadingProgress(int progress); + void onLoadingFinished(bool success); + + void receiveMessageStatusChangeRequest(int message_id, WebPage::MessageStatusChange change); + + void onTitleChanged(const QString& new_title); + +#if QT_VERSION >= 0x050700 + void onIconChanged(const QIcon& icon); +#endif + + signals: + // Title/icon is changed. + void iconChanged(int index, const QIcon& icon); + void titleChanged(int index, const QString& title); + + void markMessageRead(int id, RootItem::ReadStatus read); + void markMessageImportant(int id, RootItem::Importance important); + void requestMessageListReload(bool mark_current_as_read); + + private: + void initializeLayout(); + Message* findMessage(int id); + void markMessageAsRead(int id, bool read); + void switchMessageImportance(int id, bool checked); + void createConnections(); + + QVBoxLayout* m_layout; + QToolBar* m_toolBar; + WebViewer* m_webView; + LocationLineEdit* m_txtLocation; + DiscoverFeedsButton* m_btnDiscoverFeeds; + QProgressBar* m_loadingProgress; + + QAction* m_actionBack; + QAction* m_actionForward; + QAction* m_actionReload; + QAction* m_actionStop; + + QList m_messages; + QPointer m_root; +}; + +#endif // WEBBROWSER_H diff --git a/src/gui/webviewer.cpp b/src/gui/webviewer.cpp index e0d5abf5c..9ee11e0ea 100755 --- a/src/gui/webviewer.cpp +++ b/src/gui/webviewer.cpp @@ -29,152 +29,150 @@ #include -WebViewer::WebViewer(QWidget *parent) : QWebEngineView(parent) { - WebPage *page = new WebPage(this); - - connect(page, &WebPage::messageStatusChangeRequested, this, &WebViewer::messageStatusChangeRequested); - setPage(page); +WebViewer::WebViewer(QWidget* parent) : QWebEngineView(parent) { + WebPage* page = new WebPage(this); + connect(page, &WebPage::messageStatusChangeRequested, this, &WebViewer::messageStatusChangeRequested); + setPage(page); } bool WebViewer::canIncreaseZoom() { - return zoomFactor() <= MAX_ZOOM_FACTOR - ZOOM_FACTOR_STEP; + return zoomFactor() <= MAX_ZOOM_FACTOR - ZOOM_FACTOR_STEP; } bool WebViewer::canDecreaseZoom() { - return zoomFactor() >= MIN_ZOOM_FACTOR + ZOOM_FACTOR_STEP; + return zoomFactor() >= MIN_ZOOM_FACTOR + ZOOM_FACTOR_STEP; } -WebPage *WebViewer::page() const { - return qobject_cast(QWebEngineView::page()); +WebPage* WebViewer::page() const { + return qobject_cast(QWebEngineView::page()); } void WebViewer::displayMessage() { - setHtml(m_messageContents, QUrl::fromUserInput(INTERNAL_URL_MESSAGE)); + setHtml(m_messageContents, QUrl::fromUserInput(INTERNAL_URL_MESSAGE)); } bool WebViewer::increaseWebPageZoom() { - if (canIncreaseZoom()) { - setZoomFactor(zoomFactor() + ZOOM_FACTOR_STEP); - return true; - } - else { - return false; - } + if (canIncreaseZoom()) { + setZoomFactor(zoomFactor() + ZOOM_FACTOR_STEP); + return true; + } + + else { + return false; + } } bool WebViewer::decreaseWebPageZoom() { - if (canDecreaseZoom()) { - setZoomFactor(zoomFactor() - ZOOM_FACTOR_STEP); - return true; - } - else { - return false; - } + if (canDecreaseZoom()) { + setZoomFactor(zoomFactor() - ZOOM_FACTOR_STEP); + return true; + } + + else { + return false; + } } bool WebViewer::resetWebPageZoom() { - const qreal new_factor = 1.0; + const qreal new_factor = 1.0; - if (new_factor != zoomFactor()) { - setZoomFactor(new_factor); - return true; - } - else { - return false; - } + if (new_factor != zoomFactor()) { + setZoomFactor(new_factor); + return true; + } + + else { + return false; + } } -void WebViewer::loadMessages(const QList &messages) { - Skin skin = qApp->skins()->currentSkin(); - QString messages_layout; - QString single_message_layout = skin.m_layoutMarkup; +void WebViewer::loadMessages(const QList& messages) { + Skin skin = qApp->skins()->currentSkin(); + QString messages_layout; + QString single_message_layout = skin.m_layoutMarkup; - foreach (const Message &message, messages) { - QString enclosures; - QString enclosure_images; + foreach (const Message& message, messages) { + QString enclosures; + QString enclosure_images; - foreach (const Enclosure &enclosure, message.m_enclosures) { - enclosures += skin.m_enclosureMarkup.arg(enclosure.m_url, tr("Attachment"), enclosure.m_mimeType); + foreach (const Enclosure& enclosure, message.m_enclosures) { + enclosures += skin.m_enclosureMarkup.arg(enclosure.m_url, tr("Attachment"), enclosure.m_mimeType); - if (enclosure.m_mimeType.startsWith(QSL("image/"))) { - // Add thumbnail image. - enclosure_images += skin.m_enclosureImageMarkup.arg( - enclosure.m_url, - enclosure.m_mimeType, - qApp->settings()->value(GROUP(Messages), SETTING(Messages::MessageHeadImageHeight)).toString()); - } - } + if (enclosure.m_mimeType.startsWith(QSL("image/"))) { + // Add thumbnail image. + enclosure_images += skin.m_enclosureImageMarkup.arg( + enclosure.m_url, + enclosure.m_mimeType, + qApp->settings()->value(GROUP(Messages), SETTING(Messages::MessageHeadImageHeight)).toString()); + } + } - messages_layout.append(single_message_layout - .arg(message.m_title, - tr("Written by ") + (message.m_author.isEmpty() ? - tr("unknown author") : - message.m_author), - message.m_url, - message.m_contents, - message.m_created.toString(Qt::DefaultLocaleShortDate), - enclosures, - message.m_isRead ? "mark-unread" : "mark-read", - message.m_isImportant ? "mark-unstarred" : "mark-starred", - QString::number(message.m_id)) - .arg(enclosure_images)); - } + messages_layout.append(single_message_layout + .arg(message.m_title, + tr("Written by ") + (message.m_author.isEmpty() ? + tr("unknown author") : + message.m_author), + message.m_url, + message.m_contents, + message.m_created.toString(Qt::DefaultLocaleShortDate), + enclosures, + message.m_isRead ? "mark-unread" : "mark-read", + message.m_isImportant ? "mark-unstarred" : "mark-starred", + QString::number(message.m_id)) + .arg(enclosure_images)); + } - m_messageContents = skin.m_layoutMarkupWrapper.arg(messages.size() == 1 ? messages.at(0).m_title : tr("Newspaper view"), - messages_layout); - bool previously_enabled = isEnabled(); - - setEnabled(false); - displayMessage(); - setEnabled(previously_enabled); + m_messageContents = skin.m_layoutMarkupWrapper.arg(messages.size() == 1 ? messages.at(0).m_title : tr("Newspaper view"), + messages_layout); + bool previously_enabled = isEnabled(); + setEnabled(false); + displayMessage(); + setEnabled(previously_enabled); } -void WebViewer::loadMessage(const Message &message) { - loadMessages(QList() << message); +void WebViewer::loadMessage(const Message& message) { + loadMessages(QList() << message); } void WebViewer::clear() { - bool previously_enabled = isEnabled(); - - setEnabled(false); - setHtml("", QUrl(INTERNAL_URL_BLANK)); - setEnabled(previously_enabled); + bool previously_enabled = isEnabled(); + setEnabled(false); + setHtml("", QUrl(INTERNAL_URL_BLANK)); + setEnabled(previously_enabled); } -void WebViewer::contextMenuEvent(QContextMenuEvent *event) { - event->accept(); - - QMenu *menu = page()->createStandardContextMenu(); - - menu->addAction(qApp->mainForm()->adblockIcon()->menuAction()); - - const QPoint pos = event->globalPos(); - QPoint p(pos.x(), pos.y() + 1); - menu->popup(p); +void WebViewer::contextMenuEvent(QContextMenuEvent* event) { + event->accept(); + QMenu* menu = page()->createStandardContextMenu(); + menu->addAction(qApp->mainForm()->adblockIcon()->menuAction()); + const QPoint pos = event->globalPos(); + QPoint p(pos.x(), pos.y() + 1); + menu->popup(p); } -QWebEngineView *WebViewer::createWindow(QWebEnginePage::WebWindowType type) { - Q_UNUSED(type) +QWebEngineView* WebViewer::createWindow(QWebEnginePage::WebWindowType type) { + Q_UNUSED(type) + int index = qApp->mainForm()->tabWidget()->addBrowser(false, false); - int index = qApp->mainForm()->tabWidget()->addBrowser(false, false); + if (index >= 0) { + return qApp->mainForm()->tabWidget()->widget(index)->webBrowser()->viewer(); + } - if (index >= 0) { - return qApp->mainForm()->tabWidget()->widget(index)->webBrowser()->viewer(); - } - else { - return nullptr; - } + else { + return nullptr; + } } -void WebViewer::wheelEvent(QWheelEvent *event) { - QWebEngineView::wheelEvent(event); +void WebViewer::wheelEvent(QWheelEvent* event) { + QWebEngineView::wheelEvent(event); - if ((event->modifiers() & Qt::ControlModifier) > 0) { - if (event->delta() > 0) { - increaseWebPageZoom(); - } - else if (event->delta() < 0) { - decreaseWebPageZoom(); - } - } + if ((event->modifiers() & Qt::ControlModifier) > 0) { + if (event->delta() > 0) { + increaseWebPageZoom(); + } + + else if (event->delta() < 0) { + decreaseWebPageZoom(); + } + } } diff --git a/src/gui/webviewer.h b/src/gui/webviewer.h index 0d8651260..e9c710e4c 100755 --- a/src/gui/webviewer.h +++ b/src/gui/webviewer.h @@ -25,42 +25,42 @@ class WebViewer : public QWebEngineView { - Q_OBJECT + Q_OBJECT - public: - explicit WebViewer(QWidget* parent = 0); + public: + explicit WebViewer(QWidget* parent = 0); - bool canIncreaseZoom(); - bool canDecreaseZoom(); + bool canIncreaseZoom(); + bool canDecreaseZoom(); - inline QString messageContents() { - return m_messageContents; - } + inline QString messageContents() { + return m_messageContents; + } - WebPage *page() const; + WebPage* page() const; - public slots: - // Page zoom modifiers. - bool increaseWebPageZoom(); - bool decreaseWebPageZoom(); - bool resetWebPageZoom(); + public slots: + // Page zoom modifiers. + bool increaseWebPageZoom(); + bool decreaseWebPageZoom(); + bool resetWebPageZoom(); - void displayMessage(); - void loadMessages(const QList &messages); - void loadMessage(const Message &message); - void clear(); + void displayMessage(); + void loadMessages(const QList& messages); + void loadMessage(const Message& message); + void clear(); - protected: - void contextMenuEvent(QContextMenuEvent *event); + protected: + void contextMenuEvent(QContextMenuEvent* event); - QWebEngineView *createWindow(QWebEnginePage::WebWindowType type); - void wheelEvent(QWheelEvent *event); + QWebEngineView* createWindow(QWebEnginePage::WebWindowType type); + void wheelEvent(QWheelEvent* event); - signals: - void messageStatusChangeRequested(int message_id, WebPage::MessageStatusChange change); + signals: + void messageStatusChangeRequested(int message_id, WebPage::MessageStatusChange change); - private: - QString m_messageContents; + private: + QString m_messageContents; }; #endif // WEBVIEWER_H diff --git a/src/gui/widgetwithstatus.cpp b/src/gui/widgetwithstatus.cpp index ab0cb9cbf..7a6e1888b 100755 --- a/src/gui/widgetwithstatus.cpp +++ b/src/gui/widgetwithstatus.cpp @@ -1,78 +1,75 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "gui/widgetwithstatus.h" - -#include "gui/plaintoolbutton.h" -#include "miscellaneous/iconfactory.h" - -#include - - -WidgetWithStatus::WidgetWithStatus(QWidget *parent) - : QWidget(parent), m_wdgInput(nullptr) { - m_layout = new QHBoxLayout(this); - m_btnStatus = new PlainToolButton(this); - m_btnStatus->setFocusPolicy(Qt::NoFocus); - - m_iconProgress = qApp->icons()->fromTheme(QSL("view-refresh")); - m_iconInformation = qApp->icons()->fromTheme(QSL("dialog-information")); - m_iconWarning = qApp->icons()->fromTheme(QSL("dialog-warning")); - m_iconError = qApp->icons()->fromTheme(QSL("dialog-error")); - m_iconOk = qApp->icons()->fromTheme(QSL("dialog-yes")); - - // Set layout properties. - m_layout->setMargin(0); - - setLayout(m_layout); - setStatus(Information, QString()); -} - -WidgetWithStatus::~WidgetWithStatus() { -} - -void WidgetWithStatus::setStatus(WidgetWithStatus::StatusType status, const QString &tooltip_text) { - m_status = status; - - switch (status) { - case Information: - m_btnStatus->setIcon(m_iconInformation); - break; - - case Progress: - m_btnStatus->setIcon(m_iconProgress); - break; - - case Warning: - m_btnStatus->setIcon(m_iconWarning); - break; - - case Error: - m_btnStatus->setIcon(m_iconError); - break; - - case Ok: - m_btnStatus->setIcon(m_iconOk); - break; - - default: - break; - } - - // Setup the tooltip text. - m_btnStatus->setToolTip(tooltip_text); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/widgetwithstatus.h" + +#include "gui/plaintoolbutton.h" +#include "miscellaneous/iconfactory.h" + +#include + + +WidgetWithStatus::WidgetWithStatus(QWidget* parent) + : QWidget(parent), m_wdgInput(nullptr) { + m_layout = new QHBoxLayout(this); + m_btnStatus = new PlainToolButton(this); + m_btnStatus->setFocusPolicy(Qt::NoFocus); + m_iconProgress = qApp->icons()->fromTheme(QSL("view-refresh")); + m_iconInformation = qApp->icons()->fromTheme(QSL("dialog-information")); + m_iconWarning = qApp->icons()->fromTheme(QSL("dialog-warning")); + m_iconError = qApp->icons()->fromTheme(QSL("dialog-error")); + m_iconOk = qApp->icons()->fromTheme(QSL("dialog-yes")); + // Set layout properties. + m_layout->setMargin(0); + setLayout(m_layout); + setStatus(Information, QString()); +} + +WidgetWithStatus::~WidgetWithStatus() { +} + +void WidgetWithStatus::setStatus(WidgetWithStatus::StatusType status, const QString& tooltip_text) { + m_status = status; + + switch (status) { + case Information: + m_btnStatus->setIcon(m_iconInformation); + break; + + case Progress: + m_btnStatus->setIcon(m_iconProgress); + break; + + case Warning: + m_btnStatus->setIcon(m_iconWarning); + break; + + case Error: + m_btnStatus->setIcon(m_iconError); + break; + + case Ok: + m_btnStatus->setIcon(m_iconOk); + break; + + default: + break; + } + + // Setup the tooltip text. + m_btnStatus->setToolTip(tooltip_text); +} diff --git a/src/gui/widgetwithstatus.h b/src/gui/widgetwithstatus.h index 9cd5c50f7..ff3c7dbcf 100755 --- a/src/gui/widgetwithstatus.h +++ b/src/gui/widgetwithstatus.h @@ -1,65 +1,65 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef WIDGETWITHSTATUS_H -#define WIDGETWITHSTATUS_H - -#include -#include - - -class PlainToolButton; -class QHBoxLayout; - -class WidgetWithStatus : public QWidget { - Q_OBJECT - - public: - enum StatusType { - Information, - Warning, - Error, - Ok, - Progress - }; - - // Constructors and destructors. - explicit WidgetWithStatus(QWidget *parent); - virtual ~WidgetWithStatus(); - - // Sets custom status for this control. - void setStatus(StatusType status, const QString &tooltip_text); - - inline StatusType status() const { - return m_status; - } - - protected: - StatusType m_status; - QWidget *m_wdgInput; - PlainToolButton *m_btnStatus; - QHBoxLayout *m_layout; - - QIcon m_iconProgress; - QIcon m_iconInformation; - QIcon m_iconWarning; - QIcon m_iconError; - QIcon m_iconOk; -}; - - -#endif // WIDGETWITHSTATUS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef WIDGETWITHSTATUS_H +#define WIDGETWITHSTATUS_H + +#include +#include + + +class PlainToolButton; +class QHBoxLayout; + +class WidgetWithStatus : public QWidget { + Q_OBJECT + + public: + enum StatusType { + Information, + Warning, + Error, + Ok, + Progress + }; + + // Constructors and destructors. + explicit WidgetWithStatus(QWidget* parent); + virtual ~WidgetWithStatus(); + + // Sets custom status for this control. + void setStatus(StatusType status, const QString& tooltip_text); + + inline StatusType status() const { + return m_status; + } + + protected: + StatusType m_status; + QWidget* m_wdgInput; + PlainToolButton* m_btnStatus; + QHBoxLayout* m_layout; + + QIcon m_iconProgress; + QIcon m_iconInformation; + QIcon m_iconWarning; + QIcon m_iconError; + QIcon m_iconOk; +}; + + +#endif // WIDGETWITHSTATUS_H diff --git a/src/main.cpp b/src/main.cpp index bee16db02..90c498cef 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,160 +1,145 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "definitions/definitions.h" -#include "miscellaneous/application.h" -#include "miscellaneous/databasefactory.h" -#include "miscellaneous/debugging.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/feedreader.h" -#include "dynamic-shortcuts/dynamicshortcuts.h" -#include "gui/dialogs/formmain.h" -#include "gui/feedmessageviewer.h" -#include "gui/feedsview.h" -#include "gui/messagebox.h" -#include "network-web/silentnetworkaccessmanager.h" -#include "network-web/webfactory.h" - -// Needed for setting ini file format on Mac OS. -#ifdef Q_OS_MAC -#include -#endif - -#include -#include -#include -#include - -extern void disableWindowTabbing(); - -int main(int argc, char *argv[]) { - for (int i = 0; i < argc; i++) { - const QString str = QString::fromLocal8Bit(argv[i]); - - if (str == "-h") { - qDebug("Usage: rssguard [OPTIONS]\n\n" - "Option\t\tMeaning\n" - "-h\t\tDisplays this help."); - - return EXIT_SUCCESS; - } - } - - //: Abbreviation of language, e.g. en. - //: Use ISO 639-1 code here combined with ISO 3166-1 (alpha-2) code. - //: Examples: "cs", "en", "it", "cs_CZ", "en_GB", "en_US". - QObject::tr("LANG_ABBREV"); - //: Name of translator - optional. - QObject::tr("LANG_AUTHOR"); - - // Ensure that ini format is used as application settings storage on Mac OS. - QSettings::setDefaultFormat(QSettings::IniFormat); - - // Setup debug output system. - qInstallMessageHandler(Debugging::debugHandler); - - // Instantiate base application object. - Application application(APP_LOW_NAME, argc, argv); - qDebug("Instantiated Application class."); - - // Check if another instance is running. - if (application.sendMessage((QStringList() << APP_IS_RUNNING << application.arguments().mid(1)).join(ARGUMENTS_LIST_SEPARATOR))) { - qWarning("Another instance of the application is already running. Notifying it."); - return EXIT_FAILURE; - } - - // Load localization and setup locale before any widget is constructed. - qApp->localization()->loadActiveLanguage(); - - application.setFeedReader(new FeedReader(&application)); - - QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#ifdef Q_OS_MAC - QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); - disableWindowTabbing(); -#endif - - // Register needed metatypes. - qRegisterMetaType >("QList"); - qRegisterMetaType >("QList"); - - // Just call this instance, so that is is created in main GUI thread. - WebFactory::instance(); - - // Add an extra path for non-system icon themes and set current icon theme - // and skin. - qApp->icons()->setupSearchPaths(); - qApp->icons()->loadCurrentIconTheme(); - qApp->skins()->loadCurrentSkin(); - - // These settings needs to be set before any QSettings object. - Application::setApplicationName(APP_NAME); - Application::setApplicationVersion(APP_VERSION); - Application::setOrganizationDomain(APP_URL); - Application::setWindowIcon(QIcon(APP_ICON_PATH)); - - // Load activated accounts. - qApp->feedReader()->feedsModel()->loadActivatedServiceAccounts(); - - // Setup single-instance behavior. - QObject::connect(&application, &Application::messageReceived, &application, &Application::processExecutionMessage); - - qDebug().nospace() << "Creating main application form in thread: \'" << QThread::currentThreadId() << "\'."; - - // Instantiate main application window. - FormMain main_window; - - // Set correct information for main window. - main_window.setWindowTitle(APP_LONG_NAME); - - // Now is a good time to initialize dynamic keyboard shortcuts. - DynamicShortcuts::load(qApp->userActions()); - - // Display main window. - if (qApp->settings()->value(GROUP(GUI), SETTING(GUI::MainWindowStartsHidden)).toBool() && SystemTrayIcon::isSystemTrayActivated()) { - qDebug("Hiding the main window when the application is starting."); - main_window.switchVisibility(true); - } - else { - qDebug("Showing the main window when the application is starting."); - main_window.show(); - } - - // Display tray icon if it is enabled and available. - if (SystemTrayIcon::isSystemTrayActivated()) { - qApp->showTrayIcon(); - } - - if (qApp->isFirstRun() || qApp->isFirstRun(APP_VERSION)) { - qApp->showGuiMessage(QSL(APP_NAME), QObject::tr("Welcome to %1.\n\nPlease, check NEW stuff included in this\n" - "version by clicking this popup notification.").arg(APP_LONG_NAME), - QSystemTrayIcon::NoIcon, 0, false, &main_window, SLOT(showAbout())); - } - else { - qApp->showGuiMessage(QSL(APP_NAME), QObject::tr("Welcome to %1.").arg(APP_NAME), QSystemTrayIcon::NoIcon); - } - - if (qApp->settings()->value(GROUP(General), SETTING(General::UpdateOnStartup)).toBool()) { - QTimer::singleShot(STARTUP_UPDATE_DELAY, application.system(), SLOT(checkForUpdatesOnStartup())); - } - - qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->loadAllExpandStates(); - - // Enter global event loop. - return Application::exec(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "miscellaneous/databasefactory.h" +#include "miscellaneous/debugging.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/feedreader.h" +#include "dynamic-shortcuts/dynamicshortcuts.h" +#include "gui/dialogs/formmain.h" +#include "gui/feedmessageviewer.h" +#include "gui/feedsview.h" +#include "gui/messagebox.h" +#include "network-web/silentnetworkaccessmanager.h" +#include "network-web/webfactory.h" + +// Needed for setting ini file format on Mac OS. +#ifdef Q_OS_MAC +#include +#endif + +#include +#include +#include +#include + +extern void disableWindowTabbing(); + +int main(int argc, char* argv[]) { + for (int i = 0; i < argc; i++) { + const QString str = QString::fromLocal8Bit(argv[i]); + + if (str == "-h") { + qDebug("Usage: rssguard [OPTIONS]\n\n" + "Option\t\tMeaning\n" + "-h\t\tDisplays this help."); + return EXIT_SUCCESS; + } + } + + //: Abbreviation of language, e.g. en. + //: Use ISO 639-1 code here combined with ISO 3166-1 (alpha-2) code. + //: Examples: "cs", "en", "it", "cs_CZ", "en_GB", "en_US". + QObject::tr("LANG_ABBREV"); + //: Name of translator - optional. + QObject::tr("LANG_AUTHOR"); + // Ensure that ini format is used as application settings storage on Mac OS. + QSettings::setDefaultFormat(QSettings::IniFormat); + // Setup debug output system. + qInstallMessageHandler(Debugging::debugHandler); + // Instantiate base application object. + Application application(APP_LOW_NAME, argc, argv); + qDebug("Instantiated Application class."); + + // Check if another instance is running. + if (application.sendMessage((QStringList() << APP_IS_RUNNING << application.arguments().mid(1)).join(ARGUMENTS_LIST_SEPARATOR))) { + qWarning("Another instance of the application is already running. Notifying it."); + return EXIT_FAILURE; + } + + // Load localization and setup locale before any widget is constructed. + qApp->localization()->loadActiveLanguage(); + application.setFeedReader(new FeedReader(&application)); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#ifdef Q_OS_MAC + QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); + disableWindowTabbing(); +#endif + // Register needed metatypes. + qRegisterMetaType>("QList"); + qRegisterMetaType>("QList"); + // Just call this instance, so that is is created in main GUI thread. + WebFactory::instance(); + // Add an extra path for non-system icon themes and set current icon theme + // and skin. + qApp->icons()->setupSearchPaths(); + qApp->icons()->loadCurrentIconTheme(); + qApp->skins()->loadCurrentSkin(); + // These settings needs to be set before any QSettings object. + Application::setApplicationName(APP_NAME); + Application::setApplicationVersion(APP_VERSION); + Application::setOrganizationDomain(APP_URL); + Application::setWindowIcon(QIcon(APP_ICON_PATH)); + // Load activated accounts. + qApp->feedReader()->feedsModel()->loadActivatedServiceAccounts(); + // Setup single-instance behavior. + QObject::connect(&application, &Application::messageReceived, &application, &Application::processExecutionMessage); + qDebug().nospace() << "Creating main application form in thread: \'" << QThread::currentThreadId() << "\'."; + // Instantiate main application window. + FormMain main_window; + // Set correct information for main window. + main_window.setWindowTitle(APP_LONG_NAME); + // Now is a good time to initialize dynamic keyboard shortcuts. + DynamicShortcuts::load(qApp->userActions()); + + // Display main window. + if (qApp->settings()->value(GROUP(GUI), SETTING(GUI::MainWindowStartsHidden)).toBool() && SystemTrayIcon::isSystemTrayActivated()) { + qDebug("Hiding the main window when the application is starting."); + main_window.switchVisibility(true); + } + + else { + qDebug("Showing the main window when the application is starting."); + main_window.show(); + } + + // Display tray icon if it is enabled and available. + if (SystemTrayIcon::isSystemTrayActivated()) { + qApp->showTrayIcon(); + } + + if (qApp->isFirstRun() || qApp->isFirstRun(APP_VERSION)) { + qApp->showGuiMessage(QSL(APP_NAME), QObject::tr("Welcome to %1.\n\nPlease, check NEW stuff included in this\n" + "version by clicking this popup notification.").arg(APP_LONG_NAME), + QSystemTrayIcon::NoIcon, 0, false, &main_window, SLOT(showAbout())); + } + + else { + qApp->showGuiMessage(QSL(APP_NAME), QObject::tr("Welcome to %1.").arg(APP_NAME), QSystemTrayIcon::NoIcon); + } + + if (qApp->settings()->value(GROUP(General), SETTING(General::UpdateOnStartup)).toBool()) { + QTimer::singleShot(STARTUP_UPDATE_DELAY, application.system(), SLOT(checkForUpdatesOnStartup())); + } + + qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->loadAllExpandStates(); + // Enter global event loop. + return Application::exec(); +} diff --git a/src/miscellaneous/application.cpp b/src/miscellaneous/application.cpp index 6ec2eadc9..6a1f4b94b 100755 --- a/src/miscellaneous/application.cpp +++ b/src/miscellaneous/application.cpp @@ -1,462 +1,456 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "miscellaneous/application.h" - -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/iofactory.h" -#include "miscellaneous/mutex.h" -#include "miscellaneous/feedreader.h" -#include "gui/feedsview.h" -#include "gui/feedmessageviewer.h" -#include "gui/messagebox.h" -#include "gui/statusbar.h" -#include "gui/dialogs/formmain.h" -#include "exceptions/applicationexception.h" - -#include "services/abstract/serviceroot.h" -#include "services/standard/standardserviceroot.h" -#include "services/standard/standardserviceentrypoint.h" -#include "services/tt-rss/ttrssserviceentrypoint.h" -#include "services/owncloud/owncloudserviceentrypoint.h" - -#include -#include - -#if defined(USE_WEBENGINE) -#include "network-web/urlinterceptor.h" -#include "network-web/networkurlinterceptor.h" -#include "network-web/adblock/adblockicon.h" -#include "network-web/adblock/adblockmanager.h" - -#include -#include -#endif - -Application::Application(const QString &id, int &argc, char **argv) - : QtSingleApplication(id, argc, argv), - #if defined(USE_WEBENGINE) - m_urlInterceptor(new NetworkUrlInterceptor(this)), - #endif - m_feedReader(nullptr), - m_updateFeedsLock(nullptr), m_userActions(QList()), m_mainForm(nullptr), - m_trayIcon(nullptr), m_settings(nullptr), m_system(nullptr), m_skins(nullptr), - m_localization(nullptr), m_icons(nullptr), m_database(nullptr), m_downloadManager(nullptr), m_shouldRestart(false) { - connect(this, &Application::aboutToQuit, this, &Application::onAboutToQuit); - connect(this, &Application::commitDataRequest, this, &Application::onCommitData); - connect(this, &Application::saveStateRequest, this, &Application::onSaveState); - -#if defined(USE_WEBENGINE) - connect(QWebEngineProfile::defaultProfile(), &QWebEngineProfile::downloadRequested, this, &Application::downloadRequested); - - QWebEngineProfile::defaultProfile()->setRequestInterceptor(m_urlInterceptor); - - // TODO: Teď tam žádný nastavení není, ale jestli se DNT třeba - // přidá do dialogu nastavení, tak toto volat při ukládání nastavení. - m_urlInterceptor->loadSettings(); -#endif -} - -Application::~Application() { - qDebug("Destroying Application instance."); -} - -FeedReader *Application::feedReader() { - return m_feedReader; -} - -QList Application::userActions() { - if (m_mainForm != nullptr && m_userActions.isEmpty()) { - m_userActions = m_mainForm->allActions(); - } - - return m_userActions; -} - -bool Application::isFirstRun() { - return settings()->value(GROUP(General), SETTING(General::FirstRun)).toBool(); -} - -bool Application::isFirstRun(const QString &version) { - if (version == APP_VERSION) { - // Check this only if checked version is equal to actual version. - return settings()->value(GROUP(General), QString(General::FirstRun) + QL1C('_') + version, true).toBool(); - } - else { - return false; - } -} - -SystemFactory *Application::system() { - if (m_system == nullptr) { - m_system = new SystemFactory(this); - } - - return m_system; -} - -SkinFactory *Application::skins() { - if (m_skins == nullptr) { - m_skins = new SkinFactory(this); - } - - return m_skins; -} - -Localization *Application::localization() { - if (m_localization == nullptr) { - m_localization = new Localization(this); - } - - return m_localization; -} - -DatabaseFactory *Application::database() { - if (m_database == nullptr) { - m_database = new DatabaseFactory(this); - } - - return m_database; -} - -void Application::eliminateFirstRun() { - settings()->setValue(GROUP(General), General::FirstRun, false); -} - -void Application::eliminateFirstRun(const QString &version) { - settings()->setValue(GROUP(General), QString(General::FirstRun) + QL1C('_') + version, false); -} - -void Application::setFeedReader(FeedReader *feed_reader) { - m_feedReader = feed_reader; - - connect(m_feedReader, &FeedReader::feedUpdatesStarted, this, &Application::onFeedUpdatesStarted); - connect(m_feedReader, &FeedReader::feedUpdatesProgress, this, &Application::onFeedUpdatesProgress); - connect(m_feedReader, &FeedReader::feedUpdatesFinished, this, &Application::onFeedUpdatesFinished); -} - -IconFactory *Application::icons() { - if (m_icons == nullptr) { - m_icons = new IconFactory(this); - } - - return m_icons; -} - -DownloadManager *Application::downloadManager() { - if (m_downloadManager == nullptr) { - m_downloadManager = new DownloadManager(); - - connect(m_downloadManager, &DownloadManager::downloadFinished, mainForm()->statusBar(), &StatusBar::clearProgressDownload); - connect(m_downloadManager, &DownloadManager::downloadProgressed, mainForm()->statusBar(), &StatusBar::showProgressDownload); - } - - return m_downloadManager; -} - -Settings *Application::settings() { - if (m_settings == nullptr) { - m_settings = Settings::setupSettings(this); - } - - return m_settings; -} - -Mutex *Application::feedUpdateLock() { - if (m_updateFeedsLock.isNull()) { - // NOTE: Cannot use parent hierarchy because this method can be usually called - // from any thread. - m_updateFeedsLock.reset(new Mutex()); - } - - return m_updateFeedsLock.data(); -} - -FormMain *Application::mainForm() { - return m_mainForm; -} - -QWidget *Application::mainFormWidget() { - return m_mainForm; -} - -void Application::setMainForm(FormMain *main_form) { - m_mainForm = main_form; -} - -QString Application::getConfigHomePath() { - return IOFactory::getSystemFolder(QStandardPaths::GenericConfigLocation); -} - -QString Application::getUserDataAppPath() { - // In "app" folder, we would like to separate all user data into own subfolder, - // therefore stick to "data" folder in this mode. - return applicationDirPath() + QDir::separator() + QSL("data"); -} - -QString Application::getUserDataPath() { - if (settings()->type() == SettingsProperties::Portable) { - return getUserDataAppPath(); - } - else { - return getUserDataHomePath(); - } -} - -QString Application::getUserDataHomePath() { - // Fallback folder. - const QString home_folder = getHomeFolderPath() + QDir::separator() + QSL(APP_LOW_H_NAME) + QDir::separator() + QSL("data"); - - if (QDir().exists(home_folder)) { - return home_folder; - } - else { - return getConfigHomePath() + QDir::separator() + QSL(APP_NAME); - } -} - -QString Application::getTempFolderPath() { - return IOFactory::getSystemFolder(QStandardPaths::TempLocation); -} - -QString Application::getDocumentsFolderPath() { - return IOFactory::getSystemFolder(QStandardPaths::DocumentsLocation); -} - -QString Application::getHomeFolderPath() { - return IOFactory::getSystemFolder(QStandardPaths::HomeLocation); -} - -void Application::backupDatabaseSettings(bool backup_database, bool backup_settings, - const QString &target_path, const QString &backup_name) { - if (!QFileInfo(target_path).isWritable()) { - throw ApplicationException(tr("Output directory is not writable.")); - } - - if (backup_settings) { - settings()->sync(); - - if (!IOFactory::copyFile(settings()->fileName(), target_path + QDir::separator() + backup_name + BACKUP_SUFFIX_SETTINGS)) { - throw ApplicationException(tr("Settings file not copied to output directory successfully.")); - } - } - - if (backup_database && - (database()->activeDatabaseDriver() == DatabaseFactory::SQLITE || - database()->activeDatabaseDriver() == DatabaseFactory::SQLITE_MEMORY)) { - // We need to save the database first. - database()->saveDatabase(); - - if (!IOFactory::copyFile(database()->sqliteDatabaseFilePath(), target_path + QDir::separator() + backup_name + BACKUP_SUFFIX_DATABASE)) { - throw ApplicationException(tr("Database file not copied to output directory successfully.")); - } - } -} - -void Application::restoreDatabaseSettings(bool restore_database, bool restore_settings, - const QString &source_database_file_path, const QString &source_settings_file_path) { - if (restore_database) { - if (!qApp->database()->initiateRestoration(source_database_file_path)) { - throw ApplicationException(tr("Database restoration was not initiated. Make sure that output directory is writable.")); - } - } - - if (restore_settings) { - if (!qApp->settings()->initiateRestoration(source_settings_file_path)) { - throw ApplicationException(tr("Settings restoration was not initiated. Make sure that output directory is writable.")); - } - } -} - -void Application::processExecutionMessage(const QString &message) { - qDebug("Received '%s' execution message from another application instance.", qPrintable(message)); - - const QStringList messages = message.split(ARGUMENTS_LIST_SEPARATOR); - - if (messages.contains(APP_QUIT_INSTANCE)) { - quit(); - } - else { - foreach (const QString &msg, messages) { - if (msg == APP_IS_RUNNING) { - showGuiMessage(APP_NAME, tr("Application is already running."), QSystemTrayIcon::Information); - mainForm()->display(); - } - else if (msg.startsWith(QL1S(URI_SCHEME_FEED_SHORT))) { - // Application was running, and someone wants to add new feed. - StandardServiceRoot *root = qApp->feedReader()->feedsModel()->standardServiceRoot(); - - if (root != nullptr) { - root->checkArgumentForFeedAdding(msg); - } - else { - showGuiMessage(tr("Cannot add feed"), - tr("Feed cannot be added because standard RSS/ATOM account is not enabled."), - QSystemTrayIcon::Warning, qApp->mainForm(), - true); - } - } - } - } -} - -SystemTrayIcon *Application::trayIcon() { - if (m_trayIcon == nullptr) { - m_trayIcon = new SystemTrayIcon(APP_ICON_PATH, APP_ICON_PLAIN_PATH, m_mainForm); - connect(m_trayIcon, &SystemTrayIcon::shown, m_feedReader->feedsModel(), &FeedsModel::notifyWithCounts); - connect(m_feedReader->feedsModel(), &FeedsModel::messageCountsChanged, m_trayIcon, &SystemTrayIcon::setNumber); - } - - return m_trayIcon; -} - -#if defined(USE_WEBENGINE) -NetworkUrlInterceptor *Application::urlIinterceptor() { - return m_urlInterceptor; -} -#endif - -void Application::showTrayIcon() { - qDebug("Showing tray icon."); - trayIcon()->show(); -} - -void Application::deleteTrayIcon() { - if (m_trayIcon != nullptr) { - qDebug("Disabling tray icon, deleting it and raising main application window."); - m_mainForm->display(); - delete m_trayIcon; - m_trayIcon = nullptr; - - // Make sure that application quits when last window is closed. - setQuitOnLastWindowClosed(true); - } -} - -void Application::showGuiMessage(const QString &title, const QString &message, - QSystemTrayIcon::MessageIcon message_type, QWidget *parent, - bool show_at_least_msgbox, QObject *invokation_target, - const char *invokation_slot) { - if (SystemTrayIcon::areNotificationsEnabled() && SystemTrayIcon::isSystemTrayActivated()) { - trayIcon()->showMessage(title, message, message_type, TRAY_ICON_BUBBLE_TIMEOUT, invokation_target, invokation_slot); - } - else if (show_at_least_msgbox) { - // Tray icon or OSD is not available, display simple text box. - MessageBox::show(parent, (QMessageBox::Icon) message_type, title, message); - } - else { - qDebug("Silencing GUI message: '%s'.", qPrintable(message)); - } -} - -void Application::onCommitData(QSessionManager &manager) { - qDebug("OS asked application to commit its data."); - - manager.setRestartHint(QSessionManager::RestartNever); - manager.release(); -} - -void Application::onSaveState(QSessionManager &manager) { - qDebug("OS asked application to save its state."); - - manager.setRestartHint(QSessionManager::RestartNever); - manager.release(); -} - -void Application::onAboutToQuit() { - eliminateFirstRun(); - eliminateFirstRun(APP_VERSION); - -#if defined(USE_WEBENGINE) - AdBlockManager::instance()->save(); -#endif - - // Make sure that we obtain close lock BEFORE even trying to quit the application. - const bool locked_safely = feedUpdateLock()->tryLock(4 * CLOSE_LOCK_TIMEOUT); - - processEvents(); - - qDebug("Cleaning up resources and saving application state."); - -#if defined(Q_OS_WIN) - system()->removeTrolltechJunkRegistryKeys(); -#endif - - qApp->feedReader()->quit(); - database()->saveDatabase(); - - if (mainForm() != nullptr) { - mainForm()->saveSize(); - } - - if (locked_safely) { - // Application obtained permission to close in a safe way. - qDebug("Close lock was obtained safely."); - - // We locked the lock to exit peacefully, unlock it to avoid warnings. - feedUpdateLock()->unlock(); - } - else { - // Request for write lock timed-out. This means - // that some critical action can be processed right now. - qDebug("Close lock timed-out."); - } - - // Now, we can check if application should just quit or restart itself. - if (m_shouldRestart) { - finish(); - qDebug("Killing local peer connection to allow another instance to start."); - - // TODO: Start RSS Guard with sleep before it cross-platform way if possible. - // sleep 5 && "". - if (QProcess::startDetached(QString("\"") + QDir::toNativeSeparators(applicationFilePath()) + QString("\""))) { - qDebug("New application instance was started."); - } - else { - qWarning("New application instance was not started successfully."); - } - } -} - -void Application::restart() { - m_shouldRestart = true; - quit(); -} - -#if defined(USE_WEBENGINE) -void Application::downloadRequested(QWebEngineDownloadItem *download_item) { - downloadManager()->download(download_item->url()); - download_item->cancel(); - download_item->deleteLater(); -} -#endif - -void Application::onFeedUpdatesStarted() { -} - -void Application::onFeedUpdatesProgress(const Feed *feed, int current, int total) { - Q_UNUSED(feed) - Q_UNUSED(current) - Q_UNUSED(total) -} - -void Application::onFeedUpdatesFinished(FeedDownloadResults results) { - if (!results.updatedFeeds().isEmpty()) { - // Now, inform about results via GUI message/notification. - qApp->showGuiMessage(tr("New messages downloaded"), results.overview(10), QSystemTrayIcon::NoIcon, 0, false); - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "miscellaneous/application.h" + +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/iofactory.h" +#include "miscellaneous/mutex.h" +#include "miscellaneous/feedreader.h" +#include "gui/feedsview.h" +#include "gui/feedmessageviewer.h" +#include "gui/messagebox.h" +#include "gui/statusbar.h" +#include "gui/dialogs/formmain.h" +#include "exceptions/applicationexception.h" + +#include "services/abstract/serviceroot.h" +#include "services/standard/standardserviceroot.h" +#include "services/standard/standardserviceentrypoint.h" +#include "services/tt-rss/ttrssserviceentrypoint.h" +#include "services/owncloud/owncloudserviceentrypoint.h" + +#include +#include + +#if defined(USE_WEBENGINE) +#include "network-web/urlinterceptor.h" +#include "network-web/networkurlinterceptor.h" +#include "network-web/adblock/adblockicon.h" +#include "network-web/adblock/adblockmanager.h" + +#include +#include +#endif + +Application::Application(const QString& id, int& argc, char** argv) + : QtSingleApplication(id, argc, argv), +#if defined(USE_WEBENGINE) + m_urlInterceptor(new NetworkUrlInterceptor(this)), +#endif + m_feedReader(nullptr), + m_updateFeedsLock(nullptr), m_userActions(QList()), m_mainForm(nullptr), + m_trayIcon(nullptr), m_settings(nullptr), m_system(nullptr), m_skins(nullptr), + m_localization(nullptr), m_icons(nullptr), m_database(nullptr), m_downloadManager(nullptr), m_shouldRestart(false) { + connect(this, &Application::aboutToQuit, this, &Application::onAboutToQuit); + connect(this, &Application::commitDataRequest, this, &Application::onCommitData); + connect(this, &Application::saveStateRequest, this, &Application::onSaveState); +#if defined(USE_WEBENGINE) + connect(QWebEngineProfile::defaultProfile(), &QWebEngineProfile::downloadRequested, this, &Application::downloadRequested); + QWebEngineProfile::defaultProfile()->setRequestInterceptor(m_urlInterceptor); + // TODO: Teď tam žádný nastavení není, ale jestli se DNT třeba + // přidá do dialogu nastavení, tak toto volat při ukládání nastavení. + m_urlInterceptor->loadSettings(); +#endif +} + +Application::~Application() { + qDebug("Destroying Application instance."); +} + +FeedReader* Application::feedReader() { + return m_feedReader; +} + +QList Application::userActions() { + if (m_mainForm != nullptr && m_userActions.isEmpty()) { + m_userActions = m_mainForm->allActions(); + } + + return m_userActions; +} + +bool Application::isFirstRun() { + return settings()->value(GROUP(General), SETTING(General::FirstRun)).toBool(); +} + +bool Application::isFirstRun(const QString& version) { + if (version == APP_VERSION) { + // Check this only if checked version is equal to actual version. + return settings()->value(GROUP(General), QString(General::FirstRun) + QL1C('_') + version, true).toBool(); + } + + else { + return false; + } +} + +SystemFactory* Application::system() { + if (m_system == nullptr) { + m_system = new SystemFactory(this); + } + + return m_system; +} + +SkinFactory* Application::skins() { + if (m_skins == nullptr) { + m_skins = new SkinFactory(this); + } + + return m_skins; +} + +Localization* Application::localization() { + if (m_localization == nullptr) { + m_localization = new Localization(this); + } + + return m_localization; +} + +DatabaseFactory* Application::database() { + if (m_database == nullptr) { + m_database = new DatabaseFactory(this); + } + + return m_database; +} + +void Application::eliminateFirstRun() { + settings()->setValue(GROUP(General), General::FirstRun, false); +} + +void Application::eliminateFirstRun(const QString& version) { + settings()->setValue(GROUP(General), QString(General::FirstRun) + QL1C('_') + version, false); +} + +void Application::setFeedReader(FeedReader* feed_reader) { + m_feedReader = feed_reader; + connect(m_feedReader, &FeedReader::feedUpdatesStarted, this, &Application::onFeedUpdatesStarted); + connect(m_feedReader, &FeedReader::feedUpdatesProgress, this, &Application::onFeedUpdatesProgress); + connect(m_feedReader, &FeedReader::feedUpdatesFinished, this, &Application::onFeedUpdatesFinished); +} + +IconFactory* Application::icons() { + if (m_icons == nullptr) { + m_icons = new IconFactory(this); + } + + return m_icons; +} + +DownloadManager* Application::downloadManager() { + if (m_downloadManager == nullptr) { + m_downloadManager = new DownloadManager(); + connect(m_downloadManager, &DownloadManager::downloadFinished, mainForm()->statusBar(), &StatusBar::clearProgressDownload); + connect(m_downloadManager, &DownloadManager::downloadProgressed, mainForm()->statusBar(), &StatusBar::showProgressDownload); + } + + return m_downloadManager; +} + +Settings* Application::settings() { + if (m_settings == nullptr) { + m_settings = Settings::setupSettings(this); + } + + return m_settings; +} + +Mutex* Application::feedUpdateLock() { + if (m_updateFeedsLock.isNull()) { + // NOTE: Cannot use parent hierarchy because this method can be usually called + // from any thread. + m_updateFeedsLock.reset(new Mutex()); + } + + return m_updateFeedsLock.data(); +} + +FormMain* Application::mainForm() { + return m_mainForm; +} + +QWidget* Application::mainFormWidget() { + return m_mainForm; +} + +void Application::setMainForm(FormMain* main_form) { + m_mainForm = main_form; +} + +QString Application::getConfigHomePath() { + return IOFactory::getSystemFolder(QStandardPaths::GenericConfigLocation); +} + +QString Application::getUserDataAppPath() { + // In "app" folder, we would like to separate all user data into own subfolder, + // therefore stick to "data" folder in this mode. + return applicationDirPath() + QDir::separator() + QSL("data"); +} + +QString Application::getUserDataPath() { + if (settings()->type() == SettingsProperties::Portable) { + return getUserDataAppPath(); + } + + else { + return getUserDataHomePath(); + } +} + +QString Application::getUserDataHomePath() { + // Fallback folder. + const QString home_folder = getHomeFolderPath() + QDir::separator() + QSL(APP_LOW_H_NAME) + QDir::separator() + QSL("data"); + + if (QDir().exists(home_folder)) { + return home_folder; + } + + else { + return getConfigHomePath() + QDir::separator() + QSL(APP_NAME); + } +} + +QString Application::getTempFolderPath() { + return IOFactory::getSystemFolder(QStandardPaths::TempLocation); +} + +QString Application::getDocumentsFolderPath() { + return IOFactory::getSystemFolder(QStandardPaths::DocumentsLocation); +} + +QString Application::getHomeFolderPath() { + return IOFactory::getSystemFolder(QStandardPaths::HomeLocation); +} + +void Application::backupDatabaseSettings(bool backup_database, bool backup_settings, + const QString& target_path, const QString& backup_name) { + if (!QFileInfo(target_path).isWritable()) { + throw ApplicationException(tr("Output directory is not writable.")); + } + + if (backup_settings) { + settings()->sync(); + + if (!IOFactory::copyFile(settings()->fileName(), target_path + QDir::separator() + backup_name + BACKUP_SUFFIX_SETTINGS)) { + throw ApplicationException(tr("Settings file not copied to output directory successfully.")); + } + } + + if (backup_database && + (database()->activeDatabaseDriver() == DatabaseFactory::SQLITE || + database()->activeDatabaseDriver() == DatabaseFactory::SQLITE_MEMORY)) { + // We need to save the database first. + database()->saveDatabase(); + + if (!IOFactory::copyFile(database()->sqliteDatabaseFilePath(), target_path + QDir::separator() + backup_name + BACKUP_SUFFIX_DATABASE)) { + throw ApplicationException(tr("Database file not copied to output directory successfully.")); + } + } +} + +void Application::restoreDatabaseSettings(bool restore_database, bool restore_settings, + const QString& source_database_file_path, const QString& source_settings_file_path) { + if (restore_database) { + if (!qApp->database()->initiateRestoration(source_database_file_path)) { + throw ApplicationException(tr("Database restoration was not initiated. Make sure that output directory is writable.")); + } + } + + if (restore_settings) { + if (!qApp->settings()->initiateRestoration(source_settings_file_path)) { + throw ApplicationException(tr("Settings restoration was not initiated. Make sure that output directory is writable.")); + } + } +} + +void Application::processExecutionMessage(const QString& message) { + qDebug("Received '%s' execution message from another application instance.", qPrintable(message)); + const QStringList messages = message.split(ARGUMENTS_LIST_SEPARATOR); + + if (messages.contains(APP_QUIT_INSTANCE)) { + quit(); + } + + else { + foreach (const QString& msg, messages) { + if (msg == APP_IS_RUNNING) { + showGuiMessage(APP_NAME, tr("Application is already running."), QSystemTrayIcon::Information); + mainForm()->display(); + } + + else if (msg.startsWith(QL1S(URI_SCHEME_FEED_SHORT))) { + // Application was running, and someone wants to add new feed. + StandardServiceRoot* root = qApp->feedReader()->feedsModel()->standardServiceRoot(); + + if (root != nullptr) { + root->checkArgumentForFeedAdding(msg); + } + + else { + showGuiMessage(tr("Cannot add feed"), + tr("Feed cannot be added because standard RSS/ATOM account is not enabled."), + QSystemTrayIcon::Warning, qApp->mainForm(), + true); + } + } + } + } +} + +SystemTrayIcon* Application::trayIcon() { + if (m_trayIcon == nullptr) { + m_trayIcon = new SystemTrayIcon(APP_ICON_PATH, APP_ICON_PLAIN_PATH, m_mainForm); + connect(m_trayIcon, &SystemTrayIcon::shown, m_feedReader->feedsModel(), &FeedsModel::notifyWithCounts); + connect(m_feedReader->feedsModel(), &FeedsModel::messageCountsChanged, m_trayIcon, &SystemTrayIcon::setNumber); + } + + return m_trayIcon; +} + +#if defined(USE_WEBENGINE) +NetworkUrlInterceptor* Application::urlIinterceptor() { + return m_urlInterceptor; +} +#endif + +void Application::showTrayIcon() { + qDebug("Showing tray icon."); + trayIcon()->show(); +} + +void Application::deleteTrayIcon() { + if (m_trayIcon != nullptr) { + qDebug("Disabling tray icon, deleting it and raising main application window."); + m_mainForm->display(); + delete m_trayIcon; + m_trayIcon = nullptr; + // Make sure that application quits when last window is closed. + setQuitOnLastWindowClosed(true); + } +} + +void Application::showGuiMessage(const QString& title, const QString& message, + QSystemTrayIcon::MessageIcon message_type, QWidget* parent, + bool show_at_least_msgbox, QObject* invokation_target, + const char* invokation_slot) { + if (SystemTrayIcon::areNotificationsEnabled() && SystemTrayIcon::isSystemTrayActivated()) { + trayIcon()->showMessage(title, message, message_type, TRAY_ICON_BUBBLE_TIMEOUT, invokation_target, invokation_slot); + } + + else if (show_at_least_msgbox) { + // Tray icon or OSD is not available, display simple text box. + MessageBox::show(parent, (QMessageBox::Icon) message_type, title, message); + } + + else { + qDebug("Silencing GUI message: '%s'.", qPrintable(message)); + } +} + +void Application::onCommitData(QSessionManager& manager) { + qDebug("OS asked application to commit its data."); + manager.setRestartHint(QSessionManager::RestartNever); + manager.release(); +} + +void Application::onSaveState(QSessionManager& manager) { + qDebug("OS asked application to save its state."); + manager.setRestartHint(QSessionManager::RestartNever); + manager.release(); +} + +void Application::onAboutToQuit() { + eliminateFirstRun(); + eliminateFirstRun(APP_VERSION); +#if defined(USE_WEBENGINE) + AdBlockManager::instance()->save(); +#endif + // Make sure that we obtain close lock BEFORE even trying to quit the application. + const bool locked_safely = feedUpdateLock()->tryLock(4 * CLOSE_LOCK_TIMEOUT); + processEvents(); + qDebug("Cleaning up resources and saving application state."); +#if defined(Q_OS_WIN) + system()->removeTrolltechJunkRegistryKeys(); +#endif + qApp->feedReader()->quit(); + database()->saveDatabase(); + + if (mainForm() != nullptr) { + mainForm()->saveSize(); + } + + if (locked_safely) { + // Application obtained permission to close in a safe way. + qDebug("Close lock was obtained safely."); + // We locked the lock to exit peacefully, unlock it to avoid warnings. + feedUpdateLock()->unlock(); + } + + else { + // Request for write lock timed-out. This means + // that some critical action can be processed right now. + qDebug("Close lock timed-out."); + } + + // Now, we can check if application should just quit or restart itself. + if (m_shouldRestart) { + finish(); + qDebug("Killing local peer connection to allow another instance to start."); + + // TODO: Start RSS Guard with sleep before it cross-platform way if possible. + // sleep 5 && "". + if (QProcess::startDetached(QString("\"") + QDir::toNativeSeparators(applicationFilePath()) + QString("\""))) { + qDebug("New application instance was started."); + } + + else { + qWarning("New application instance was not started successfully."); + } + } +} + +void Application::restart() { + m_shouldRestart = true; + quit(); +} + +#if defined(USE_WEBENGINE) +void Application::downloadRequested(QWebEngineDownloadItem* download_item) { + downloadManager()->download(download_item->url()); + download_item->cancel(); + download_item->deleteLater(); +} +#endif + +void Application::onFeedUpdatesStarted() { +} + +void Application::onFeedUpdatesProgress(const Feed* feed, int current, int total) { + Q_UNUSED(feed) + Q_UNUSED(current) + Q_UNUSED(total) +} + +void Application::onFeedUpdatesFinished(FeedDownloadResults results) { + if (!results.updatedFeeds().isEmpty()) { + // Now, inform about results via GUI message/notification. + qApp->showGuiMessage(tr("New messages downloaded"), results.overview(10), QSystemTrayIcon::NoIcon, 0, false); + } +} diff --git a/src/miscellaneous/application.h b/src/miscellaneous/application.h index 3c797e5c8..47ecde42a 100755 --- a/src/miscellaneous/application.h +++ b/src/miscellaneous/application.h @@ -1,184 +1,184 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef APPLICATION_H -#define APPLICATION_H - -#include "qtsingleapplication/qtsingleapplication.h" - -#include "definitions/definitions.h" -#include "miscellaneous/settings.h" -#include "miscellaneous/systemfactory.h" -#include "miscellaneous/skinfactory.h" -#include "miscellaneous/localization.h" -#include "miscellaneous/databasefactory.h" -#include "miscellaneous/iofactory.h" -#include "gui/systemtrayicon.h" -#include "network-web/downloadmanager.h" -#include "core/feeddownloader.h" - -#include - -#if defined(qApp) -#undef qApp -#endif - -// Define new qApp macro. Yeaaaaah. -#define qApp (Application::instance()) - - -class FormMain; -class IconFactory; -class QAction; -class Mutex; -class QWebEngineDownloadItem; -class FeedReader; - -#if defined(USE_WEBENGINE) -class NetworkUrlInterceptor; -#endif - -class Application : public QtSingleApplication { - Q_OBJECT - - public: - // Constructors and destructors. - explicit Application(const QString &id, int &argc, char **argv); - virtual ~Application(); - - FeedReader *feedReader(); - void setFeedReader(FeedReader *feed_reader); - - // Globally accessible actions. - QList userActions(); - - // Check whether this application starts for the first time (ever). - bool isFirstRun(); - - // Check whether GIVEN VERSION of the application starts for the first time. - bool isFirstRun(const QString &version); - - SystemFactory *system(); - SkinFactory *skins(); - Localization *localization(); - DatabaseFactory *database(); - IconFactory *icons(); - DownloadManager *downloadManager(); - Settings *settings(); - Mutex *feedUpdateLock(); - FormMain *mainForm(); - QWidget *mainFormWidget(); - SystemTrayIcon *trayIcon(); - -#if defined(USE_WEBENGINE) - NetworkUrlInterceptor *urlIinterceptor(); -#endif - - QString getTempFolderPath(); - QString getDocumentsFolderPath(); - QString getHomeFolderPath(); - QString getConfigHomePath(); - - // These return user ready folders. - QString getUserDataAppPath(); - QString getUserDataHomePath(); - - // Returns the base folder to which store user data, the "data" folder. - // NOTE: Use this to get correct path under which store user data. - QString getUserDataPath(); - - void setMainForm(FormMain *main_form); - - void backupDatabaseSettings(bool backup_database, bool backup_settings, - const QString &target_path, const QString &backup_name); - void restoreDatabaseSettings(bool restore_database, bool restore_settings, - const QString &source_database_file_path = QString(), - const QString &source_settings_file_path = QString()); - - void showTrayIcon(); - void deleteTrayIcon(); - - // Displays given simple message in tray icon bubble or OSD - // or in message box if tray icon is disabled. - void showGuiMessage(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon message_type, - QWidget *parent = nullptr, bool show_at_least_msgbox = false, - QObject *invokation_target = nullptr, const char *invokation_slot = nullptr); - - // Returns pointer to "GOD" application singleton. - inline static Application *instance() { - return static_cast(QCoreApplication::instance()); - } - - public slots: - // Restarts the application. - void restart(); - - // Processes incoming message from another RSS Guard instance. - void processExecutionMessage(const QString &message); - - private slots: - // Last-minute reactors. - void onCommitData(QSessionManager &manager); - void onSaveState(QSessionManager &manager); - void onAboutToQuit(); - -#if defined(USE_WEBENGINE) - void downloadRequested(QWebEngineDownloadItem*download_item); -#endif - - void onFeedUpdatesStarted(); - void onFeedUpdatesProgress(const Feed *feed, int current, int total); - void onFeedUpdatesFinished(FeedDownloadResults results); - - private: - void eliminateFirstRun(); - void eliminateFirstRun(const QString &version); - - FeedReader *m_feedReader; - - // This read-write lock is used by application on its close. - // Application locks this lock for WRITING. - // This means that if application locks that lock, then - // no other transaction-critical action can acquire lock - // for reading and won't be executed, so no critical action - // will be running when application quits - // - // EACH critical action locks this lock for READING. - // Several actions can lock this lock for reading. - // But of user decides to close the application (in other words, - // tries to lock the lock for writing), then no other - // action will be allowed to lock for reading. - QScopedPointer m_updateFeedsLock; - -#if defined(USE_WEBENGINE) - NetworkUrlInterceptor *m_urlInterceptor; -#endif - - QList m_userActions; - FormMain *m_mainForm; - SystemTrayIcon *m_trayIcon; - Settings *m_settings; - SystemFactory *m_system; - SkinFactory *m_skins; - Localization *m_localization; - IconFactory *m_icons; - DatabaseFactory *m_database; - DownloadManager *m_downloadManager; - bool m_shouldRestart; -}; - -#endif // APPLICATION_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef APPLICATION_H +#define APPLICATION_H + +#include "qtsingleapplication/qtsingleapplication.h" + +#include "definitions/definitions.h" +#include "miscellaneous/settings.h" +#include "miscellaneous/systemfactory.h" +#include "miscellaneous/skinfactory.h" +#include "miscellaneous/localization.h" +#include "miscellaneous/databasefactory.h" +#include "miscellaneous/iofactory.h" +#include "gui/systemtrayicon.h" +#include "network-web/downloadmanager.h" +#include "core/feeddownloader.h" + +#include + +#if defined(qApp) +#undef qApp +#endif + +// Define new qApp macro. Yeaaaaah. +#define qApp (Application::instance()) + + +class FormMain; +class IconFactory; +class QAction; +class Mutex; +class QWebEngineDownloadItem; +class FeedReader; + +#if defined(USE_WEBENGINE) +class NetworkUrlInterceptor; +#endif + +class Application : public QtSingleApplication { + Q_OBJECT + + public: + // Constructors and destructors. + explicit Application(const QString& id, int& argc, char** argv); + virtual ~Application(); + + FeedReader* feedReader(); + void setFeedReader(FeedReader* feed_reader); + + // Globally accessible actions. + QList userActions(); + + // Check whether this application starts for the first time (ever). + bool isFirstRun(); + + // Check whether GIVEN VERSION of the application starts for the first time. + bool isFirstRun(const QString& version); + + SystemFactory* system(); + SkinFactory* skins(); + Localization* localization(); + DatabaseFactory* database(); + IconFactory* icons(); + DownloadManager* downloadManager(); + Settings* settings(); + Mutex* feedUpdateLock(); + FormMain* mainForm(); + QWidget* mainFormWidget(); + SystemTrayIcon* trayIcon(); + +#if defined(USE_WEBENGINE) + NetworkUrlInterceptor* urlIinterceptor(); +#endif + + QString getTempFolderPath(); + QString getDocumentsFolderPath(); + QString getHomeFolderPath(); + QString getConfigHomePath(); + + // These return user ready folders. + QString getUserDataAppPath(); + QString getUserDataHomePath(); + + // Returns the base folder to which store user data, the "data" folder. + // NOTE: Use this to get correct path under which store user data. + QString getUserDataPath(); + + void setMainForm(FormMain* main_form); + + void backupDatabaseSettings(bool backup_database, bool backup_settings, + const QString& target_path, const QString& backup_name); + void restoreDatabaseSettings(bool restore_database, bool restore_settings, + const QString& source_database_file_path = QString(), + const QString& source_settings_file_path = QString()); + + void showTrayIcon(); + void deleteTrayIcon(); + + // Displays given simple message in tray icon bubble or OSD + // or in message box if tray icon is disabled. + void showGuiMessage(const QString& title, const QString& message, QSystemTrayIcon::MessageIcon message_type, + QWidget* parent = nullptr, bool show_at_least_msgbox = false, + QObject* invokation_target = nullptr, const char* invokation_slot = nullptr); + + // Returns pointer to "GOD" application singleton. + inline static Application* instance() { + return static_cast(QCoreApplication::instance()); + } + + public slots: + // Restarts the application. + void restart(); + + // Processes incoming message from another RSS Guard instance. + void processExecutionMessage(const QString& message); + + private slots: + // Last-minute reactors. + void onCommitData(QSessionManager& manager); + void onSaveState(QSessionManager& manager); + void onAboutToQuit(); + +#if defined(USE_WEBENGINE) + void downloadRequested(QWebEngineDownloadItem* download_item); +#endif + + void onFeedUpdatesStarted(); + void onFeedUpdatesProgress(const Feed* feed, int current, int total); + void onFeedUpdatesFinished(FeedDownloadResults results); + + private: + void eliminateFirstRun(); + void eliminateFirstRun(const QString& version); + + FeedReader* m_feedReader; + + // This read-write lock is used by application on its close. + // Application locks this lock for WRITING. + // This means that if application locks that lock, then + // no other transaction-critical action can acquire lock + // for reading and won't be executed, so no critical action + // will be running when application quits + // + // EACH critical action locks this lock for READING. + // Several actions can lock this lock for reading. + // But of user decides to close the application (in other words, + // tries to lock the lock for writing), then no other + // action will be allowed to lock for reading. + QScopedPointer m_updateFeedsLock; + +#if defined(USE_WEBENGINE) + NetworkUrlInterceptor* m_urlInterceptor; +#endif + + QList m_userActions; + FormMain* m_mainForm; + SystemTrayIcon* m_trayIcon; + Settings* m_settings; + SystemFactory* m_system; + SkinFactory* m_skins; + Localization* m_localization; + IconFactory* m_icons; + DatabaseFactory* m_database; + DownloadManager* m_downloadManager; + bool m_shouldRestart; +}; + +#endif // APPLICATION_H diff --git a/src/miscellaneous/autosaver.cpp b/src/miscellaneous/autosaver.cpp index 6f732c3df..8774d9a13 100755 --- a/src/miscellaneous/autosaver.cpp +++ b/src/miscellaneous/autosaver.cpp @@ -25,49 +25,51 @@ #define MAXWAIT 1000 * 15 // seconds -AutoSaver::AutoSaver(QObject *parent) : QObject(parent) { - Q_ASSERT(parent); +AutoSaver::AutoSaver(QObject* parent) : QObject(parent) { + Q_ASSERT(parent); } AutoSaver::~AutoSaver() { - if (m_timer.isActive()) { - qWarning("AutoSaver: still active when destroyed, changes not saved."); + if (m_timer.isActive()) { + qWarning("AutoSaver: still active when destroyed, changes not saved."); - if (parent() && parent()->metaObject()) { - qWarning("Should call saveIfNeccessary."); - } - } + if (parent() && parent()->metaObject()) { + qWarning("Should call saveIfNeccessary."); + } + } } void AutoSaver::changeOccurred() { - if (m_firstChange.isNull()) { - m_firstChange.start(); - } + if (m_firstChange.isNull()) { + m_firstChange.start(); + } - if (m_firstChange.elapsed() > MAXWAIT) { - saveIfNeccessary(); - } - else { - m_timer.start(AUTOSAVE_IN, this); - } + if (m_firstChange.elapsed() > MAXWAIT) { + saveIfNeccessary(); + } + + else { + m_timer.start(AUTOSAVE_IN, this); + } } -void AutoSaver::timerEvent(QTimerEvent *event) { - if (event->timerId() == m_timer.timerId()) { - saveIfNeccessary(); - } - else { - QObject::timerEvent(event); - } +void AutoSaver::timerEvent(QTimerEvent* event) { + if (event->timerId() == m_timer.timerId()) { + saveIfNeccessary(); + } + + else { + QObject::timerEvent(event); + } } void AutoSaver::saveIfNeccessary() { - if (m_timer.isActive()) { - m_timer.stop(); - m_firstChange = QTime(); + if (m_timer.isActive()) { + m_timer.stop(); + m_firstChange = QTime(); - if (!QMetaObject::invokeMethod(parent(), "save", Qt::DirectConnection)) { - qWarning("AutoSaver: error invoking slot save() on parent."); - } - } + if (!QMetaObject::invokeMethod(parent(), "save", Qt::DirectConnection)) { + qWarning("AutoSaver: error invoking slot save() on parent."); + } + } } diff --git a/src/miscellaneous/autosaver.h b/src/miscellaneous/autosaver.h index b3207ece8..8fb5e2b01 100755 --- a/src/miscellaneous/autosaver.h +++ b/src/miscellaneous/autosaver.h @@ -24,23 +24,23 @@ class AutoSaver : public QObject { - Q_OBJECT + Q_OBJECT - public: - explicit AutoSaver(QObject *parent); - virtual ~AutoSaver(); + public: + explicit AutoSaver(QObject* parent); + virtual ~AutoSaver(); - void saveIfNeccessary(); + void saveIfNeccessary(); - public slots: - void changeOccurred(); + public slots: + void changeOccurred(); - protected: - void timerEvent(QTimerEvent *event); + protected: + void timerEvent(QTimerEvent* event); - private: - QBasicTimer m_timer; - QTime m_firstChange; + private: + QBasicTimer m_timer; + QTime m_firstChange; }; #endif // AUTOSAVER_H diff --git a/src/miscellaneous/databasecleaner.cpp b/src/miscellaneous/databasecleaner.cpp index e825a4b2c..acbaac095 100755 --- a/src/miscellaneous/databasecleaner.cpp +++ b/src/miscellaneous/databasecleaner.cpp @@ -1,105 +1,95 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "miscellaneous/databasecleaner.h" - -#include "miscellaneous/application.h" -#include "miscellaneous/databasequeries.h" - -#include -#include - - -DatabaseCleaner::DatabaseCleaner(QObject *parent) : QObject(parent) { -} - -DatabaseCleaner::~DatabaseCleaner() { -} - -void DatabaseCleaner::purgeDatabaseData(const CleanerOrders &which_data) { - qDebug().nospace() << "Performing database cleanup in thread: \'" << QThread::currentThreadId() << "\'."; - - // Inform everyone about the start of the process. - emit purgeStarted(); - - bool result = true; - const int difference = 99 / 8; - int progress = 0; - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - if (which_data.m_removeReadMessages) { - progress += difference; - emit purgeProgress(progress, tr("Removing read messages...")); - - // Remove read messages. - result &= purgeReadMessages(database); - - progress += difference; - emit purgeProgress(progress, tr("Read messages purged...")); - } - - if (which_data.m_removeRecycleBin) { - progress += difference; - emit purgeProgress(progress, tr("Purging recycle bin...")); - - // Remove read messages. - result &= purgeRecycleBin(database); - - progress += difference; - emit purgeProgress(progress, tr("Recycle bin purged...")); - } - - if (which_data.m_removeOldMessages) { - progress += difference; - emit purgeProgress(progress, tr("Removing old messages...")); - - // Remove old messages. - result &= purgeOldMessages(database, which_data.m_barrierForRemovingOldMessagesInDays); - - progress += difference; - emit purgeProgress(progress, tr("Old messages purged...")); - } - - if (which_data.m_shrinkDatabase) { - progress += difference; - emit purgeProgress(progress, tr("Shrinking database file...")); - - // Call driver-specific vacuuming function. - result &= qApp->database()->vacuumDatabase(); - - progress += difference; - emit purgeProgress(progress, tr("Database file shrinked...")); - } - - emit purgeFinished(result); -} - -bool DatabaseCleaner::purgeStarredMessages(const QSqlDatabase &database) { - return DatabaseQueries::purgeImportantMessages(database); -} - -bool DatabaseCleaner::purgeReadMessages(const QSqlDatabase &database) { - return DatabaseQueries::purgeReadMessages(database); -} - -bool DatabaseCleaner::purgeOldMessages(const QSqlDatabase &database, int days) { - return DatabaseQueries::purgeOldMessages(database, days); -} - -bool DatabaseCleaner::purgeRecycleBin(const QSqlDatabase &database) { - return DatabaseQueries::purgeRecycleBin(database); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "miscellaneous/databasecleaner.h" + +#include "miscellaneous/application.h" +#include "miscellaneous/databasequeries.h" + +#include +#include + + +DatabaseCleaner::DatabaseCleaner(QObject* parent) : QObject(parent) { +} + +DatabaseCleaner::~DatabaseCleaner() { +} + +void DatabaseCleaner::purgeDatabaseData(const CleanerOrders& which_data) { + qDebug().nospace() << "Performing database cleanup in thread: \'" << QThread::currentThreadId() << "\'."; + // Inform everyone about the start of the process. + emit purgeStarted(); + bool result = true; + const int difference = 99 / 8; + int progress = 0; + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + + if (which_data.m_removeReadMessages) { + progress += difference; + emit purgeProgress(progress, tr("Removing read messages...")); + // Remove read messages. + result &= purgeReadMessages(database); + progress += difference; + emit purgeProgress(progress, tr("Read messages purged...")); + } + + if (which_data.m_removeRecycleBin) { + progress += difference; + emit purgeProgress(progress, tr("Purging recycle bin...")); + // Remove read messages. + result &= purgeRecycleBin(database); + progress += difference; + emit purgeProgress(progress, tr("Recycle bin purged...")); + } + + if (which_data.m_removeOldMessages) { + progress += difference; + emit purgeProgress(progress, tr("Removing old messages...")); + // Remove old messages. + result &= purgeOldMessages(database, which_data.m_barrierForRemovingOldMessagesInDays); + progress += difference; + emit purgeProgress(progress, tr("Old messages purged...")); + } + + if (which_data.m_shrinkDatabase) { + progress += difference; + emit purgeProgress(progress, tr("Shrinking database file...")); + // Call driver-specific vacuuming function. + result &= qApp->database()->vacuumDatabase(); + progress += difference; + emit purgeProgress(progress, tr("Database file shrinked...")); + } + + emit purgeFinished(result); +} + +bool DatabaseCleaner::purgeStarredMessages(const QSqlDatabase& database) { + return DatabaseQueries::purgeImportantMessages(database); +} + +bool DatabaseCleaner::purgeReadMessages(const QSqlDatabase& database) { + return DatabaseQueries::purgeReadMessages(database); +} + +bool DatabaseCleaner::purgeOldMessages(const QSqlDatabase& database, int days) { + return DatabaseQueries::purgeOldMessages(database, days); +} + +bool DatabaseCleaner::purgeRecycleBin(const QSqlDatabase& database) { + return DatabaseQueries::purgeRecycleBin(database); +} diff --git a/src/miscellaneous/databasecleaner.h b/src/miscellaneous/databasecleaner.h index c1d3d0808..f16ee10d5 100755 --- a/src/miscellaneous/databasecleaner.h +++ b/src/miscellaneous/databasecleaner.h @@ -1,58 +1,58 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef DATABASECLEANER_H -#define DATABASECLEANER_H - -#include - -#include - - -struct CleanerOrders { - bool m_removeReadMessages; - bool m_shrinkDatabase; - bool m_removeOldMessages; - bool m_removeRecycleBin; - bool m_removeStarredMessages; - int m_barrierForRemovingOldMessagesInDays; -}; - -class DatabaseCleaner : public QObject { - Q_OBJECT - - public: - // Constructors. - explicit DatabaseCleaner(QObject *parent = 0); - virtual ~DatabaseCleaner(); - - signals: - void purgeStarted(); - void purgeProgress(int progress, const QString &description); - void purgeFinished(bool result); - - public slots: - void purgeDatabaseData(const CleanerOrders &which_data); - - private: - bool purgeStarredMessages(const QSqlDatabase &database); - bool purgeReadMessages(const QSqlDatabase &database); - bool purgeOldMessages(const QSqlDatabase &database, int days); - bool purgeRecycleBin(const QSqlDatabase &database); -}; - -#endif // DATABASECLEANER_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef DATABASECLEANER_H +#define DATABASECLEANER_H + +#include + +#include + + +struct CleanerOrders { + bool m_removeReadMessages; + bool m_shrinkDatabase; + bool m_removeOldMessages; + bool m_removeRecycleBin; + bool m_removeStarredMessages; + int m_barrierForRemovingOldMessagesInDays; +}; + +class DatabaseCleaner : public QObject { + Q_OBJECT + + public: + // Constructors. + explicit DatabaseCleaner(QObject* parent = 0); + virtual ~DatabaseCleaner(); + + signals: + void purgeStarted(); + void purgeProgress(int progress, const QString& description); + void purgeFinished(bool result); + + public slots: + void purgeDatabaseData(const CleanerOrders& which_data); + + private: + bool purgeStarredMessages(const QSqlDatabase& database); + bool purgeReadMessages(const QSqlDatabase& database); + bool purgeOldMessages(const QSqlDatabase& database, int days); + bool purgeRecycleBin(const QSqlDatabase& database); +}; + +#endif // DATABASECLEANER_H diff --git a/src/miscellaneous/databasefactory.cpp b/src/miscellaneous/databasefactory.cpp index 872f57e94..a1dc3563d 100755 --- a/src/miscellaneous/databasefactory.cpp +++ b/src/miscellaneous/databasefactory.cpp @@ -1,836 +1,835 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "miscellaneous/databasefactory.h" - -#include "miscellaneous/iofactory.h" -#include "miscellaneous/application.h" -#include "miscellaneous/textfactory.h" -#include "gui/messagebox.h" - -#include -#include -#include -#include - - -DatabaseFactory::DatabaseFactory(QObject *parent) - : QObject(parent), - m_mysqlDatabaseInitialized(false), - m_sqliteFileBasedDatabaseinitialized(false), - m_sqliteInMemoryDatabaseInitialized(false) { - setObjectName(QSL("DatabaseFactory")); - determineDriver(); -} - -DatabaseFactory::~DatabaseFactory() { -} - -qint64 DatabaseFactory::getDatabaseFileSize() const { - if (m_activeDatabaseDriver == SQLITE || m_activeDatabaseDriver == SQLITE_MEMORY) { - return QFileInfo(sqliteDatabaseFilePath()).size(); - } - else { - return 0; - } -} - -qint64 DatabaseFactory::getDatabaseDataSize() const { - if (m_activeDatabaseDriver == SQLITE || m_activeDatabaseDriver == SQLITE_MEMORY) { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - qint64 result = 1; - QSqlQuery query(database); - - if (query.exec(QSL("PRAGMA page_count;"))) { - query.next(); - result *= query.value(0).value(); - } - else { - return 0; - } - - if (query.exec(QSL("PRAGMA page_size;"))) { - query.next(); - result *= query.value(0).value(); - } - else { - return 0; - } - - return result; - } - else if (m_activeDatabaseDriver == MYSQL) { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - qint64 result = 1; - QSqlQuery query(database); - - if (query.exec("SELECT Round(Sum(data_length + index_length), 1) " - "FROM information_schema.tables " - "GROUP BY table_schema;")) { - while (query.next()) { - result *= query.value(0).value(); - } - - return result; - } - else { - return 0; - } - } - else { - return 0; - } -} - -DatabaseFactory::MySQLError DatabaseFactory::mysqlTestConnection(const QString &hostname, int port, const QString &w_database, - const QString &username, const QString &password) { - QSqlDatabase database = QSqlDatabase::addDatabase(APP_DB_MYSQL_DRIVER, APP_DB_MYSQL_TEST); - - database.setHostName(hostname); - database.setPort(port); - database.setUserName(username); - database.setPassword(password); - database.setDatabaseName(w_database); - - if (database.open() && !database.lastError().isValid()) { - QSqlQuery query(QSL("SELECT version();"), database); - - if (!query.lastError().isValid() && query.next()) { - qDebug("Checked MySQL database, version is '%s'.", qPrintable(query.value(0).toString())); - - // Connection succeeded, clean up the mess and return OK status. - database.close(); - return MySQLOk; - } - else { - database.close(); - return MySQLUnknownError; - } - } - else if (database.lastError().isValid()) { - // Connection failed, do cleanup and return specific error code. - return static_cast(database.lastError().number()); - } - else { - return MySQLUnknownError; - } -} - -QString DatabaseFactory::mysqlInterpretErrorCode(MySQLError error_code) const { - switch (error_code) { - case MySQLOk: - return tr("MySQL server works as expected."); - - case MySQLUnknownDatabase: - return tr("Selected database does not exist (yet). It will be created. It's okay."); - - case MySQLCantConnect: - case MySQLConnectionError: - case MySQLUnknownHost: - return tr("No MySQL server is running in the target destination."); - - case MySQLAccessDenied: - //: Access to MySQL server was denied. - return tr("Access denied. Invalid username or password used."); - - default: - //: Unknown MySQL error arised. - return tr("Unknown error."); - } -} - -bool DatabaseFactory::initiateRestoration(const QString &database_backup_file_path) { - switch (m_activeDatabaseDriver) { - case SQLITE: - case SQLITE_MEMORY: - return IOFactory::copyFile(database_backup_file_path, - m_sqliteDatabaseFilePath + QDir::separator() + - BACKUP_NAME_DATABASE + BACKUP_SUFFIX_DATABASE); - - default: - return false; - } -} - -void DatabaseFactory::finishRestoration() { - if (m_activeDatabaseDriver != SQLITE && m_activeDatabaseDriver != SQLITE_MEMORY) { - return; - } - - const QString backup_database_file = m_sqliteDatabaseFilePath + QDir::separator() + BACKUP_NAME_DATABASE + BACKUP_SUFFIX_DATABASE; - - if (QFile::exists(backup_database_file)) { - qWarning("Backup database file '%s' was detected. Restoring it.", qPrintable(QDir::toNativeSeparators(backup_database_file))); - - if (IOFactory::copyFile(backup_database_file, m_sqliteDatabaseFilePath + QDir::separator() + APP_DB_SQLITE_FILE)) { - QFile::remove(backup_database_file); - qDebug("Database file was restored successully."); - } - else { - qCritical("Database file was NOT restored due to error when copying the file."); - } - } -} - -void DatabaseFactory::sqliteAssemblyDatabaseFilePath() { - m_sqliteDatabaseFilePath = qApp->getUserDataPath() + QDir::separator() + QString(APP_DB_SQLITE_PATH); -} - -QSqlDatabase DatabaseFactory::sqliteInitializeInMemoryDatabase() { - QSqlDatabase database = QSqlDatabase::addDatabase(APP_DB_SQLITE_DRIVER); - - database.setDatabaseName(QSL(":memory:")); - - if (!database.open()) { - qFatal("In-memory SQLite database was NOT opened. Delivered error message: '%s'", qPrintable(database.lastError().text())); - } - else { - QSqlQuery query_db(database); - - query_db.setForwardOnly(true); - query_db.exec(QSL("PRAGMA encoding = \"UTF-8\"")); - query_db.exec(QSL("PRAGMA synchronous = OFF")); - query_db.exec(QSL("PRAGMA journal_mode = MEMORY")); - query_db.exec(QSL("PRAGMA page_size = 4096")); - query_db.exec(QSL("PRAGMA cache_size = 16384")); - query_db.exec(QSL("PRAGMA count_changes = OFF")); - query_db.exec(QSL("PRAGMA temp_store = MEMORY")); - - // Sample query which checks for existence of tables. - query_db.exec(QSL("SELECT inf_value FROM Information WHERE inf_key = 'schema_version'")); - - if (query_db.lastError().isValid()) { - qWarning("Error occurred. In-memory SQLite database is not initialized. Initializing now."); - - QFile file_init(APP_SQL_PATH + QDir::separator() + APP_DB_SQLITE_INIT); - - if (!file_init.open(QIODevice::ReadOnly | QIODevice::Text)) { - // Database initialization file not opened. HUGE problem. - qFatal("In-memory SQLite database initialization file '%s' from directory '%s' was not found. In-memory database is uninitialized.", - APP_DB_SQLITE_INIT, - qPrintable(APP_SQL_PATH)); - } - - const QStringList statements = QString(file_init.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); - database.transaction(); - - foreach(const QString &statement, statements) { - query_db.exec(statement); - - if (query_db.lastError().isValid()) { - qFatal("In-memory SQLite database initialization failed. Initialization script '%s' is not correct.", APP_DB_SQLITE_INIT); - } - } - - database.commit(); - qDebug("In-memory SQLite database backend should be ready now."); - } - else { - query_db.next(); - - qDebug("In-memory SQLite database connection seems to be established."); - qDebug("In-memory SQLite database has version '%s'.", qPrintable(query_db.value(0).toString())); - } - - // Loading messages from file-based database. - QSqlDatabase file_database = sqliteConnection(objectName(), StrictlyFileBased); - QSqlQuery copy_contents(database); - - // Attach database. - copy_contents.exec(QString("ATTACH DATABASE '%1' AS 'storage';").arg(file_database.databaseName())); - - // Copy all stuff. - // WARNING: All tables belong here. - QStringList tables; - - if (copy_contents.exec(QSL("SELECT name FROM storage.sqlite_master WHERE type='table';"))) { - while (copy_contents.next()) { - tables.append(copy_contents.value(0).toString()); - } - } - else { - qFatal("Cannot obtain list of table names from file-base SQLite database."); - } - - foreach (const QString &table, tables) { - copy_contents.exec(QString("INSERT INTO main.%1 SELECT * FROM storage.%1;").arg(table)); - } - - qDebug("Copying data from file-based database into working in-memory database."); - - // Detach database and finish. - copy_contents.exec(QSL("DETACH 'storage'")); - copy_contents.finish(); - - query_db.finish(); - } - - // Everything is initialized now. - m_sqliteInMemoryDatabaseInitialized = true; - - return database; -} - -QSqlDatabase DatabaseFactory::sqliteInitializeFileBasedDatabase(const QString &connection_name) { - finishRestoration(); - - // Prepare file paths. - const QDir db_path(m_sqliteDatabaseFilePath); - QFile db_file(db_path.absoluteFilePath(APP_DB_SQLITE_FILE)); - - // Check if database directory exists. - if (!db_path.exists()) { - if (!db_path.mkpath(db_path.absolutePath())) { - // Failure when create database file path. - qFatal("Directory '%s' for SQLite database file '%s' was NOT created." - "This is HUGE problem.", - qPrintable(db_path.absolutePath()), - qPrintable(db_file.symLinkTarget())); - } - } - - // Folders are created. Create new QSQLDatabase object. - QSqlDatabase database; - - database = QSqlDatabase::addDatabase(APP_DB_SQLITE_DRIVER, connection_name); - database.setDatabaseName(db_file.fileName()); - - if (!database.open()) { - qFatal("File-based SQLite database was NOT opened. Delivered error message: '%s'", - qPrintable(database.lastError().text())); - } - else { - QSqlQuery query_db(database); - - query_db.setForwardOnly(true); - query_db.exec(QSL("PRAGMA encoding = \"UTF-8\"")); - query_db.exec(QSL("PRAGMA synchronous = OFF")); - query_db.exec(QSL("PRAGMA journal_mode = MEMORY")); - query_db.exec(QSL("PRAGMA page_size = 4096")); - query_db.exec(QSL("PRAGMA cache_size = 16384")); - query_db.exec(QSL("PRAGMA count_changes = OFF")); - query_db.exec(QSL("PRAGMA temp_store = MEMORY")); - - // Sample query which checks for existence of tables. - if (!query_db.exec(QSL("SELECT inf_value FROM Information WHERE inf_key = 'schema_version'"))) { - qWarning("Error occurred. File-based SQLite database is not initialized. Initializing now."); - - QFile file_init(APP_SQL_PATH + QDir::separator() + APP_DB_SQLITE_INIT); - - if (!file_init.open(QIODevice::ReadOnly | QIODevice::Text)) { - // Database initialization file not opened. HUGE problem. - qFatal("SQLite database initialization file '%s' from directory '%s' was not found. File-based database is uninitialized.", - APP_DB_SQLITE_INIT, - qPrintable(APP_SQL_PATH)); - } - - const QStringList statements = QString(file_init.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); - database.transaction(); - - foreach(const QString &statement, statements) { - query_db.exec(statement); - - if (query_db.lastError().isValid()) { - qFatal("File-based SQLite database initialization failed. Initialization script '%s' is not correct.", - APP_DB_SQLITE_INIT); - } - } - - database.commit(); - query_db.finish(); - qDebug("File-based SQLite database backend should be ready now."); - } - else { - query_db.next(); - const QString installed_db_schema = query_db.value(0).toString(); - query_db.finish(); - - if (installed_db_schema < APP_DB_SCHEMA_VERSION) { - if (sqliteUpdateDatabaseSchema(database, installed_db_schema)) { - qDebug("Database schema was updated from '%s' to '%s' successully or it is already up to date.", - qPrintable(installed_db_schema), - APP_DB_SCHEMA_VERSION); - } - else { - qFatal("Database schema was not updated from '%s' to '%s' successully.", - qPrintable(installed_db_schema), - APP_DB_SCHEMA_VERSION); - } - } - - qDebug("File-based SQLite database connection '%s' to file '%s' seems to be established.", - qPrintable(connection_name), - qPrintable(QDir::toNativeSeparators(database.databaseName()))); - qDebug("File-based SQLite database has version '%s'.", qPrintable(installed_db_schema)); - } - } - - // Everything is initialized now. - m_sqliteFileBasedDatabaseinitialized = true; - - return database; -} - -QString DatabaseFactory::sqliteDatabaseFilePath() const { - return m_sqliteDatabaseFilePath + QDir::separator() + APP_DB_SQLITE_FILE; -} - -bool DatabaseFactory::sqliteUpdateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version) { - int working_version = QString(source_db_schema_version).remove('.').toInt(); - const int current_version = QString(APP_DB_SCHEMA_VERSION).remove('.').toInt(); - - // Now, it would be good to create backup of SQLite DB file. - if (IOFactory::copyFile(sqliteDatabaseFilePath(), sqliteDatabaseFilePath() + ".bak")) { - qDebug("Creating backup of SQLite DB file."); - } - else { - qFatal("Creation of backup SQLite DB file failed."); - } - - while (working_version != current_version) { - const QString update_file_name = QString(APP_SQL_PATH) + QDir::separator() + - QString(APP_DB_UPDATE_FILE_PATTERN).arg(QSL("sqlite"), - QString::number(working_version), - QString::number(working_version + 1)); - - if (!QFile::exists(update_file_name)) { - qFatal("Updating of database schema failed. File '%s' does not exist.", qPrintable(QDir::toNativeSeparators(update_file_name))); - } - - QFile update_file_handle(update_file_name); - - if (!update_file_handle.open(QIODevice::Text | QIODevice::ReadOnly | QIODevice::Unbuffered)) { - qFatal("Updating of database schema failed. File '%s' cannot be opened.", qPrintable(QDir::toNativeSeparators(update_file_name))); - } - - const QStringList statements = QString(update_file_handle.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); - - foreach (const QString &statement, statements) { - QSqlQuery query = database.exec(statement); - - if (query.lastError().isValid()) { - qFatal("Query for updating database schema failed: '%s'.", qPrintable(query.lastError().text())); - } - } - - // Increment the version. - qDebug("Updating database schema: '%d' -> '%d'.", working_version, working_version + 1); - working_version++; - } - - return true; -} - -bool DatabaseFactory::mysqlUpdateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version, const QString &db_name) { - int working_version = QString(source_db_schema_version).remove('.').toInt(); - const int current_version = QString(APP_DB_SCHEMA_VERSION).remove('.').toInt(); - - while (working_version != current_version) { - const QString update_file_name = QString(APP_SQL_PATH) + QDir::separator() + - QString(APP_DB_UPDATE_FILE_PATTERN).arg(QSL("mysql"), - QString::number(working_version), - QString::number(working_version + 1)); - - if (!QFile::exists(update_file_name)) { - qFatal("Updating of database schema failed. File '%s' does not exist.", qPrintable(QDir::toNativeSeparators(update_file_name))); - } - - QFile update_file_handle(update_file_name); - - if (!update_file_handle.open(QIODevice::Text | QIODevice::ReadOnly | QIODevice::Unbuffered)) { - qFatal("Updating of database schema failed. File '%s' cannot be opened.", qPrintable(QDir::toNativeSeparators(update_file_name))); - } - - QStringList statements = QString(update_file_handle.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); - - foreach (QString statement, statements) { - QSqlQuery query = database.exec(statement.replace(APP_DB_NAME_PLACEHOLDER, db_name)); - - if (query.lastError().isValid()) { - qFatal("Query for updating database schema failed: '%s'.", qPrintable(query.lastError().text())); - } - } - - // Increment the version. - qDebug("Updating database schema: '%d' -> '%d'.", working_version, working_version + 1); - working_version++; - } - - return true; -} - -QSqlDatabase DatabaseFactory::connection(const QString &connection_name, DesiredType desired_type) { - switch (m_activeDatabaseDriver) { - case MYSQL: - return mysqlConnection(connection_name); - - case SQLITE: - case SQLITE_MEMORY: - default: - return sqliteConnection(connection_name, desired_type); - } -} - -QString DatabaseFactory::humanDriverName(DatabaseFactory::UsedDriver driver) const { - switch (driver) { - case MYSQL: - return tr("MySQL/MariaDB (dedicated database)"); - - case SQLITE: - case SQLITE_MEMORY: - default: - return tr("SQLite (embedded database)"); - } -} - -QString DatabaseFactory::humanDriverName(const QString &driver_code) const { - if (driver_code == APP_DB_SQLITE_DRIVER) { - return humanDriverName(SQLITE); - } - else if (driver_code == APP_DB_MYSQL_DRIVER) { - return humanDriverName(MYSQL); - } - else { - return humanDriverName(SQLITE); - } -} - -void DatabaseFactory::removeConnection(const QString &connection_name) { - qDebug("Removing database connection '%s'.", qPrintable(connection_name)); - QSqlDatabase::removeDatabase(connection_name); -} - -QString DatabaseFactory::obtainBeginTransactionSql() const { - if (m_activeDatabaseDriver == DatabaseFactory::SQLITE || m_activeDatabaseDriver == DatabaseFactory::SQLITE_MEMORY) { - return QSL("BEGIN IMMEDIATE TRANSACTION;"); - } - else { - return QSL("START TRANSACTION;"); - } -} - -void DatabaseFactory::sqliteSaveMemoryDatabase() { - qDebug("Saving in-memory working database back to persistent file-based storage."); - - QSqlDatabase database = sqliteConnection(objectName(), StrictlyInMemory); - QSqlDatabase file_database = sqliteConnection(objectName(), StrictlyFileBased); - QSqlQuery copy_contents(database); - - // Attach database. - copy_contents.exec(QString(QSL("ATTACH DATABASE '%1' AS 'storage';")).arg(file_database.databaseName())); - - // Copy all stuff. - // WARNING: All tables belong here. - QStringList tables; - - if (copy_contents.exec(QSL("SELECT name FROM storage.sqlite_master WHERE type='table';"))) { - while (copy_contents.next()) { - tables.append(copy_contents.value(0).toString()); - } - } - else { - qFatal("Cannot obtain list of table names from file-base SQLite database."); - } - - foreach (const QString &table, tables) { - copy_contents.exec(QString(QSL("DELETE FROM storage.%1;")).arg(table)); - copy_contents.exec(QString(QSL("INSERT INTO storage.%1 SELECT * FROM main.%1;")).arg(table)); - } - - // Detach database and finish. - copy_contents.exec(QSL("DETACH 'storage'")); - copy_contents.finish(); -} - -void DatabaseFactory::determineDriver() { - const QString db_driver = qApp->settings()->value(GROUP(Database), SETTING(Database::ActiveDriver)).toString(); - - if (db_driver == APP_DB_MYSQL_DRIVER && QSqlDatabase::isDriverAvailable(APP_DB_SQLITE_DRIVER)) { - // User wants to use MySQL and MySQL is actually available. Use it. - m_activeDatabaseDriver = MYSQL; - - qDebug("Working database source was as MySQL database."); - } - else { - // User wants to use SQLite, which is always available. Check if file-based - // or in-memory database will be used. - if (qApp->settings()->value(GROUP(Database), SETTING(Database::UseInMemory)).toBool()) { - // Use in-memory SQLite database. - m_activeDatabaseDriver = SQLITE_MEMORY; - - qDebug("Working database source was determined as SQLite in-memory database."); - } - else { - // Use strictly file-base SQLite database. - m_activeDatabaseDriver = SQLITE; - - qDebug("Working database source was determined as SQLite file-based database."); - } - - sqliteAssemblyDatabaseFilePath(); - } -} - -DatabaseFactory::UsedDriver DatabaseFactory::activeDatabaseDriver() const { - return m_activeDatabaseDriver; -} - -QSqlDatabase DatabaseFactory::mysqlConnection(const QString &connection_name) { - if (!m_mysqlDatabaseInitialized) { - // Return initialized database. - return mysqlInitializeDatabase(connection_name); - } - else { - QSqlDatabase database; - - if (QSqlDatabase::contains(connection_name)) { - qDebug("MySQL connection '%s' is already active.", qPrintable(connection_name)); - - // This database connection was added previously, no need to - // setup its properties. - database = QSqlDatabase::database(connection_name); - } - else { - // Database connection with this name does not exist - // yet, add it and set it up. - database = QSqlDatabase::addDatabase(APP_DB_MYSQL_DRIVER, connection_name); - - database.setHostName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLHostname)).toString()); - database.setPort(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLPort)).toInt()); - database.setUserName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLUsername)).toString()); - database.setPassword(TextFactory::decrypt(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLPassword)).toString())); - database.setDatabaseName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLDatabase)).toString()); - } - - if (!database.isOpen() && !database.open()) { - qFatal("MySQL database was NOT opened. Delivered error message: '%s'.", - qPrintable(database.lastError().text())); - } - else { - qDebug("MySQL database connection '%s' to file '%s' seems to be established.", - qPrintable(connection_name), - qPrintable(QDir::toNativeSeparators(database.databaseName()))); - } - - return database; - } -} - -QSqlDatabase DatabaseFactory::mysqlInitializeDatabase(const QString &connection_name) { - // Folders are created. Create new QSQLDatabase object. - QSqlDatabase database = QSqlDatabase::addDatabase(APP_DB_MYSQL_DRIVER, connection_name); - const QString database_name = qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLDatabase)).toString(); - - database.setHostName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLHostname)).toString()); - database.setPort(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLPort)).toInt()); - database.setUserName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLUsername)).toString()); - database.setPassword(TextFactory::decrypt(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLPassword)).toString())); - - if (!database.open()) { - qCritical("MySQL database was NOT opened. Delivered error message: '%s'", qPrintable(database.lastError().text())); - - // Now, we will display error warning and return SQLite connection. - // Also, we set the SQLite driver as active one. - qApp->settings()->setValue(GROUP(Database), Database::ActiveDriver, APP_DB_SQLITE_DRIVER); - determineDriver(); - MessageBox::show(nullptr, QMessageBox::Critical, tr("MySQL database not available"), - tr("%1 cannot use MySQL storage, it is not available. %1 is now switching to SQLite database. Start your MySQL server " - "and make adjustments in application settings.").arg(APP_NAME)); - - return connection(objectName(), FromSettings); - } - else { - QSqlQuery query_db(database); - query_db.setForwardOnly(true); - - if (!query_db.exec(QString("USE %1").arg(database_name)) || !query_db.exec(QSL("SELECT inf_value FROM Information WHERE inf_key = 'schema_version'"))) { - // If no "rssguard" database exists or schema version is wrong, then initialize it. - qWarning("Error occurred. MySQL database is not initialized. Initializing now."); - - QFile file_init(APP_SQL_PATH + QDir::separator() + APP_DB_MYSQL_INIT); - - if (!file_init.open(QIODevice::ReadOnly | QIODevice::Text)) { - // Database initialization file not opened. HUGE problem. - qFatal("MySQL database initialization file '%s' from directory '%s' was not found. File-based database is uninitialized.", - APP_DB_MYSQL_INIT, - qPrintable(APP_SQL_PATH)); - } - - const QStringList statements = QString(file_init.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); - database.transaction(); - - foreach(QString statement, statements) { - // Assign real database name and run the query. - query_db.exec(statement.replace(APP_DB_NAME_PLACEHOLDER, database_name)); - - if (query_db.lastError().isValid()) { - qFatal("MySQL database initialization failed. Initialization script '%s' is not correct. Error : '%s'.", - APP_DB_MYSQL_INIT, qPrintable(query_db.lastError().databaseText())); - } - } - - database.commit(); - qDebug("MySQL database backend should be ready now."); - } - else { - // Database was previously initialized. Now just check the schema version. - query_db.next(); - - const QString installed_db_schema = query_db.value(0).toString(); - - if (installed_db_schema < APP_DB_SCHEMA_VERSION) { - if (mysqlUpdateDatabaseSchema(database, installed_db_schema, database_name)) { - qDebug("Database schema was updated from '%s' to '%s' successully or it is already up to date.", - qPrintable(installed_db_schema), - APP_DB_SCHEMA_VERSION); - - } - else { - qFatal("Database schema was not updated from '%s' to '%s' successully.", - qPrintable(installed_db_schema), - APP_DB_SCHEMA_VERSION); - } - } - } - - query_db.finish(); - } - - // Everything is initialized now. - m_mysqlDatabaseInitialized = true; - - return database; -} - -bool DatabaseFactory::mysqlVacuumDatabase() { - QSqlDatabase database = mysqlConnection(objectName()); - QSqlQuery query_vacuum(database); - - return query_vacuum.exec(QSL("OPTIMIZE TABLE rssguard.feeds;")) && query_vacuum.exec(QSL("OPTIMIZE TABLE rssguard.messages;")); -} - -QSqlDatabase DatabaseFactory::sqliteConnection(const QString &connection_name, DatabaseFactory::DesiredType desired_type) { - if (desired_type == DatabaseFactory::StrictlyInMemory || - (desired_type == DatabaseFactory::FromSettings && m_activeDatabaseDriver == SQLITE_MEMORY)) { - // We request in-memory database (either user explicitly - // needs in-memory database or it was enabled in the settings). - if (!m_sqliteInMemoryDatabaseInitialized) { - // It is not initialized yet. - return sqliteInitializeInMemoryDatabase(); - } - else { - QSqlDatabase database = QSqlDatabase::database(); - - database.setDatabaseName(QSL(":memory:")); - - if (!database.isOpen() && !database.open()) { - qFatal("In-memory SQLite database was NOT opened. Delivered error message: '%s'.", - qPrintable(database.lastError().text())); - } - else { - qDebug("In-memory SQLite database connection seems to be established."); - } - - return database; - } - } - else { - // We request file-based database. - if (!m_sqliteFileBasedDatabaseinitialized) { - // File-based database is not yet initialised. - return sqliteInitializeFileBasedDatabase(connection_name); - } - else { - QSqlDatabase database; - - if (QSqlDatabase::contains(connection_name)) { - qDebug("SQLite connection '%s' is already active.", qPrintable(connection_name)); - - // This database connection was added previously, no need to - // setup its properties. - database = QSqlDatabase::database(connection_name); - } - else { - // Database connection with this name does not exist - // yet, add it and set it up. - database = QSqlDatabase::addDatabase(APP_DB_SQLITE_DRIVER, connection_name); - - const QDir db_path(m_sqliteDatabaseFilePath); - QFile db_file(db_path.absoluteFilePath(APP_DB_SQLITE_FILE)); - - // Setup database file path. - database.setDatabaseName(db_file.fileName()); - } - - if (!database.isOpen() && !database.open()) { - qFatal("File-based SQLite database was NOT opened. Delivered error message: '%s'.", - qPrintable(database.lastError().text())); - } - else { - qDebug("File-based SQLite database connection '%s' to file '%s' seems to be established.", - qPrintable(connection_name), - qPrintable(QDir::toNativeSeparators(database.databaseName()))); - } - - return database; - } - } -} - -bool DatabaseFactory::sqliteVacuumDatabase() { - QSqlDatabase database; - - if (m_activeDatabaseDriver == SQLITE) { - database = sqliteConnection(objectName(), StrictlyFileBased); - } - else if (m_activeDatabaseDriver == SQLITE_MEMORY) { - sqliteSaveMemoryDatabase(); - database = sqliteConnection(objectName(), StrictlyFileBased); - } - else { - return false; - } - - QSqlQuery query_vacuum(database); - - return query_vacuum.exec(QSL("VACUUM")); -} - -void DatabaseFactory::saveDatabase() { - switch (m_activeDatabaseDriver) { - case SQLITE_MEMORY: - sqliteSaveMemoryDatabase(); - break; - - default: - break; - } -} - -bool DatabaseFactory::vacuumDatabase() { - switch (m_activeDatabaseDriver) { - case SQLITE_MEMORY: - case SQLITE: - return sqliteVacuumDatabase(); - - case MYSQL: - return mysqlVacuumDatabase(); - - default: - return false; - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "miscellaneous/databasefactory.h" + +#include "miscellaneous/iofactory.h" +#include "miscellaneous/application.h" +#include "miscellaneous/textfactory.h" +#include "gui/messagebox.h" + +#include +#include +#include +#include + + +DatabaseFactory::DatabaseFactory(QObject* parent) + : QObject(parent), + m_mysqlDatabaseInitialized(false), + m_sqliteFileBasedDatabaseinitialized(false), + m_sqliteInMemoryDatabaseInitialized(false) { + setObjectName(QSL("DatabaseFactory")); + determineDriver(); +} + +DatabaseFactory::~DatabaseFactory() { +} + +qint64 DatabaseFactory::getDatabaseFileSize() const { + if (m_activeDatabaseDriver == SQLITE || m_activeDatabaseDriver == SQLITE_MEMORY) { + return QFileInfo(sqliteDatabaseFilePath()).size(); + } + + else { + return 0; + } +} + +qint64 DatabaseFactory::getDatabaseDataSize() const { + if (m_activeDatabaseDriver == SQLITE || m_activeDatabaseDriver == SQLITE_MEMORY) { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + qint64 result = 1; + QSqlQuery query(database); + + if (query.exec(QSL("PRAGMA page_count;"))) { + query.next(); + result *= query.value(0).value(); + } + + else { + return 0; + } + + if (query.exec(QSL("PRAGMA page_size;"))) { + query.next(); + result *= query.value(0).value(); + } + + else { + return 0; + } + + return result; + } + + else if (m_activeDatabaseDriver == MYSQL) { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + qint64 result = 1; + QSqlQuery query(database); + + if (query.exec("SELECT Round(Sum(data_length + index_length), 1) " + "FROM information_schema.tables " + "GROUP BY table_schema;")) { + while (query.next()) { + result *= query.value(0).value(); + } + + return result; + } + + else { + return 0; + } + } + + else { + return 0; + } +} + +DatabaseFactory::MySQLError DatabaseFactory::mysqlTestConnection(const QString& hostname, int port, const QString& w_database, + const QString& username, const QString& password) { + QSqlDatabase database = QSqlDatabase::addDatabase(APP_DB_MYSQL_DRIVER, APP_DB_MYSQL_TEST); + database.setHostName(hostname); + database.setPort(port); + database.setUserName(username); + database.setPassword(password); + database.setDatabaseName(w_database); + + if (database.open() && !database.lastError().isValid()) { + QSqlQuery query(QSL("SELECT version();"), database); + + if (!query.lastError().isValid() && query.next()) { + qDebug("Checked MySQL database, version is '%s'.", qPrintable(query.value(0).toString())); + // Connection succeeded, clean up the mess and return OK status. + database.close(); + return MySQLOk; + } + + else { + database.close(); + return MySQLUnknownError; + } + } + + else if (database.lastError().isValid()) { + // Connection failed, do cleanup and return specific error code. + return static_cast(database.lastError().number()); + } + + else { + return MySQLUnknownError; + } +} + +QString DatabaseFactory::mysqlInterpretErrorCode(MySQLError error_code) const { + switch (error_code) { + case MySQLOk: + return tr("MySQL server works as expected."); + + case MySQLUnknownDatabase: + return tr("Selected database does not exist (yet). It will be created. It's okay."); + + case MySQLCantConnect: + case MySQLConnectionError: + case MySQLUnknownHost: + return tr("No MySQL server is running in the target destination."); + + case MySQLAccessDenied: + //: Access to MySQL server was denied. + return tr("Access denied. Invalid username or password used."); + + default: + //: Unknown MySQL error arised. + return tr("Unknown error."); + } +} + +bool DatabaseFactory::initiateRestoration(const QString& database_backup_file_path) { + switch (m_activeDatabaseDriver) { + case SQLITE: + case SQLITE_MEMORY: + return IOFactory::copyFile(database_backup_file_path, + m_sqliteDatabaseFilePath + QDir::separator() + + BACKUP_NAME_DATABASE + BACKUP_SUFFIX_DATABASE); + + default: + return false; + } +} + +void DatabaseFactory::finishRestoration() { + if (m_activeDatabaseDriver != SQLITE && m_activeDatabaseDriver != SQLITE_MEMORY) { + return; + } + + const QString backup_database_file = m_sqliteDatabaseFilePath + QDir::separator() + BACKUP_NAME_DATABASE + BACKUP_SUFFIX_DATABASE; + + if (QFile::exists(backup_database_file)) { + qWarning("Backup database file '%s' was detected. Restoring it.", qPrintable(QDir::toNativeSeparators(backup_database_file))); + + if (IOFactory::copyFile(backup_database_file, m_sqliteDatabaseFilePath + QDir::separator() + APP_DB_SQLITE_FILE)) { + QFile::remove(backup_database_file); + qDebug("Database file was restored successully."); + } + + else { + qCritical("Database file was NOT restored due to error when copying the file."); + } + } +} + +void DatabaseFactory::sqliteAssemblyDatabaseFilePath() { + m_sqliteDatabaseFilePath = qApp->getUserDataPath() + QDir::separator() + QString(APP_DB_SQLITE_PATH); +} + +QSqlDatabase DatabaseFactory::sqliteInitializeInMemoryDatabase() { + QSqlDatabase database = QSqlDatabase::addDatabase(APP_DB_SQLITE_DRIVER); + database.setDatabaseName(QSL(":memory:")); + + if (!database.open()) { + qFatal("In-memory SQLite database was NOT opened. Delivered error message: '%s'", qPrintable(database.lastError().text())); + } + + else { + QSqlQuery query_db(database); + query_db.setForwardOnly(true); + query_db.exec(QSL("PRAGMA encoding = \"UTF-8\"")); + query_db.exec(QSL("PRAGMA synchronous = OFF")); + query_db.exec(QSL("PRAGMA journal_mode = MEMORY")); + query_db.exec(QSL("PRAGMA page_size = 4096")); + query_db.exec(QSL("PRAGMA cache_size = 16384")); + query_db.exec(QSL("PRAGMA count_changes = OFF")); + query_db.exec(QSL("PRAGMA temp_store = MEMORY")); + // Sample query which checks for existence of tables. + query_db.exec(QSL("SELECT inf_value FROM Information WHERE inf_key = 'schema_version'")); + + if (query_db.lastError().isValid()) { + qWarning("Error occurred. In-memory SQLite database is not initialized. Initializing now."); + QFile file_init(APP_SQL_PATH + QDir::separator() + APP_DB_SQLITE_INIT); + + if (!file_init.open(QIODevice::ReadOnly | QIODevice::Text)) { + // Database initialization file not opened. HUGE problem. + qFatal("In-memory SQLite database initialization file '%s' from directory '%s' was not found. In-memory database is uninitialized.", + APP_DB_SQLITE_INIT, + qPrintable(APP_SQL_PATH)); + } + + const QStringList statements = QString(file_init.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); + database.transaction(); + + foreach (const QString& statement, statements) { + query_db.exec(statement); + + if (query_db.lastError().isValid()) { + qFatal("In-memory SQLite database initialization failed. Initialization script '%s' is not correct.", APP_DB_SQLITE_INIT); + } + } + + database.commit(); + qDebug("In-memory SQLite database backend should be ready now."); + } + + else { + query_db.next(); + qDebug("In-memory SQLite database connection seems to be established."); + qDebug("In-memory SQLite database has version '%s'.", qPrintable(query_db.value(0).toString())); + } + + // Loading messages from file-based database. + QSqlDatabase file_database = sqliteConnection(objectName(), StrictlyFileBased); + QSqlQuery copy_contents(database); + // Attach database. + copy_contents.exec(QString("ATTACH DATABASE '%1' AS 'storage';").arg(file_database.databaseName())); + // Copy all stuff. + // WARNING: All tables belong here. + QStringList tables; + + if (copy_contents.exec(QSL("SELECT name FROM storage.sqlite_master WHERE type='table';"))) { + while (copy_contents.next()) { + tables.append(copy_contents.value(0).toString()); + } + } + + else { + qFatal("Cannot obtain list of table names from file-base SQLite database."); + } + + foreach (const QString& table, tables) { + copy_contents.exec(QString("INSERT INTO main.%1 SELECT * FROM storage.%1;").arg(table)); + } + + qDebug("Copying data from file-based database into working in-memory database."); + // Detach database and finish. + copy_contents.exec(QSL("DETACH 'storage'")); + copy_contents.finish(); + query_db.finish(); + } + + // Everything is initialized now. + m_sqliteInMemoryDatabaseInitialized = true; + return database; +} + +QSqlDatabase DatabaseFactory::sqliteInitializeFileBasedDatabase(const QString& connection_name) { + finishRestoration(); + // Prepare file paths. + const QDir db_path(m_sqliteDatabaseFilePath); + QFile db_file(db_path.absoluteFilePath(APP_DB_SQLITE_FILE)); + + // Check if database directory exists. + if (!db_path.exists()) { + if (!db_path.mkpath(db_path.absolutePath())) { + // Failure when create database file path. + qFatal("Directory '%s' for SQLite database file '%s' was NOT created." + "This is HUGE problem.", + qPrintable(db_path.absolutePath()), + qPrintable(db_file.symLinkTarget())); + } + } + + // Folders are created. Create new QSQLDatabase object. + QSqlDatabase database; + database = QSqlDatabase::addDatabase(APP_DB_SQLITE_DRIVER, connection_name); + database.setDatabaseName(db_file.fileName()); + + if (!database.open()) { + qFatal("File-based SQLite database was NOT opened. Delivered error message: '%s'", + qPrintable(database.lastError().text())); + } + + else { + QSqlQuery query_db(database); + query_db.setForwardOnly(true); + query_db.exec(QSL("PRAGMA encoding = \"UTF-8\"")); + query_db.exec(QSL("PRAGMA synchronous = OFF")); + query_db.exec(QSL("PRAGMA journal_mode = MEMORY")); + query_db.exec(QSL("PRAGMA page_size = 4096")); + query_db.exec(QSL("PRAGMA cache_size = 16384")); + query_db.exec(QSL("PRAGMA count_changes = OFF")); + query_db.exec(QSL("PRAGMA temp_store = MEMORY")); + + // Sample query which checks for existence of tables. + if (!query_db.exec(QSL("SELECT inf_value FROM Information WHERE inf_key = 'schema_version'"))) { + qWarning("Error occurred. File-based SQLite database is not initialized. Initializing now."); + QFile file_init(APP_SQL_PATH + QDir::separator() + APP_DB_SQLITE_INIT); + + if (!file_init.open(QIODevice::ReadOnly | QIODevice::Text)) { + // Database initialization file not opened. HUGE problem. + qFatal("SQLite database initialization file '%s' from directory '%s' was not found. File-based database is uninitialized.", + APP_DB_SQLITE_INIT, + qPrintable(APP_SQL_PATH)); + } + + const QStringList statements = QString(file_init.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); + database.transaction(); + + foreach (const QString& statement, statements) { + query_db.exec(statement); + + if (query_db.lastError().isValid()) { + qFatal("File-based SQLite database initialization failed. Initialization script '%s' is not correct.", + APP_DB_SQLITE_INIT); + } + } + + database.commit(); + query_db.finish(); + qDebug("File-based SQLite database backend should be ready now."); + } + + else { + query_db.next(); + const QString installed_db_schema = query_db.value(0).toString(); + query_db.finish(); + + if (installed_db_schema < APP_DB_SCHEMA_VERSION) { + if (sqliteUpdateDatabaseSchema(database, installed_db_schema)) { + qDebug("Database schema was updated from '%s' to '%s' successully or it is already up to date.", + qPrintable(installed_db_schema), + APP_DB_SCHEMA_VERSION); + } + + else { + qFatal("Database schema was not updated from '%s' to '%s' successully.", + qPrintable(installed_db_schema), + APP_DB_SCHEMA_VERSION); + } + } + + qDebug("File-based SQLite database connection '%s' to file '%s' seems to be established.", + qPrintable(connection_name), + qPrintable(QDir::toNativeSeparators(database.databaseName()))); + qDebug("File-based SQLite database has version '%s'.", qPrintable(installed_db_schema)); + } + } + + // Everything is initialized now. + m_sqliteFileBasedDatabaseinitialized = true; + return database; +} + +QString DatabaseFactory::sqliteDatabaseFilePath() const { + return m_sqliteDatabaseFilePath + QDir::separator() + APP_DB_SQLITE_FILE; +} + +bool DatabaseFactory::sqliteUpdateDatabaseSchema(QSqlDatabase database, const QString& source_db_schema_version) { + int working_version = QString(source_db_schema_version).remove('.').toInt(); + const int current_version = QString(APP_DB_SCHEMA_VERSION).remove('.').toInt(); + + // Now, it would be good to create backup of SQLite DB file. + if (IOFactory::copyFile(sqliteDatabaseFilePath(), sqliteDatabaseFilePath() + ".bak")) { + qDebug("Creating backup of SQLite DB file."); + } + + else { + qFatal("Creation of backup SQLite DB file failed."); + } + + while (working_version != current_version) { + const QString update_file_name = QString(APP_SQL_PATH) + QDir::separator() + + QString(APP_DB_UPDATE_FILE_PATTERN).arg(QSL("sqlite"), + QString::number(working_version), + QString::number(working_version + 1)); + + if (!QFile::exists(update_file_name)) { + qFatal("Updating of database schema failed. File '%s' does not exist.", qPrintable(QDir::toNativeSeparators(update_file_name))); + } + + QFile update_file_handle(update_file_name); + + if (!update_file_handle.open(QIODevice::Text | QIODevice::ReadOnly | QIODevice::Unbuffered)) { + qFatal("Updating of database schema failed. File '%s' cannot be opened.", qPrintable(QDir::toNativeSeparators(update_file_name))); + } + + const QStringList statements = QString(update_file_handle.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); + + foreach (const QString& statement, statements) { + QSqlQuery query = database.exec(statement); + + if (query.lastError().isValid()) { + qFatal("Query for updating database schema failed: '%s'.", qPrintable(query.lastError().text())); + } + } + + // Increment the version. + qDebug("Updating database schema: '%d' -> '%d'.", working_version, working_version + 1); + working_version++; + } + + return true; +} + +bool DatabaseFactory::mysqlUpdateDatabaseSchema(QSqlDatabase database, const QString& source_db_schema_version, const QString& db_name) { + int working_version = QString(source_db_schema_version).remove('.').toInt(); + const int current_version = QString(APP_DB_SCHEMA_VERSION).remove('.').toInt(); + + while (working_version != current_version) { + const QString update_file_name = QString(APP_SQL_PATH) + QDir::separator() + + QString(APP_DB_UPDATE_FILE_PATTERN).arg(QSL("mysql"), + QString::number(working_version), + QString::number(working_version + 1)); + + if (!QFile::exists(update_file_name)) { + qFatal("Updating of database schema failed. File '%s' does not exist.", qPrintable(QDir::toNativeSeparators(update_file_name))); + } + + QFile update_file_handle(update_file_name); + + if (!update_file_handle.open(QIODevice::Text | QIODevice::ReadOnly | QIODevice::Unbuffered)) { + qFatal("Updating of database schema failed. File '%s' cannot be opened.", qPrintable(QDir::toNativeSeparators(update_file_name))); + } + + QStringList statements = QString(update_file_handle.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); + + foreach (QString statement, statements) { + QSqlQuery query = database.exec(statement.replace(APP_DB_NAME_PLACEHOLDER, db_name)); + + if (query.lastError().isValid()) { + qFatal("Query for updating database schema failed: '%s'.", qPrintable(query.lastError().text())); + } + } + + // Increment the version. + qDebug("Updating database schema: '%d' -> '%d'.", working_version, working_version + 1); + working_version++; + } + + return true; +} + +QSqlDatabase DatabaseFactory::connection(const QString& connection_name, DesiredType desired_type) { + switch (m_activeDatabaseDriver) { + case MYSQL: + return mysqlConnection(connection_name); + + case SQLITE: + case SQLITE_MEMORY: + default: + return sqliteConnection(connection_name, desired_type); + } +} + +QString DatabaseFactory::humanDriverName(DatabaseFactory::UsedDriver driver) const { + switch (driver) { + case MYSQL: + return tr("MySQL/MariaDB (dedicated database)"); + + case SQLITE: + case SQLITE_MEMORY: + default: + return tr("SQLite (embedded database)"); + } +} + +QString DatabaseFactory::humanDriverName(const QString& driver_code) const { + if (driver_code == APP_DB_SQLITE_DRIVER) { + return humanDriverName(SQLITE); + } + + else if (driver_code == APP_DB_MYSQL_DRIVER) { + return humanDriverName(MYSQL); + } + + else { + return humanDriverName(SQLITE); + } +} + +void DatabaseFactory::removeConnection(const QString& connection_name) { + qDebug("Removing database connection '%s'.", qPrintable(connection_name)); + QSqlDatabase::removeDatabase(connection_name); +} + +QString DatabaseFactory::obtainBeginTransactionSql() const { + if (m_activeDatabaseDriver == DatabaseFactory::SQLITE || m_activeDatabaseDriver == DatabaseFactory::SQLITE_MEMORY) { + return QSL("BEGIN IMMEDIATE TRANSACTION;"); + } + + else { + return QSL("START TRANSACTION;"); + } +} + +void DatabaseFactory::sqliteSaveMemoryDatabase() { + qDebug("Saving in-memory working database back to persistent file-based storage."); + QSqlDatabase database = sqliteConnection(objectName(), StrictlyInMemory); + QSqlDatabase file_database = sqliteConnection(objectName(), StrictlyFileBased); + QSqlQuery copy_contents(database); + // Attach database. + copy_contents.exec(QString(QSL("ATTACH DATABASE '%1' AS 'storage';")).arg(file_database.databaseName())); + // Copy all stuff. + // WARNING: All tables belong here. + QStringList tables; + + if (copy_contents.exec(QSL("SELECT name FROM storage.sqlite_master WHERE type='table';"))) { + while (copy_contents.next()) { + tables.append(copy_contents.value(0).toString()); + } + } + + else { + qFatal("Cannot obtain list of table names from file-base SQLite database."); + } + + foreach (const QString& table, tables) { + copy_contents.exec(QString(QSL("DELETE FROM storage.%1;")).arg(table)); + copy_contents.exec(QString(QSL("INSERT INTO storage.%1 SELECT * FROM main.%1;")).arg(table)); + } + + // Detach database and finish. + copy_contents.exec(QSL("DETACH 'storage'")); + copy_contents.finish(); +} + +void DatabaseFactory::determineDriver() { + const QString db_driver = qApp->settings()->value(GROUP(Database), SETTING(Database::ActiveDriver)).toString(); + + if (db_driver == APP_DB_MYSQL_DRIVER && QSqlDatabase::isDriverAvailable(APP_DB_SQLITE_DRIVER)) { + // User wants to use MySQL and MySQL is actually available. Use it. + m_activeDatabaseDriver = MYSQL; + qDebug("Working database source was as MySQL database."); + } + + else { + // User wants to use SQLite, which is always available. Check if file-based + // or in-memory database will be used. + if (qApp->settings()->value(GROUP(Database), SETTING(Database::UseInMemory)).toBool()) { + // Use in-memory SQLite database. + m_activeDatabaseDriver = SQLITE_MEMORY; + qDebug("Working database source was determined as SQLite in-memory database."); + } + + else { + // Use strictly file-base SQLite database. + m_activeDatabaseDriver = SQLITE; + qDebug("Working database source was determined as SQLite file-based database."); + } + + sqliteAssemblyDatabaseFilePath(); + } +} + +DatabaseFactory::UsedDriver DatabaseFactory::activeDatabaseDriver() const { + return m_activeDatabaseDriver; +} + +QSqlDatabase DatabaseFactory::mysqlConnection(const QString& connection_name) { + if (!m_mysqlDatabaseInitialized) { + // Return initialized database. + return mysqlInitializeDatabase(connection_name); + } + + else { + QSqlDatabase database; + + if (QSqlDatabase::contains(connection_name)) { + qDebug("MySQL connection '%s' is already active.", qPrintable(connection_name)); + // This database connection was added previously, no need to + // setup its properties. + database = QSqlDatabase::database(connection_name); + } + + else { + // Database connection with this name does not exist + // yet, add it and set it up. + database = QSqlDatabase::addDatabase(APP_DB_MYSQL_DRIVER, connection_name); + database.setHostName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLHostname)).toString()); + database.setPort(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLPort)).toInt()); + database.setUserName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLUsername)).toString()); + database.setPassword(TextFactory::decrypt(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLPassword)).toString())); + database.setDatabaseName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLDatabase)).toString()); + } + + if (!database.isOpen() && !database.open()) { + qFatal("MySQL database was NOT opened. Delivered error message: '%s'.", + qPrintable(database.lastError().text())); + } + + else { + qDebug("MySQL database connection '%s' to file '%s' seems to be established.", + qPrintable(connection_name), + qPrintable(QDir::toNativeSeparators(database.databaseName()))); + } + + return database; + } +} + +QSqlDatabase DatabaseFactory::mysqlInitializeDatabase(const QString& connection_name) { + // Folders are created. Create new QSQLDatabase object. + QSqlDatabase database = QSqlDatabase::addDatabase(APP_DB_MYSQL_DRIVER, connection_name); + const QString database_name = qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLDatabase)).toString(); + database.setHostName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLHostname)).toString()); + database.setPort(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLPort)).toInt()); + database.setUserName(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLUsername)).toString()); + database.setPassword(TextFactory::decrypt(qApp->settings()->value(GROUP(Database), SETTING(Database::MySQLPassword)).toString())); + + if (!database.open()) { + qCritical("MySQL database was NOT opened. Delivered error message: '%s'", qPrintable(database.lastError().text())); + // Now, we will display error warning and return SQLite connection. + // Also, we set the SQLite driver as active one. + qApp->settings()->setValue(GROUP(Database), Database::ActiveDriver, APP_DB_SQLITE_DRIVER); + determineDriver(); + MessageBox::show(nullptr, QMessageBox::Critical, tr("MySQL database not available"), + tr("%1 cannot use MySQL storage, it is not available. %1 is now switching to SQLite database. Start your MySQL server " + "and make adjustments in application settings.").arg(APP_NAME)); + return connection(objectName(), FromSettings); + } + + else { + QSqlQuery query_db(database); + query_db.setForwardOnly(true); + + if (!query_db.exec(QString("USE %1").arg(database_name)) || !query_db.exec(QSL("SELECT inf_value FROM Information WHERE inf_key = 'schema_version'"))) { + // If no "rssguard" database exists or schema version is wrong, then initialize it. + qWarning("Error occurred. MySQL database is not initialized. Initializing now."); + QFile file_init(APP_SQL_PATH + QDir::separator() + APP_DB_MYSQL_INIT); + + if (!file_init.open(QIODevice::ReadOnly | QIODevice::Text)) { + // Database initialization file not opened. HUGE problem. + qFatal("MySQL database initialization file '%s' from directory '%s' was not found. File-based database is uninitialized.", + APP_DB_MYSQL_INIT, + qPrintable(APP_SQL_PATH)); + } + + const QStringList statements = QString(file_init.readAll()).split(APP_DB_COMMENT_SPLIT, QString::SkipEmptyParts); + database.transaction(); + + foreach (QString statement, statements) { + // Assign real database name and run the query. + query_db.exec(statement.replace(APP_DB_NAME_PLACEHOLDER, database_name)); + + if (query_db.lastError().isValid()) { + qFatal("MySQL database initialization failed. Initialization script '%s' is not correct. Error : '%s'.", + APP_DB_MYSQL_INIT, qPrintable(query_db.lastError().databaseText())); + } + } + + database.commit(); + qDebug("MySQL database backend should be ready now."); + } + + else { + // Database was previously initialized. Now just check the schema version. + query_db.next(); + const QString installed_db_schema = query_db.value(0).toString(); + + if (installed_db_schema < APP_DB_SCHEMA_VERSION) { + if (mysqlUpdateDatabaseSchema(database, installed_db_schema, database_name)) { + qDebug("Database schema was updated from '%s' to '%s' successully or it is already up to date.", + qPrintable(installed_db_schema), + APP_DB_SCHEMA_VERSION); + } + + else { + qFatal("Database schema was not updated from '%s' to '%s' successully.", + qPrintable(installed_db_schema), + APP_DB_SCHEMA_VERSION); + } + } + } + + query_db.finish(); + } + + // Everything is initialized now. + m_mysqlDatabaseInitialized = true; + return database; +} + +bool DatabaseFactory::mysqlVacuumDatabase() { + QSqlDatabase database = mysqlConnection(objectName()); + QSqlQuery query_vacuum(database); + return query_vacuum.exec(QSL("OPTIMIZE TABLE rssguard.feeds;")) && query_vacuum.exec(QSL("OPTIMIZE TABLE rssguard.messages;")); +} + +QSqlDatabase DatabaseFactory::sqliteConnection(const QString& connection_name, DatabaseFactory::DesiredType desired_type) { + if (desired_type == DatabaseFactory::StrictlyInMemory || + (desired_type == DatabaseFactory::FromSettings && m_activeDatabaseDriver == SQLITE_MEMORY)) { + // We request in-memory database (either user explicitly + // needs in-memory database or it was enabled in the settings). + if (!m_sqliteInMemoryDatabaseInitialized) { + // It is not initialized yet. + return sqliteInitializeInMemoryDatabase(); + } + + else { + QSqlDatabase database = QSqlDatabase::database(); + database.setDatabaseName(QSL(":memory:")); + + if (!database.isOpen() && !database.open()) { + qFatal("In-memory SQLite database was NOT opened. Delivered error message: '%s'.", + qPrintable(database.lastError().text())); + } + + else { + qDebug("In-memory SQLite database connection seems to be established."); + } + + return database; + } + } + + else { + // We request file-based database. + if (!m_sqliteFileBasedDatabaseinitialized) { + // File-based database is not yet initialised. + return sqliteInitializeFileBasedDatabase(connection_name); + } + + else { + QSqlDatabase database; + + if (QSqlDatabase::contains(connection_name)) { + qDebug("SQLite connection '%s' is already active.", qPrintable(connection_name)); + // This database connection was added previously, no need to + // setup its properties. + database = QSqlDatabase::database(connection_name); + } + + else { + // Database connection with this name does not exist + // yet, add it and set it up. + database = QSqlDatabase::addDatabase(APP_DB_SQLITE_DRIVER, connection_name); + const QDir db_path(m_sqliteDatabaseFilePath); + QFile db_file(db_path.absoluteFilePath(APP_DB_SQLITE_FILE)); + // Setup database file path. + database.setDatabaseName(db_file.fileName()); + } + + if (!database.isOpen() && !database.open()) { + qFatal("File-based SQLite database was NOT opened. Delivered error message: '%s'.", + qPrintable(database.lastError().text())); + } + + else { + qDebug("File-based SQLite database connection '%s' to file '%s' seems to be established.", + qPrintable(connection_name), + qPrintable(QDir::toNativeSeparators(database.databaseName()))); + } + + return database; + } + } +} + +bool DatabaseFactory::sqliteVacuumDatabase() { + QSqlDatabase database; + + if (m_activeDatabaseDriver == SQLITE) { + database = sqliteConnection(objectName(), StrictlyFileBased); + } + + else if (m_activeDatabaseDriver == SQLITE_MEMORY) { + sqliteSaveMemoryDatabase(); + database = sqliteConnection(objectName(), StrictlyFileBased); + } + + else { + return false; + } + + QSqlQuery query_vacuum(database); + return query_vacuum.exec(QSL("VACUUM")); +} + +void DatabaseFactory::saveDatabase() { + switch (m_activeDatabaseDriver) { + case SQLITE_MEMORY: + sqliteSaveMemoryDatabase(); + break; + + default: + break; + } +} + +bool DatabaseFactory::vacuumDatabase() { + switch (m_activeDatabaseDriver) { + case SQLITE_MEMORY: + case SQLITE: + return sqliteVacuumDatabase(); + + case MYSQL: + return mysqlVacuumDatabase(); + + default: + return false; + } +} diff --git a/src/miscellaneous/databasefactory.h b/src/miscellaneous/databasefactory.h index 31cfc76cc..225e7ab9f 100755 --- a/src/miscellaneous/databasefactory.h +++ b/src/miscellaneous/databasefactory.h @@ -1,181 +1,181 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef DATABASEFACTORY_H -#define DATABASEFACTORY_H - -#include -#include - - -class DatabaseFactory : public QObject { - Q_OBJECT - - public: - // Describes available typos of database backend. - enum UsedDriver { - SQLITE, - SQLITE_MEMORY, - MYSQL - }; - - // Describes what type of database user wants. - enum DesiredType { - StrictlyFileBased, - StrictlyInMemory, - FromSettings - }; - - // Describes possible MySQL-specific errors. - enum MySQLError { - MySQLOk = 0, - MySQLUnknownError = 1, - MySQLAccessDenied = 1045, - MySQLUnknownDatabase = 1049, - MySQLConnectionError = 2002, - MySQLCantConnect = 2003, - MySQLUnknownHost = 2005 - }; - - // - // GENERAL stuff. - // - - // Constructor. - explicit DatabaseFactory(QObject *parent = 0); - - // Destructor. - virtual ~DatabaseFactory(); - - // Returns size of DB file. - qint64 getDatabaseFileSize() const; - - // Returns size of data contained in the DB file. - qint64 getDatabaseDataSize() const; - - // If in-memory is true, then :memory: database is returned - // In-memory database is DEFAULT database. - // NOTE: This always returns OPENED database. - QSqlDatabase connection(const QString &connection_name, DesiredType desired_type = FromSettings); - - QString humanDriverName(UsedDriver driver) const; - QString humanDriverName(const QString &driver_code) const; - - // Removes connection. - void removeConnection(const QString &connection_name = QString()); - - QString obtainBeginTransactionSql() const; - - // Performs any needed database-related operation to be done - // to gracefully exit the application. - void saveDatabase(); - - // Performs cleanup of the database. - bool vacuumDatabase(); - - // Returns identification of currently active database driver. - UsedDriver activeDatabaseDriver() const; - - // Copies selected backup database (file) to active database path. - bool initiateRestoration(const QString &database_backup_file_path); - - // Finishes restoration from backup file. - void finishRestoration(); - - // - // SQLITE stuff. - // - QString sqliteDatabaseFilePath() const; - - // - // MySQL stuff. - // - - // Tests if database connection with given data - // can be established and returns 0 if it can. - // Otherwise returns MySQL-specific error code. - MySQLError mysqlTestConnection(const QString &hostname, int port, const QString &w_database, - const QString &username, const QString &password); - - // Interprets MySQL error code. - QString mysqlInterpretErrorCode(MySQLError error_code) const; - - private: - // - // GENERAL stuff. - // - - // Decides which database backend will be used in this - // application session. - void determineDriver(); - - // Holds the type of currently activated database backend. - UsedDriver m_activeDatabaseDriver; - - // - // MYSQL stuff. - // - - // Returns (always OPENED) MySQL database connection. - QSqlDatabase mysqlConnection(const QString &connection_name); - - // Initializes MySQL database. - QSqlDatabase mysqlInitializeDatabase(const QString &connection_name); - - // Updates database schema. - bool mysqlUpdateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version, const QString &db_name); - - // Runs "VACUUM" on the database. - bool mysqlVacuumDatabase(); - - // True if MySQL database is fully initialized for use, - // otherwise false. - bool m_mysqlDatabaseInitialized; - - // - // SQLITE stuff. - // - - QSqlDatabase sqliteConnection(const QString &connection_name, DesiredType desired_type); - - // Runs "VACUUM" on the database. - bool sqliteVacuumDatabase(); - - // Performs saving of items from in-memory database - // to file-based database. - void sqliteSaveMemoryDatabase(); - - // Assemblies database file path. - void sqliteAssemblyDatabaseFilePath(); - - // Updates database schema. - bool sqliteUpdateDatabaseSchema(QSqlDatabase database, const QString &source_db_schema_version); - - // Creates new connection, initializes database and - // returns opened connections. - QSqlDatabase sqliteInitializeInMemoryDatabase(); - QSqlDatabase sqliteInitializeFileBasedDatabase(const QString &connection_name); - - // Path to database file. - QString m_sqliteDatabaseFilePath; - - // Is database file initialized? - bool m_sqliteFileBasedDatabaseinitialized; - bool m_sqliteInMemoryDatabaseInitialized; -}; - -#endif // DATABASEFACTORY_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef DATABASEFACTORY_H +#define DATABASEFACTORY_H + +#include +#include + + +class DatabaseFactory : public QObject { + Q_OBJECT + + public: + // Describes available typos of database backend. + enum UsedDriver { + SQLITE, + SQLITE_MEMORY, + MYSQL + }; + + // Describes what type of database user wants. + enum DesiredType { + StrictlyFileBased, + StrictlyInMemory, + FromSettings + }; + + // Describes possible MySQL-specific errors. + enum MySQLError { + MySQLOk = 0, + MySQLUnknownError = 1, + MySQLAccessDenied = 1045, + MySQLUnknownDatabase = 1049, + MySQLConnectionError = 2002, + MySQLCantConnect = 2003, + MySQLUnknownHost = 2005 + }; + + // + // GENERAL stuff. + // + + // Constructor. + explicit DatabaseFactory(QObject* parent = 0); + + // Destructor. + virtual ~DatabaseFactory(); + + // Returns size of DB file. + qint64 getDatabaseFileSize() const; + + // Returns size of data contained in the DB file. + qint64 getDatabaseDataSize() const; + + // If in-memory is true, then :memory: database is returned + // In-memory database is DEFAULT database. + // NOTE: This always returns OPENED database. + QSqlDatabase connection(const QString& connection_name, DesiredType desired_type = FromSettings); + + QString humanDriverName(UsedDriver driver) const; + QString humanDriverName(const QString& driver_code) const; + + // Removes connection. + void removeConnection(const QString& connection_name = QString()); + + QString obtainBeginTransactionSql() const; + + // Performs any needed database-related operation to be done + // to gracefully exit the application. + void saveDatabase(); + + // Performs cleanup of the database. + bool vacuumDatabase(); + + // Returns identification of currently active database driver. + UsedDriver activeDatabaseDriver() const; + + // Copies selected backup database (file) to active database path. + bool initiateRestoration(const QString& database_backup_file_path); + + // Finishes restoration from backup file. + void finishRestoration(); + + // + // SQLITE stuff. + // + QString sqliteDatabaseFilePath() const; + + // + // MySQL stuff. + // + + // Tests if database connection with given data + // can be established and returns 0 if it can. + // Otherwise returns MySQL-specific error code. + MySQLError mysqlTestConnection(const QString& hostname, int port, const QString& w_database, + const QString& username, const QString& password); + + // Interprets MySQL error code. + QString mysqlInterpretErrorCode(MySQLError error_code) const; + + private: + // + // GENERAL stuff. + // + + // Decides which database backend will be used in this + // application session. + void determineDriver(); + + // Holds the type of currently activated database backend. + UsedDriver m_activeDatabaseDriver; + + // + // MYSQL stuff. + // + + // Returns (always OPENED) MySQL database connection. + QSqlDatabase mysqlConnection(const QString& connection_name); + + // Initializes MySQL database. + QSqlDatabase mysqlInitializeDatabase(const QString& connection_name); + + // Updates database schema. + bool mysqlUpdateDatabaseSchema(QSqlDatabase database, const QString& source_db_schema_version, const QString& db_name); + + // Runs "VACUUM" on the database. + bool mysqlVacuumDatabase(); + + // True if MySQL database is fully initialized for use, + // otherwise false. + bool m_mysqlDatabaseInitialized; + + // + // SQLITE stuff. + // + + QSqlDatabase sqliteConnection(const QString& connection_name, DesiredType desired_type); + + // Runs "VACUUM" on the database. + bool sqliteVacuumDatabase(); + + // Performs saving of items from in-memory database + // to file-based database. + void sqliteSaveMemoryDatabase(); + + // Assemblies database file path. + void sqliteAssemblyDatabaseFilePath(); + + // Updates database schema. + bool sqliteUpdateDatabaseSchema(QSqlDatabase database, const QString& source_db_schema_version); + + // Creates new connection, initializes database and + // returns opened connections. + QSqlDatabase sqliteInitializeInMemoryDatabase(); + QSqlDatabase sqliteInitializeFileBasedDatabase(const QString& connection_name); + + // Path to database file. + QString m_sqliteDatabaseFilePath; + + // Is database file initialized? + bool m_sqliteFileBasedDatabaseinitialized; + bool m_sqliteInMemoryDatabaseInitialized; +}; + +#endif // DATABASEFACTORY_H diff --git a/src/miscellaneous/databasequeries.cpp b/src/miscellaneous/databasequeries.cpp index 13e235899..6b055ff14 100755 --- a/src/miscellaneous/databasequeries.cpp +++ b/src/miscellaneous/databasequeries.cpp @@ -1,1543 +1,1507 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "miscellaneous/databasequeries.h" - -#include "services/abstract/category.h" -#include "services/owncloud/owncloudserviceroot.h" -#include "services/owncloud/owncloudcategory.h" -#include "services/owncloud/owncloudfeed.h" -#include "services/owncloud/network/owncloudnetworkfactory.h" -#include "services/standard/standardserviceroot.h" -#include "services/standard/standardcategory.h" -#include "services/standard/standardfeed.h" -#include "services/tt-rss/ttrssserviceroot.h" -#include "services/tt-rss/ttrsscategory.h" -#include "services/tt-rss/ttrssfeed.h" -#include "services/tt-rss/network/ttrssnetworkfactory.h" -#include "miscellaneous/textfactory.h" -#include "miscellaneous/application.h" -#include "miscellaneous/iconfactory.h" - -#include -#include -#include - - -bool DatabaseQueries::markMessagesReadUnread(QSqlDatabase db, const QStringList &ids, RootItem::ReadStatus read) { - QSqlQuery q(db); - q.setForwardOnly(true); - - return q.exec(QString(QSL("UPDATE Messages SET is_read = %2 WHERE id IN (%1);")) - .arg(ids.join(QSL(", ")), read == RootItem::Read ? QSL("1") : QSL("0"))); -} - -bool DatabaseQueries::markMessageImportant(QSqlDatabase db, int id, RootItem::Importance importance) { - QSqlQuery q(db); - q.setForwardOnly(true); - - if (!q.prepare(QSL("UPDATE Messages SET is_important = :important WHERE id = :id;"))) { - qWarning("Query preparation failed for message importance switch."); - return false; - } - - q.bindValue(QSL(":id"), id); - q.bindValue(QSL(":important"), (int) importance); - - // Commit changes. - return q.exec(); -} - -bool DatabaseQueries::markFeedsReadUnread(QSqlDatabase db, const QStringList &ids, int account_id, RootItem::ReadStatus read) { - QSqlQuery q(db); - q.setForwardOnly(true); - q.prepare(QString("UPDATE Messages SET is_read = :read " - "WHERE feed IN (%1) AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;").arg(ids.join(QSL(", ")))); - - q.bindValue(QSL(":read"), read == RootItem::Read ? 1 : 0); - q.bindValue(QSL(":account_id"), account_id); - - return q.exec(); -} - -bool DatabaseQueries::markBinReadUnread(QSqlDatabase db, int account_id, RootItem::ReadStatus read) { - QSqlQuery q(db); - q.setForwardOnly(true); - q.prepare("UPDATE Messages SET is_read = :read " - "WHERE is_deleted = 1 AND is_pdeleted = 0 AND account_id = :account_id;"); - - q.bindValue(QSL(":read"), read == RootItem::Read ? 1 : 0); - q.bindValue(QSL(":account_id"), account_id); - - return q.exec(); -} - -bool DatabaseQueries::markAccountReadUnread(QSqlDatabase db, int account_id, RootItem::ReadStatus read) { - QSqlQuery q(db); - q.setForwardOnly(true); - q.prepare(QSL("UPDATE Messages SET is_read = :read WHERE is_pdeleted = 0 AND account_id = :account_id;")); - - q.bindValue(QSL(":account_id"), account_id); - q.bindValue(QSL(":read"), read == RootItem::Read ? 1 : 0); - - return q.exec(); -} - -bool DatabaseQueries::switchMessagesImportance(QSqlDatabase db, const QStringList &ids) { - QSqlQuery q(db); - q.setForwardOnly(true); - - return q.exec(QString(QSL("UPDATE Messages SET is_important = NOT is_important WHERE id IN (%1);")).arg(ids.join(QSL(", ")))); -} - -bool DatabaseQueries::permanentlyDeleteMessages(QSqlDatabase db, const QStringList &ids) { - QSqlQuery q(db); - q.setForwardOnly(true); - - return q.exec(QString(QSL("UPDATE Messages SET is_pdeleted = 1 WHERE id IN (%1);")).arg(ids.join(QSL(", ")))); -} - -bool DatabaseQueries::deleteOrRestoreMessagesToFromBin(QSqlDatabase db, const QStringList &ids, bool deleted) { - QSqlQuery q(db); - q.setForwardOnly(true); - - return q.exec(QString(QSL("UPDATE Messages SET is_deleted = %2, is_pdeleted = %3 WHERE id IN (%1);")).arg(ids.join(QSL(", ")), - QString::number(deleted ? 1 : 0), - QString::number(0))); -} - -bool DatabaseQueries::restoreBin(QSqlDatabase db, int account_id) { - QSqlQuery q(db); - q.setForwardOnly(true); - q.prepare("UPDATE Messages SET is_deleted = 0 " - "WHERE is_deleted = 1 AND is_pdeleted = 0 AND account_id = :account_id;"); - q.bindValue(QSL(":account_id"), account_id); - - return q.exec(); -} - -bool DatabaseQueries::purgeImportantMessages(QSqlDatabase db) { - QSqlQuery q(db); - q.setForwardOnly(true); - q.prepare(QSL("DELETE FROM Messages WHERE is_important = 1;")); - - return q.exec(); -} - -bool DatabaseQueries::purgeReadMessages(QSqlDatabase db) { - QSqlQuery q(db); - q.setForwardOnly(true); - q.prepare(QSL("DELETE FROM Messages WHERE is_important = :is_important AND is_deleted = :is_deleted AND is_read = :is_read;")); - q.bindValue(QSL(":is_read"), 1); - - // Remove only messages which are NOT in recycle bin. - q.bindValue(QSL(":is_deleted"), 0); - - // Remove only messages which are NOT starred. - q.bindValue(QSL(":is_important"), 0); - - return q.exec(); -} - -bool DatabaseQueries::purgeOldMessages(QSqlDatabase db, int older_than_days) { - QSqlQuery q(db); - const qint64 since_epoch = QDateTime::currentDateTimeUtc().addDays(-older_than_days).toMSecsSinceEpoch(); - - q.setForwardOnly(true); - q.prepare(QSL("DELETE FROM Messages WHERE is_important = :is_important AND date_created < :date_created;")); - q.bindValue(QSL(":date_created"), since_epoch); - - // Remove only messages which are NOT starred. - q.bindValue(QSL(":is_important"), 0); - - return q.exec(); -} - -bool DatabaseQueries::purgeRecycleBin(QSqlDatabase db) { - QSqlQuery q(db); - - q.setForwardOnly(true); - q.prepare(QSL("DELETE FROM Messages WHERE is_important = :is_important AND is_deleted = :is_deleted;")); - q.bindValue(QSL(":is_deleted"), 1); - - // Remove only messages which are NOT starred. - q.bindValue(QSL(":is_important"), 0); - - return q.exec(); -} - -QMap > DatabaseQueries::getMessageCountsForCategory(QSqlDatabase db, int custom_id, int account_id, - bool including_total_counts, bool *ok) { - QMap > counts; - QSqlQuery q(db); - q.setForwardOnly(true); - - if (including_total_counts) { - q.prepare("SELECT feed, sum((is_read + 1) % 2), count(*) FROM Messages " - "WHERE feed IN (SELECT custom_id FROM Feeds WHERE category = :category AND account_id = :account_id) AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id " - "GROUP BY feed;"); - } - else { - q.prepare("SELECT feed, sum((is_read + 1) % 2) FROM Messages " - "WHERE feed IN (SELECT custom_id FROM Feeds WHERE category = :category AND account_id = :account_id) AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id " - "GROUP BY feed;"); - } - - q.bindValue(QSL(":category"), custom_id); - q.bindValue(QSL(":account_id"), account_id); - - if (q.exec()) { - while (q.next()) { - int feed_id = q.value(0).toInt(); - int unread_count = q.value(1).toInt(); - - if (including_total_counts) { - int total_count = q.value(2).toInt(); - - counts.insert(feed_id, QPair(unread_count, total_count)); - } - else { - counts.insert(feed_id, QPair(unread_count, 0)); - } - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - if (ok != nullptr) { - *ok = false; - } - } - - return counts; -} - -QMap > DatabaseQueries::getMessageCountsForAccount(QSqlDatabase db, int account_id, - bool including_total_counts, bool *ok) { - QMap > counts; - QSqlQuery q(db); - q.setForwardOnly(true); - - if (including_total_counts) { - q.prepare("SELECT feed, sum((is_read + 1) % 2), count(*) FROM Messages " - "WHERE is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id " - "GROUP BY feed;"); - } - else { - q.prepare("SELECT feed, sum((is_read + 1) % 2) FROM Messages " - "WHERE is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id " - "GROUP BY feed;"); - } - - q.bindValue(QSL(":account_id"), account_id); - - if (q.exec()) { - while (q.next()) { - int feed_id = q.value(0).toInt(); - int unread_count = q.value(1).toInt(); - - if (including_total_counts) { - int total_count = q.value(2).toInt(); - - counts.insert(feed_id, QPair(unread_count, total_count)); - } - else { - counts.insert(feed_id, QPair(unread_count, 0)); - } - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - if (ok != nullptr) { - *ok = false; - } - } - - return counts; -} - -int DatabaseQueries::getMessageCountsForFeed(QSqlDatabase db, int feed_custom_id, - int account_id, bool including_total_counts, bool *ok) { - QSqlQuery q(db); - q.setForwardOnly(true); - - if (including_total_counts) { - q.prepare("SELECT count(*) FROM Messages " - "WHERE feed = :feed AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;"); - } - else { - q.prepare("SELECT count(*) FROM Messages " - "WHERE feed = :feed AND is_deleted = 0 AND is_pdeleted = 0 AND is_read = 0 AND account_id = :account_id;"); - } - - q.bindValue(QSL(":feed"), feed_custom_id); - q.bindValue(QSL(":account_id"), account_id); - - if (q.exec() && q.next()) { - if (ok != nullptr) { - *ok = true; - } - - return q.value(0).toInt(); - } - else { - if (ok != nullptr) { - *ok = false; - } - - return 0; - } -} - -int DatabaseQueries::getMessageCountsForBin(QSqlDatabase db, int account_id, bool including_total_counts, bool *ok) { - QSqlQuery q(db); - q.setForwardOnly(true); - - if (including_total_counts) { - q.prepare("SELECT count(*) FROM Messages " - "WHERE is_deleted = 1 AND is_pdeleted = 0 AND account_id = :account_id;"); - } - else { - q.prepare("SELECT count(*) FROM Messages " - "WHERE is_read = 0 AND is_deleted = 1 AND is_pdeleted = 0 AND account_id = :account_id;"); - } - - q.bindValue(QSL(":account_id"), account_id); - - if (q.exec() && q.next()) { - if (ok != nullptr) { - *ok = true; - } - - return q.value(0).toInt(); - } - else { - if (ok != nullptr) { - *ok = false; - } - - return 0; - } -} - -QList DatabaseQueries::getUndeletedMessagesForFeed(QSqlDatabase db, int feed_custom_id, int account_id, bool *ok) { - QList messages; - QSqlQuery q(db); - q.setForwardOnly(true); - q.prepare("SELECT * " - "FROM Messages " - "WHERE is_deleted = 0 AND is_pdeleted = 0 AND feed = :feed AND account_id = :account_id;"); - - q.bindValue(QSL(":feed"), feed_custom_id); - q.bindValue(QSL(":account_id"), account_id); - - if (q.exec()) { - while (q.next()) { - bool decoded; - Message message = Message::fromSqlRecord(q.record(), &decoded); - - if (decoded) { - messages.append(message); - } - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - if (ok != nullptr) { - *ok = false; - } - } - - return messages; -} - -QList DatabaseQueries::getUndeletedMessagesForBin(QSqlDatabase db, int account_id, bool *ok) { - QList messages; - QSqlQuery q(db); - q.setForwardOnly(true); - q.prepare("SELECT * " - "FROM Messages " - "WHERE is_deleted = 1 AND is_pdeleted = 0 AND account_id = :account_id;"); - - q.bindValue(QSL(":account_id"), account_id); - - if (q.exec()) { - while (q.next()) { - bool decoded; - Message message = Message::fromSqlRecord(q.record(), &decoded); - - if (decoded) { - messages.append(message); - } - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - if (ok != nullptr) { - *ok = false; - } - } - - return messages; -} - -QList DatabaseQueries::getUndeletedMessagesForAccount(QSqlDatabase db, int account_id, bool *ok) { - QList messages; - QSqlQuery q(db); - q.setForwardOnly(true); - q.prepare("SELECT * " - "FROM Messages " - "WHERE is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;"); - q.bindValue(QSL(":account_id"), account_id); - - if (q.exec()) { - while (q.next()) { - bool decoded; - Message message = Message::fromSqlRecord(q.record(), &decoded); - - if (decoded) { - messages.append(message); - } - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - if (ok != nullptr) { - *ok = false; - } - } - - return messages; -} - -int DatabaseQueries::updateMessages(QSqlDatabase db, - const QList &messages, - int feed_custom_id, - int account_id, - const QString &url, - bool *any_message_changed, - bool *ok) { - if (messages.isEmpty()) { - *any_message_changed = false; - *ok = true; - return 0; - } - - bool use_transactions = qApp->settings()->value(GROUP(Database), SETTING(Database::UseTransactions)).toBool(); - - // Does not make any difference, since each feed now has - // its own "custom ID" (standard feeds have their custom ID equal to primary key ID). - int updated_messages = 0; - - // Prepare queries. - QSqlQuery query_select_with_url(db); - QSqlQuery query_select_with_id(db); - QSqlQuery query_update(db); - QSqlQuery query_insert(db); - QSqlQuery query_begin_transaction(db); - - // Here we have query which will check for existence of the "same" message in given feed. - // The two message are the "same" if: - // 1) they belong to the same feed AND, - // 2) they have same URL AND, - // 3) they have same AUTHOR. - query_select_with_url.setForwardOnly(true); - query_select_with_url.prepare("SELECT id, date_created, is_read, is_important, contents FROM Messages " - "WHERE feed = :feed AND title = :title AND url = :url AND author = :author AND account_id = :account_id;"); - - // When we have custom ID of the message, we can check directly for existence - // of that particular message. - query_select_with_id.setForwardOnly(true); - query_select_with_id.prepare("SELECT id, date_created, is_read, is_important, contents FROM Messages " - "WHERE custom_id = :custom_id AND account_id = :account_id;"); - - // Used to insert new messages. - query_insert.setForwardOnly(true); - query_insert.prepare("INSERT INTO Messages " - "(feed, title, is_read, is_important, url, author, date_created, contents, enclosures, custom_id, custom_hash, account_id) " - "VALUES (:feed, :title, :is_read, :is_important, :url, :author, :date_created, :contents, :enclosures, :custom_id, :custom_hash, :account_id);"); - - // Used to update existing messages. - query_update.setForwardOnly(true); - query_update.prepare("UPDATE Messages " - "SET title = :title, is_read = :is_read, is_important = :is_important, url = :url, author = :author, date_created = :date_created, contents = :contents, enclosures = :enclosures " - "WHERE id = :id;"); - - if (use_transactions && !query_begin_transaction.exec(qApp->database()->obtainBeginTransactionSql())) { - qCritical("Transaction start for message downloader failed: '%s'.", qPrintable(query_begin_transaction.lastError().text())); - return updated_messages; - } - - foreach (Message message, messages) { - // Check if messages contain relative URLs and if they do, then replace them. - if (message.m_url.startsWith(QL1S("//"))) { - message.m_url = QString(URI_SCHEME_HTTP) + message.m_url.mid(2); - } - else if (message.m_url.startsWith(QL1S("/"))) { - QString new_message_url = QUrl(url).toString(QUrl::RemoveUserInfo | - QUrl::RemovePath | - QUrl::RemoveQuery | - QUrl::RemoveFilename | - QUrl::StripTrailingSlash); - - new_message_url += message.m_url; - message.m_url = new_message_url; - } - - int id_existing_message = -1; - qint64 date_existing_message; - bool is_read_existing_message; - bool is_important_existing_message; - QString contents_existing_message; - - if (message.m_customId.isEmpty()) { - // We need to recognize existing messages according URL & AUTHOR. - // NOTE: This particularly concerns messages from standard account. - query_select_with_url.bindValue(QSL(":feed"), feed_custom_id); - query_select_with_url.bindValue(QSL(":title"), message.m_title); - query_select_with_url.bindValue(QSL(":url"), message.m_url); - query_select_with_url.bindValue(QSL(":author"), message.m_author); - query_select_with_url.bindValue(QSL(":account_id"), account_id); - - if (query_select_with_url.exec() && query_select_with_url.next()) { - id_existing_message = query_select_with_url.value(0).toInt(); - date_existing_message = query_select_with_url.value(1).value(); - is_read_existing_message = query_select_with_url.value(2).toBool(); - is_important_existing_message = query_select_with_url.value(3).toBool(); - contents_existing_message = query_select_with_url.value(4).toString(); - } - else if (query_select_with_url.lastError().isValid()) { - qWarning("Failed to check for existing message in DB via URL: '%s'.", qPrintable(query_select_with_url.lastError().text())); - } - - query_select_with_url.finish(); - } - else { - // We can recognize existing messages via their custom ID. - // NOTE: This concerns messages from custom accounts, like TT-RSS or ownCloud News. - query_select_with_id.bindValue(QSL(":account_id"), account_id); - query_select_with_id.bindValue(QSL(":custom_id"), message.m_customId); - - if (query_select_with_id.exec() && query_select_with_id.next()) { - id_existing_message = query_select_with_id.value(0).toInt(); - date_existing_message = query_select_with_id.value(1).value(); - is_read_existing_message = query_select_with_id.value(2).toBool(); - is_important_existing_message = query_select_with_id.value(3).toBool(); - contents_existing_message = query_select_with_id.value(4).toString(); - } - else if (query_select_with_id.lastError().isValid()) { - qDebug("Failed to check for existing message in DB via ID: '%s'.", qPrintable(query_select_with_id.lastError().text())); - } - - query_select_with_id.finish(); - } - - // Now, check if this message is already in the DB. - if (id_existing_message >= 0) { - // Message is already in the DB. - // - // Now, we update it if at least one of next conditions is true: - // 1) Message has custom ID AND (its date OR read status OR starred status are changed). - // 2) Message has its date fetched from feed AND its date is different from date in DB and contents is changed. - if (/* 1 */ (!message.m_customId.isEmpty() && (message.m_created.toMSecsSinceEpoch() != date_existing_message || message.m_isRead != is_read_existing_message || message.m_isImportant != is_important_existing_message)) || - /* 2 */ (message.m_createdFromFeed && message.m_created.toMSecsSinceEpoch() != date_existing_message && message.m_contents != contents_existing_message)) { - // Message exists, it is changed, update it. - query_update.bindValue(QSL(":title"), message.m_title); - query_update.bindValue(QSL(":is_read"), (int) message.m_isRead); - query_update.bindValue(QSL(":is_important"), (int) message.m_isImportant); - query_update.bindValue(QSL(":url"), message.m_url); - query_update.bindValue(QSL(":author"), message.m_author); - query_update.bindValue(QSL(":date_created"), message.m_created.toMSecsSinceEpoch()); - query_update.bindValue(QSL(":contents"), message.m_contents); - query_update.bindValue(QSL(":enclosures"), Enclosures::encodeEnclosuresToString(message.m_enclosures)); - query_update.bindValue(QSL(":id"), id_existing_message); - - *any_message_changed = true; - - if (query_update.exec() && !message.m_isRead) { - updated_messages++; - } - else if (query_update.lastError().isValid()) { - qWarning("Failed to update message in DB: '%s'.", qPrintable(query_update.lastError().text())); - } - - query_update.finish(); - qDebug("Updating message '%s' in DB.", qPrintable(message.m_title)); - } - } - else { - // Message with this URL is not fetched in this feed yet. - query_insert.bindValue(QSL(":feed"), feed_custom_id); - query_insert.bindValue(QSL(":title"), message.m_title); - query_insert.bindValue(QSL(":is_read"), (int) message.m_isRead); - query_insert.bindValue(QSL(":is_important"), (int) message.m_isImportant); - query_insert.bindValue(QSL(":url"), message.m_url); - query_insert.bindValue(QSL(":author"), message.m_author); - query_insert.bindValue(QSL(":date_created"), message.m_created.toMSecsSinceEpoch()); - query_insert.bindValue(QSL(":contents"), message.m_contents); - query_insert.bindValue(QSL(":enclosures"), Enclosures::encodeEnclosuresToString(message.m_enclosures)); - query_insert.bindValue(QSL(":custom_id"), message.m_customId); - query_insert.bindValue(QSL(":custom_hash"), message.m_customHash); - query_insert.bindValue(QSL(":account_id"), account_id); - - if (query_insert.exec() && query_insert.numRowsAffected() == 1) { - updated_messages++; - qDebug("Added new message '%s' to DB.", qPrintable(message.m_title)); - } - else if (query_insert.lastError().isValid()) { - qWarning("Failed to insert message to DB: '%s' - message title is '%s'.", - qPrintable(query_insert.lastError().text()), - qPrintable(message.m_title)); - } - - query_insert.finish(); - } - } - - // Now, fixup custom IDS for messages which initially did not have them, - // just to keep the data consistent. - if (db.exec("UPDATE Messages " - "SET custom_id = id " - "WHERE custom_id IS NULL OR custom_id = '';").lastError().isValid()) { - qWarning("Failed to set custom ID for all messages: '%s'.", qPrintable(db.lastError().text())); - } - - if (use_transactions && !db.commit()) { - qCritical("Transaction commit for message downloader failed: '%s'.", qPrintable(db.lastError().text())); - db.rollback(); - - if (ok != nullptr) { - *ok = false; - updated_messages = 0; - } - } - else { - if (ok != nullptr) { - *ok = true; - } - } - - return updated_messages; -} - -bool DatabaseQueries::purgeMessagesFromBin(QSqlDatabase db, bool clear_only_read, int account_id) { - QSqlQuery q(db); - q.setForwardOnly(true); - - if (clear_only_read) { - q.prepare(QSL("UPDATE Messages SET is_pdeleted = 1 WHERE is_read = 1 AND is_deleted = 1 AND account_id = :account_id;")); - } - else { - q.prepare(QSL("UPDATE Messages SET is_pdeleted = 1 WHERE is_deleted = 1 AND account_id = :account_id;")); - } - - q.bindValue(QSL(":account_id"), account_id); - - return q.exec(); -} - -bool DatabaseQueries::deleteAccount(QSqlDatabase db, int account_id) { - QSqlQuery query(db); - query.setForwardOnly(true); - - QStringList queries; - queries << QSL("DELETE FROM Messages WHERE account_id = :account_id;") << - QSL("DELETE FROM Feeds WHERE account_id = :account_id;") << - QSL("DELETE FROM Categories WHERE account_id = :account_id;") << - QSL("DELETE FROM Accounts WHERE id = :account_id;"); - - foreach (const QString &q, queries) { - query.prepare(q); - query.bindValue(QSL(":account_id"), account_id); - - if (!query.exec()) { - qCritical("Removing of account from DB failed, this is critical: '%s'.", qPrintable(query.lastError().text())); - return false; - } - else { - query.finish(); - } - } - - return true; -} - -bool DatabaseQueries::deleteAccountData(QSqlDatabase db, int account_id, bool delete_messages_too) { - bool result = true; - QSqlQuery q(db); - q.setForwardOnly(true); - - if (delete_messages_too) { - q.prepare(QSL("DELETE FROM Messages WHERE account_id = :account_id;")); - q.bindValue(QSL(":account_id"), account_id); - - result &= q.exec(); - } - - q.prepare(QSL("DELETE FROM Feeds WHERE account_id = :account_id;")); - q.bindValue(QSL(":account_id"), account_id); - - result &= q.exec(); - - q.prepare(QSL("DELETE FROM Categories WHERE account_id = :account_id;")); - q.bindValue(QSL(":account_id"), account_id); - - result &= q.exec(); - - return result; -} - -bool DatabaseQueries::cleanFeeds(QSqlDatabase db, const QStringList &ids, bool clean_read_only, int account_id) { - QSqlQuery q(db); - q.setForwardOnly(true); - - if (clean_read_only) { - q.prepare(QString("UPDATE Messages SET is_deleted = :deleted " - "WHERE feed IN (%1) AND is_deleted = 0 AND is_pdeleted = 0 AND is_read = 1 AND account_id = :account_id;") - .arg(ids.join(QSL(", ")))); - } - else { - q.prepare(QString("UPDATE Messages SET is_deleted = :deleted " - "WHERE feed IN (%1) AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;") - .arg(ids.join(QSL(", ")))); - } - - q.bindValue(QSL(":deleted"), 1); - q.bindValue(QSL(":account_id"), account_id); - - if (!q.exec()) { - qDebug("Cleaning of feeds failed: '%s'.", qPrintable(q.lastError().text())); - return false; - } - else { - return true; - } -} - -bool DatabaseQueries::purgeLeftoverMessages(QSqlDatabase db, int account_id) { - QSqlQuery q(db); - - q.setForwardOnly(true); - q.prepare(QSL("DELETE FROM Messages WHERE account_id = :account_id AND feed NOT IN (SELECT custom_id FROM Feeds WHERE account_id = :account_id);")); - q.bindValue(QSL(":account_id"), account_id); - - if (!q.exec()) { - qWarning("Removing of left over messages failed: '%s'.", qPrintable(q.lastError().text())); - return false; - } - else { - return true; - } -} - -bool DatabaseQueries::storeAccountTree(QSqlDatabase db, RootItem *tree_root, int account_id) { - QSqlQuery query_category(db); - QSqlQuery query_feed(db); - query_category.setForwardOnly(true); - query_feed.setForwardOnly(true); - query_category.prepare("INSERT INTO Categories (parent_id, title, account_id, custom_id) " - "VALUES (:parent_id, :title, :account_id, :custom_id);"); - query_feed.prepare("INSERT INTO Feeds (title, icon, category, protected, update_type, update_interval, account_id, custom_id) " - "VALUES (:title, :icon, :category, :protected, :update_type, :update_interval, :account_id, :custom_id);"); - - // Iterate all children. - foreach (RootItem *child, tree_root->getSubTree()) { - if (child->kind() == RootItemKind::Category) { - query_category.bindValue(QSL(":parent_id"), child->parent()->id()); - query_category.bindValue(QSL(":title"), child->title()); - query_category.bindValue(QSL(":account_id"), account_id); - query_category.bindValue(QSL(":custom_id"), QString::number(child->toCategory()->customId())); - - if (query_category.exec()) { - child->setId(query_category.lastInsertId().toInt()); - } - else { - return false; - } - } - else if (child->kind() == RootItemKind::Feed) { - Feed *feed = child->toFeed(); - - query_feed.bindValue(QSL(":title"), feed->title()); - query_feed.bindValue(QSL(":icon"), qApp->icons()->toByteArray(feed->icon())); - query_feed.bindValue(QSL(":category"), feed->parent()->customId()); - query_feed.bindValue(QSL(":protected"), 0); - query_feed.bindValue(QSL(":update_type"), (int) feed->autoUpdateType()); - query_feed.bindValue(QSL(":update_interval"), feed->autoUpdateInitialInterval()); - query_feed.bindValue(QSL(":account_id"), account_id); - query_feed.bindValue(QSL(":custom_id"), feed->customId()); - - if (query_feed.exec()) { - feed->setId(query_feed.lastInsertId().toInt()); - } - else { - return false; - } - } - } - - return true; -} - -QStringList DatabaseQueries::customIdsOfMessagesFromAccount(QSqlDatabase db, int account_id, bool *ok) { - QSqlQuery q(db); - QStringList ids; - q.setForwardOnly(true); - q.prepare(QSL("SELECT custom_id FROM Messages WHERE is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;")); - q.bindValue(QSL(":account_id"), account_id); - - if (ok != nullptr) { - *ok = q.exec(); - } - else { - q.exec(); - } - - while (q.next()) { - ids.append(q.value(0).toString()); - } - - return ids; -} - -QStringList DatabaseQueries::customIdsOfMessagesFromBin(QSqlDatabase db, int account_id, bool *ok) { - QSqlQuery q(db); - QStringList ids; - q.setForwardOnly(true); - q.prepare(QSL("SELECT custom_id FROM Messages WHERE is_deleted = 1 AND is_pdeleted = 0 AND account_id = :account_id;")); - q.bindValue(QSL(":account_id"), account_id); - - if (ok != nullptr) { - *ok = q.exec(); - } - else { - q.exec(); - } - - while (q.next()) { - ids.append(q.value(0).toString()); - } - - return ids; -} - -QStringList DatabaseQueries::customIdsOfMessagesFromFeed(QSqlDatabase db, int feed_custom_id, int account_id, bool *ok) { - QSqlQuery q(db); - QStringList ids; - q.setForwardOnly(true); - q.prepare(QSL("SELECT custom_id FROM Messages WHERE is_deleted = 0 AND is_pdeleted = 0 AND feed = :feed AND account_id = :account_id;")); - q.bindValue(QSL(":account_id"), account_id); - q.bindValue(QSL(":feed"), feed_custom_id); - - if (ok != nullptr) { - *ok = q.exec(); - } - else { - q.exec(); - } - - while (q.next()) { - ids.append(q.value(0).toString()); - } - - return ids; -} - -QList DatabaseQueries::getOwnCloudAccounts(QSqlDatabase db, bool *ok) { - QSqlQuery query(db); - QList roots; - - if (query.exec("SELECT * FROM OwnCloudAccounts;")) { - while (query.next()) { - OwnCloudServiceRoot *root = new OwnCloudServiceRoot(); - root->setId(query.value(0).toInt()); - root->setAccountId(query.value(0).toInt()); - root->network()->setAuthUsername(query.value(1).toString()); - root->network()->setAuthPassword(TextFactory::decrypt(query.value(2).toString())); - root->network()->setUrl(query.value(3).toString()); - root->network()->setForceServerSideUpdate(query.value(4).toBool()); - - root->updateTitle(); - roots.append(root); - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - qWarning("OwnCloud: Getting list of activated accounts failed: '%s'.", qPrintable(query.lastError().text())); - - if (ok != nullptr) { - *ok = false; - } - } - - return roots; -} - -QList DatabaseQueries::getTtRssAccounts(QSqlDatabase db, bool *ok) { - QSqlQuery query(db); - QList roots; - - if (query.exec("SELECT * FROM TtRssAccounts;")) { - while (query.next()) { - TtRssServiceRoot *root = new TtRssServiceRoot(); - root->setId(query.value(0).toInt()); - root->setAccountId(query.value(0).toInt()); - root->network()->setUsername(query.value(1).toString()); - root->network()->setPassword(TextFactory::decrypt(query.value(2).toString())); - root->network()->setAuthIsUsed(query.value(3).toBool()); - root->network()->setAuthUsername(query.value(4).toString()); - root->network()->setAuthPassword(TextFactory::decrypt(query.value(5).toString())); - root->network()->setUrl(query.value(6).toString()); - root->network()->setForceServerSideUpdate(query.value(7).toBool()); - - root->updateTitle(); - roots.append(root); - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - qWarning("TT-RSS: Getting list of activated accounts failed: '%s'.", qPrintable(query.lastError().text())); - - if (ok != nullptr) { - *ok = false; - } - } - - return roots; -} - -bool DatabaseQueries::deleteOwnCloudAccount(QSqlDatabase db, int account_id) { - QSqlQuery q(db); - - q.setForwardOnly(true); - q.prepare(QSL("DELETE FROM OwnCloudAccounts WHERE id = :id;")); - q.bindValue(QSL(":id"), account_id); - - return q.exec(); -} - -bool DatabaseQueries::overwriteOwnCloudAccount(QSqlDatabase db, const QString &username, const QString &password, - const QString &url, bool force_server_side_feed_update, int account_id) { - QSqlQuery query(db); - - query.prepare("UPDATE OwnCloudAccounts " - "SET username = :username, password = :password, url = :url, force_update = :force_update " - "WHERE id = :id;"); - query.bindValue(QSL(":username"), username); - query.bindValue(QSL(":password"), TextFactory::encrypt(password)); - query.bindValue(QSL(":url"), url); - query.bindValue(QSL(":force_update"), force_server_side_feed_update ? 1 : 0); - query.bindValue(QSL(":id"), account_id); - - if (query.exec()) { - return true; - } - else { - qWarning("ownCloud: Updating account failed: '%s'.", qPrintable(query.lastError().text())); - return false; - } -} - -bool DatabaseQueries::createOwnCloudAccount(QSqlDatabase db, int id_to_assign, const QString &username, - const QString &password, const QString &url, - bool force_server_side_feed_update) { - QSqlQuery q(db); - - q.prepare("INSERT INTO OwnCloudAccounts (id, username, password, url, force_update) " - "VALUES (:id, :username, :password, :url, :force_update);"); - q.bindValue(QSL(":id"), id_to_assign); - q.bindValue(QSL(":username"), username); - q.bindValue(QSL(":password"), TextFactory::encrypt(password)); - q.bindValue(QSL(":url"), url); - q.bindValue(QSL(":force_update"), force_server_side_feed_update ? 1 : 0); - - if (q.exec()) { - return true; - } - else { - qWarning("ownCloud: Inserting of new account failed: '%s'.", qPrintable(q.lastError().text())); - return false; - } -} - -int DatabaseQueries::createAccount(QSqlDatabase db, const QString &code, bool *ok) { - QSqlQuery q(db); - - // First obtain the ID, which can be assigned to this new account. - if (!q.exec("SELECT max(id) FROM Accounts;") || !q.next()) { - qWarning("Getting max ID from Accounts table failed: '%s'.", qPrintable(q.lastError().text())); - - if (ok != nullptr) { - *ok = false; - } - - return 0; - } - - int id_to_assign = q.value(0).toInt() + 1; - - q.prepare(QSL("INSERT INTO Accounts (id, type) VALUES (:id, :type);")); - q.bindValue(QSL(":id"), id_to_assign); - q.bindValue(QSL(":type"), code); - - if (q.exec()) { - if (ok != nullptr) { - *ok = true; - } - - return id_to_assign; - } - else { - if (ok != nullptr) { - *ok = false; - } - - qWarning("Inserting of new account failed: '%s'.", qPrintable(q.lastError().text())); - return 0; - } -} - -Assignment DatabaseQueries::getOwnCloudCategories(QSqlDatabase db, int account_id, bool *ok) { - Assignment categories; - - // Obtain data for categories from the database. - QSqlQuery q(db); - q.setForwardOnly(true); - q.prepare(QSL("SELECT * FROM Categories WHERE account_id = :account_id;")); - q.bindValue(QSL(":account_id"), account_id); - - if (!q.exec()) { - qFatal("ownCloud: Query for obtaining categories failed. Error message: '%s'.", qPrintable(q.lastError().text())); - - if (ok != nullptr) { - *ok = false; - } - } - - while (q.next()) { - AssignmentItem pair; - pair.first = q.value(CAT_DB_PARENT_ID_INDEX).toInt(); - pair.second = new OwnCloudCategory(q.record()); - - categories << pair; - } - - if (ok != nullptr) { - *ok = true; - } - - return categories; -} - -Assignment DatabaseQueries::getOwnCloudFeeds(QSqlDatabase db, int account_id, bool *ok) { - Assignment feeds; - - QSqlQuery q(db); - q.setForwardOnly(true); - q.prepare(QSL("SELECT * FROM Feeds WHERE account_id = :account_id;")); - q.bindValue(QSL(":account_id"), account_id); - - if (!q.exec()) { - qFatal("ownCloud: Query for obtaining feeds failed. Error message: '%s'.", qPrintable(q.lastError().text())); - - if (ok != nullptr) { - *ok = false; - } - } - - while (q.next()) { - AssignmentItem pair; - pair.first = q.value(FDS_DB_CATEGORY_INDEX).toInt(); - pair.second = new OwnCloudFeed(q.record()); - - feeds << pair; - } - - if (ok != nullptr) { - *ok = true; - } - - return feeds; -} - -bool DatabaseQueries::deleteFeed(QSqlDatabase db, int feed_custom_id, int account_id) { - QSqlQuery q(db); - q.setForwardOnly(true); - - // Remove all messages from this feed. - q.prepare(QSL("DELETE FROM Messages WHERE feed = :feed AND account_id = :account_id;")); - q.bindValue(QSL(":feed"), feed_custom_id); - q.bindValue(QSL(":account_id"), account_id); - - if (!q.exec()) { - return false; - } - - // Remove feed itself. - q.prepare(QSL("DELETE FROM Feeds WHERE custom_id = :feed AND account_id = :account_id;")); - q.bindValue(QSL(":feed"), feed_custom_id); - q.bindValue(QSL(":account_id"), account_id); - - return q.exec(); -} - -bool DatabaseQueries::deleteCategory(QSqlDatabase db, int id) { - QSqlQuery q(db); - - // Remove this category from database. - q.setForwardOnly(true); - q.prepare(QSL("DELETE FROM Categories WHERE id = :category;")); - q.bindValue(QSL(":category"), id); - - return q.exec(); -} - -int DatabaseQueries::addCategory(QSqlDatabase db, int parent_id, int account_id, const QString &title, - const QString &description, QDateTime creation_date, const QIcon &icon, - bool *ok) { - QSqlQuery q(db); - - q.setForwardOnly(true); - q.prepare("INSERT INTO Categories " - "(parent_id, title, description, date_created, icon, account_id) " - "VALUES (:parent_id, :title, :description, :date_created, :icon, :account_id);"); - q.bindValue(QSL(":parent_id"), parent_id); - q.bindValue(QSL(":title"), title); - q.bindValue(QSL(":description"), description); - q.bindValue(QSL(":date_created"), creation_date.toMSecsSinceEpoch()); - q.bindValue(QSL(":icon"), qApp->icons()->toByteArray(icon)); - q.bindValue(QSL(":account_id"), account_id); - - if (!q.exec()) { - qDebug("Failed to add category to database: '%s'.", qPrintable(q.lastError().text())); - - if (ok != nullptr) { - *ok = false; - } - - // Query failed. - return 0; - } - else { - if (ok != nullptr) { - *ok = true; - } - - int new_id = q.lastInsertId().toInt(); - - // Now set custom ID in the DB. - q.prepare(QSL("UPDATE Categories SET custom_id = :custom_id WHERE id = :id;")); - q.bindValue(QSL(":custom_id"), QString::number(new_id)); - q.bindValue(QSL(":id"), new_id); - q.exec(); - - return new_id; - } -} - -bool DatabaseQueries::editCategory(QSqlDatabase db, int parent_id, int category_id, - const QString &title, const QString &description, const QIcon &icon) { - QSqlQuery q(db); - - q.setForwardOnly(true); - q.prepare("UPDATE Categories " - "SET title = :title, description = :description, icon = :icon, parent_id = :parent_id " - "WHERE id = :id;"); - q.bindValue(QSL(":title"), title); - q.bindValue(QSL(":description"), description); - q.bindValue(QSL(":icon"), qApp->icons()->toByteArray(icon)); - q.bindValue(QSL(":parent_id"), parent_id); - q.bindValue(QSL(":id"), category_id); - - return q.exec(); -} - -int DatabaseQueries::addFeed(QSqlDatabase db, int parent_id, int account_id, const QString &title, - const QString &description, QDateTime creation_date, const QIcon &icon, - const QString &encoding, const QString &url, bool is_protected, - const QString &username, const QString &password, - Feed::AutoUpdateType auto_update_type, - int auto_update_interval, StandardFeed::Type feed_format, bool *ok) { - QSqlQuery q(db); - - qDebug() << "Adding feed with title '" << title.toUtf8() << "' to DB."; - - q.setForwardOnly(true); - q.prepare("INSERT INTO Feeds " - "(title, description, date_created, icon, category, encoding, url, protected, username, password, update_type, update_interval, type, account_id) " - "VALUES (:title, :description, :date_created, :icon, :category, :encoding, :url, :protected, :username, :password, :update_type, :update_interval, :type, :account_id);"); - q.bindValue(QSL(":title"), title.toUtf8()); - q.bindValue(QSL(":description"), description.toUtf8()); - q.bindValue(QSL(":date_created"), creation_date.toMSecsSinceEpoch()); - q.bindValue(QSL(":icon"), qApp->icons()->toByteArray(icon)); - q.bindValue(QSL(":category"), parent_id); - q.bindValue(QSL(":encoding"), encoding); - q.bindValue(QSL(":url"), url); - q.bindValue(QSL(":protected"), is_protected ? 1 : 0); - q.bindValue(QSL(":username"), username); - q.bindValue(QSL(":account_id"), account_id); - - if (password.isEmpty()) { - q.bindValue(QSL(":password"), password); - } - else { - q.bindValue(QSL(":password"), TextFactory::encrypt(password)); - } - - q.bindValue(QSL(":update_type"), (int) auto_update_type); - q.bindValue(QSL(":update_interval"), auto_update_interval); - q.bindValue(QSL(":type"), (int) feed_format); - - if (q.exec()) { - int new_id = q.lastInsertId().toInt(); - - // Now set custom ID in the DB. - q.prepare(QSL("UPDATE Feeds SET custom_id = :custom_id WHERE id = :id;")); - q.bindValue(QSL(":custom_id"), QString::number(new_id)); - q.bindValue(QSL(":id"), new_id); - q.exec(); - - if (ok != nullptr) { - *ok = true; - } - - return new_id; - } - else { - if (ok != nullptr) { - *ok = false; - } - - qDebug("Failed to add feed to database: '%s'.", qPrintable(q.lastError().text())); - return 0; - } -} - -bool DatabaseQueries::editFeed(QSqlDatabase db, int parent_id, int feed_id, const QString &title, - const QString &description, const QIcon &icon, - const QString &encoding, const QString &url, bool is_protected, - const QString &username, const QString &password, - Feed::AutoUpdateType auto_update_type, - int auto_update_interval, StandardFeed::Type feed_format) { - QSqlQuery q(db); - q.setForwardOnly(true); - - q.prepare("UPDATE Feeds " - "SET title = :title, description = :description, icon = :icon, category = :category, encoding = :encoding, url = :url, protected = :protected, username = :username, password = :password, update_type = :update_type, update_interval = :update_interval, type = :type " - "WHERE id = :id;"); - q.bindValue(QSL(":title"), title); - q.bindValue(QSL(":description"), description); - q.bindValue(QSL(":icon"), qApp->icons()->toByteArray(icon)); - q.bindValue(QSL(":category"), parent_id); - q.bindValue(QSL(":encoding"), encoding); - q.bindValue(QSL(":url"), url); - q.bindValue(QSL(":protected"), is_protected ? 1 : 0); - q.bindValue(QSL(":username"), username); - - if (password.isEmpty()) { - q.bindValue(QSL(":password"), password); - } - else { - q.bindValue(QSL(":password"), TextFactory::encrypt(password)); - } - - q.bindValue(QSL(":update_type"), (int) auto_update_type); - q.bindValue(QSL(":update_interval"), auto_update_interval); - q.bindValue(QSL(":type"), feed_format); - q.bindValue(QSL(":id"), feed_id); - - return q.exec(); -} - -bool DatabaseQueries::editBaseFeed(QSqlDatabase db, int feed_id, Feed::AutoUpdateType auto_update_type, - int auto_update_interval) { - QSqlQuery q(db); - - q.setForwardOnly(true); - q.prepare("UPDATE Feeds " - "SET update_type = :update_type, update_interval = :update_interval " - "WHERE id = :id;"); - - q.bindValue(QSL(":update_type"), (int) auto_update_type); - q.bindValue(QSL(":update_interval"), auto_update_interval); - q.bindValue(QSL(":id"), feed_id); - - return q.exec(); -} - -QList DatabaseQueries::getAccounts(QSqlDatabase db, bool *ok) { - QSqlQuery q(db); - QList roots; - - q.setForwardOnly(true); - q.prepare(QSL("SELECT id FROM Accounts WHERE type = :type;")); - q.bindValue(QSL(":type"), SERVICE_CODE_STD_RSS); - - if (q.exec()) { - while (q.next()) { - StandardServiceRoot *root = new StandardServiceRoot(); - root->setAccountId(q.value(0).toInt()); - roots.append(root); - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - if (ok != nullptr) { - *ok = false; - } - } - - return roots; -} - -Assignment DatabaseQueries::getCategories(QSqlDatabase db, int account_id, bool *ok) { - Assignment categories; - - // Obtain data for categories from the database. - QSqlQuery q(db); - q.setForwardOnly(true); - q.prepare(QSL("SELECT * FROM Categories WHERE account_id = :account_id;")); - q.bindValue(QSL(":account_id"), account_id); - - if (!q.exec()) { - qFatal("Query for obtaining categories failed. Error message: '%s'.", - qPrintable(q.lastError().text())); - - if (ok != nullptr) { - *ok = false; - } - } - else { - if (ok != nullptr) { - *ok = true; - } - } - - while (q.next()) { - AssignmentItem pair; - pair.first = q.value(CAT_DB_PARENT_ID_INDEX).toInt(); - pair.second = new StandardCategory(q.record()); - - categories << pair; - } - - return categories; -} - -Assignment DatabaseQueries::getFeeds(QSqlDatabase db, int account_id, bool *ok) { - Assignment feeds; - QSqlQuery q(db); - - q.setForwardOnly(true); - q.prepare(QSL("SELECT * FROM Feeds WHERE account_id = :account_id;")); - q.bindValue(QSL(":account_id"), account_id); - - if (!q.exec()) { - qFatal("Query for obtaining feeds failed. Error message: '%s'.", - qPrintable(q.lastError().text())); - - if (ok != nullptr) { - *ok = false; - } - } - - if (ok != nullptr) { - *ok = true; - } - - while (q.next()) { - // Process this feed. - StandardFeed::Type type = static_cast(q.value(FDS_DB_TYPE_INDEX).toInt()); - - switch (type) { - case StandardFeed::Atom10: - case StandardFeed::Rdf: - case StandardFeed::Rss0X: - case StandardFeed::Rss2X: { - AssignmentItem pair; - pair.first = q.value(FDS_DB_CATEGORY_INDEX).toInt(); - pair.second = new StandardFeed(q.record()); - qobject_cast(pair.second)->setType(type); - - feeds << pair; - break; - } - - default: - break; - } - } - - return feeds; -} - -bool DatabaseQueries::deleteTtRssAccount(QSqlDatabase db, int account_id) { - QSqlQuery q(db); - - q.setForwardOnly(true); - q.prepare(QSL("DELETE FROM TtRssAccounts WHERE id = :id;")); - q.bindValue(QSL(":id"), account_id); - - // Remove extra entry in "Tiny Tiny RSS accounts list" and then delete - // all the categories/feeds and messages. - return q.exec(); -} - -bool DatabaseQueries::overwriteTtRssAccount(QSqlDatabase db, const QString &username, const QString &password, - bool auth_protected, const QString &auth_username, const QString &auth_password, - const QString &url, bool force_server_side_feed_update, int account_id) { - QSqlQuery q(db); - - q.prepare("UPDATE TtRssAccounts " - "SET username = :username, password = :password, url = :url, auth_protected = :auth_protected, " - "auth_username = :auth_username, auth_password = :auth_password, force_update = :force_update " - "WHERE id = :id;"); - q.bindValue(QSL(":username"), username); - q.bindValue(QSL(":password"), TextFactory::encrypt(password)); - q.bindValue(QSL(":url"), url); - q.bindValue(QSL(":auth_protected"), auth_protected ? 1 : 0); - q.bindValue(QSL(":auth_username"), auth_username); - q.bindValue(QSL(":auth_password"), TextFactory::encrypt(auth_password)); - q.bindValue(QSL(":force_update"), force_server_side_feed_update ? 1 : 0); - q.bindValue(QSL(":id"), account_id); - - if (q.exec()) { - return true; - } - else { - qWarning("TT-RSS: Updating account failed: '%s'.", qPrintable(q.lastError().text())); - return false; - } -} - -bool DatabaseQueries::createTtRssAccount(QSqlDatabase db, int id_to_assign, const QString &username, - const QString &password, bool auth_protected, const QString &auth_username, - const QString &auth_password, const QString &url, - bool force_server_side_feed_update) { - QSqlQuery q(db); - - q.prepare("INSERT INTO TtRssAccounts (id, username, password, auth_protected, auth_username, auth_password, url, force_update) " - "VALUES (:id, :username, :password, :auth_protected, :auth_username, :auth_password, :url, :force_update);"); - q.bindValue(QSL(":id"), id_to_assign); - q.bindValue(QSL(":username"), username); - q.bindValue(QSL(":password"), TextFactory::encrypt(password)); - q.bindValue(QSL(":auth_protected"), auth_protected ? 1 : 0); - q.bindValue(QSL(":auth_username"), auth_username); - q.bindValue(QSL(":auth_password"), TextFactory::encrypt(auth_password)); - q.bindValue(QSL(":url"), url); - q.bindValue(QSL(":force_update"), force_server_side_feed_update ? 1 : 0); - - if (q.exec()) { - return true; - } - else { - qWarning("TT-RSS: Saving of new account failed: '%s'.", qPrintable(q.lastError().text())); - return false; - } -} - -Assignment DatabaseQueries::getTtRssCategories(QSqlDatabase db, int account_id, bool *ok) { - Assignment categories; - - // Obtain data for categories from the database. - QSqlQuery query_categories(db); - query_categories.setForwardOnly(true); - query_categories.prepare(QSL("SELECT * FROM Categories WHERE account_id = :account_id;")); - query_categories.bindValue(QSL(":account_id"), account_id); - - if (!query_categories.exec()) { - qFatal("Query for obtaining categories failed. Error message: '%s'.", qPrintable(query_categories.lastError().text())); - - if (ok != nullptr) { - *ok = false; - } - } - else { - if (ok != nullptr) { - *ok = true; - } - } - - while (query_categories.next()) { - AssignmentItem pair; - pair.first = query_categories.value(CAT_DB_PARENT_ID_INDEX).toInt(); - pair.second = new TtRssCategory(query_categories.record()); - - categories << pair; - } - - return categories; -} - -Assignment DatabaseQueries::getTtRssFeeds(QSqlDatabase db, int account_id, bool *ok) { - Assignment feeds; - - // All categories are now loaded. - QSqlQuery query_feeds(db); - query_feeds.setForwardOnly(true); - query_feeds.prepare(QSL("SELECT * FROM Feeds WHERE account_id = :account_id;")); - query_feeds.bindValue(QSL(":account_id"), account_id); - - if (!query_feeds.exec()) { - qFatal("Query for obtaining feeds failed. Error message: '%s'.", qPrintable(query_feeds.lastError().text())); - - if (ok != nullptr) { - *ok = false; - } - } - else { - if (ok != nullptr) { - *ok = true; - } - } - - while (query_feeds.next()) { - AssignmentItem pair; - pair.first = query_feeds.value(FDS_DB_CATEGORY_INDEX).toInt(); - pair.second = new TtRssFeed(query_feeds.record()); - - feeds << pair; - } - - return feeds; -} - -DatabaseQueries::DatabaseQueries() { -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "miscellaneous/databasequeries.h" + +#include "services/abstract/category.h" +#include "services/owncloud/owncloudserviceroot.h" +#include "services/owncloud/owncloudcategory.h" +#include "services/owncloud/owncloudfeed.h" +#include "services/owncloud/network/owncloudnetworkfactory.h" +#include "services/standard/standardserviceroot.h" +#include "services/standard/standardcategory.h" +#include "services/standard/standardfeed.h" +#include "services/tt-rss/ttrssserviceroot.h" +#include "services/tt-rss/ttrsscategory.h" +#include "services/tt-rss/ttrssfeed.h" +#include "services/tt-rss/network/ttrssnetworkfactory.h" +#include "miscellaneous/textfactory.h" +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" + +#include +#include +#include + + +bool DatabaseQueries::markMessagesReadUnread(QSqlDatabase db, const QStringList& ids, RootItem::ReadStatus read) { + QSqlQuery q(db); + q.setForwardOnly(true); + return q.exec(QString(QSL("UPDATE Messages SET is_read = %2 WHERE id IN (%1);")) + .arg(ids.join(QSL(", ")), read == RootItem::Read ? QSL("1") : QSL("0"))); +} + +bool DatabaseQueries::markMessageImportant(QSqlDatabase db, int id, RootItem::Importance importance) { + QSqlQuery q(db); + q.setForwardOnly(true); + + if (!q.prepare(QSL("UPDATE Messages SET is_important = :important WHERE id = :id;"))) { + qWarning("Query preparation failed for message importance switch."); + return false; + } + + q.bindValue(QSL(":id"), id); + q.bindValue(QSL(":important"), (int) importance); + // Commit changes. + return q.exec(); +} + +bool DatabaseQueries::markFeedsReadUnread(QSqlDatabase db, const QStringList& ids, int account_id, RootItem::ReadStatus read) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare(QString("UPDATE Messages SET is_read = :read " + "WHERE feed IN (%1) AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;").arg(ids.join(QSL(", ")))); + q.bindValue(QSL(":read"), read == RootItem::Read ? 1 : 0); + q.bindValue(QSL(":account_id"), account_id); + return q.exec(); +} + +bool DatabaseQueries::markBinReadUnread(QSqlDatabase db, int account_id, RootItem::ReadStatus read) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare("UPDATE Messages SET is_read = :read " + "WHERE is_deleted = 1 AND is_pdeleted = 0 AND account_id = :account_id;"); + q.bindValue(QSL(":read"), read == RootItem::Read ? 1 : 0); + q.bindValue(QSL(":account_id"), account_id); + return q.exec(); +} + +bool DatabaseQueries::markAccountReadUnread(QSqlDatabase db, int account_id, RootItem::ReadStatus read) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare(QSL("UPDATE Messages SET is_read = :read WHERE is_pdeleted = 0 AND account_id = :account_id;")); + q.bindValue(QSL(":account_id"), account_id); + q.bindValue(QSL(":read"), read == RootItem::Read ? 1 : 0); + return q.exec(); +} + +bool DatabaseQueries::switchMessagesImportance(QSqlDatabase db, const QStringList& ids) { + QSqlQuery q(db); + q.setForwardOnly(true); + return q.exec(QString(QSL("UPDATE Messages SET is_important = NOT is_important WHERE id IN (%1);")).arg(ids.join(QSL(", ")))); +} + +bool DatabaseQueries::permanentlyDeleteMessages(QSqlDatabase db, const QStringList& ids) { + QSqlQuery q(db); + q.setForwardOnly(true); + return q.exec(QString(QSL("UPDATE Messages SET is_pdeleted = 1 WHERE id IN (%1);")).arg(ids.join(QSL(", ")))); +} + +bool DatabaseQueries::deleteOrRestoreMessagesToFromBin(QSqlDatabase db, const QStringList& ids, bool deleted) { + QSqlQuery q(db); + q.setForwardOnly(true); + return q.exec(QString(QSL("UPDATE Messages SET is_deleted = %2, is_pdeleted = %3 WHERE id IN (%1);")).arg(ids.join(QSL(", ")), + QString::number(deleted ? 1 : 0), + QString::number(0))); +} + +bool DatabaseQueries::restoreBin(QSqlDatabase db, int account_id) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare("UPDATE Messages SET is_deleted = 0 " + "WHERE is_deleted = 1 AND is_pdeleted = 0 AND account_id = :account_id;"); + q.bindValue(QSL(":account_id"), account_id); + return q.exec(); +} + +bool DatabaseQueries::purgeImportantMessages(QSqlDatabase db) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare(QSL("DELETE FROM Messages WHERE is_important = 1;")); + return q.exec(); +} + +bool DatabaseQueries::purgeReadMessages(QSqlDatabase db) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare(QSL("DELETE FROM Messages WHERE is_important = :is_important AND is_deleted = :is_deleted AND is_read = :is_read;")); + q.bindValue(QSL(":is_read"), 1); + // Remove only messages which are NOT in recycle bin. + q.bindValue(QSL(":is_deleted"), 0); + // Remove only messages which are NOT starred. + q.bindValue(QSL(":is_important"), 0); + return q.exec(); +} + +bool DatabaseQueries::purgeOldMessages(QSqlDatabase db, int older_than_days) { + QSqlQuery q(db); + const qint64 since_epoch = QDateTime::currentDateTimeUtc().addDays(-older_than_days).toMSecsSinceEpoch(); + q.setForwardOnly(true); + q.prepare(QSL("DELETE FROM Messages WHERE is_important = :is_important AND date_created < :date_created;")); + q.bindValue(QSL(":date_created"), since_epoch); + // Remove only messages which are NOT starred. + q.bindValue(QSL(":is_important"), 0); + return q.exec(); +} + +bool DatabaseQueries::purgeRecycleBin(QSqlDatabase db) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare(QSL("DELETE FROM Messages WHERE is_important = :is_important AND is_deleted = :is_deleted;")); + q.bindValue(QSL(":is_deleted"), 1); + // Remove only messages which are NOT starred. + q.bindValue(QSL(":is_important"), 0); + return q.exec(); +} + +QMap> DatabaseQueries::getMessageCountsForCategory(QSqlDatabase db, int custom_id, int account_id, +bool including_total_counts, bool* ok) { + QMap> counts; + QSqlQuery q(db); + q.setForwardOnly(true); + + if (including_total_counts) { + q.prepare("SELECT feed, sum((is_read + 1) % 2), count(*) FROM Messages " + "WHERE feed IN (SELECT custom_id FROM Feeds WHERE category = :category AND account_id = :account_id) AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id " + "GROUP BY feed;"); + } + + else { + q.prepare("SELECT feed, sum((is_read + 1) % 2) FROM Messages " + "WHERE feed IN (SELECT custom_id FROM Feeds WHERE category = :category AND account_id = :account_id) AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id " + "GROUP BY feed;"); + } + + q.bindValue(QSL(":category"), custom_id); + q.bindValue(QSL(":account_id"), account_id); + + if (q.exec()) { + while (q.next()) { + int feed_id = q.value(0).toInt(); + int unread_count = q.value(1).toInt(); + + if (including_total_counts) { + int total_count = q.value(2).toInt(); + counts.insert(feed_id, QPair(unread_count, total_count)); + } + + else { + counts.insert(feed_id, QPair(unread_count, 0)); + } + } + + if (ok != nullptr) { + *ok = true; + } + } + + else { + if (ok != nullptr) { + *ok = false; + } + } + + return counts; +} + +QMap> DatabaseQueries::getMessageCountsForAccount(QSqlDatabase db, int account_id, +bool including_total_counts, bool* ok) { + QMap> counts; + QSqlQuery q(db); + q.setForwardOnly(true); + + if (including_total_counts) { + q.prepare("SELECT feed, sum((is_read + 1) % 2), count(*) FROM Messages " + "WHERE is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id " + "GROUP BY feed;"); + } + + else { + q.prepare("SELECT feed, sum((is_read + 1) % 2) FROM Messages " + "WHERE is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id " + "GROUP BY feed;"); + } + + q.bindValue(QSL(":account_id"), account_id); + + if (q.exec()) { + while (q.next()) { + int feed_id = q.value(0).toInt(); + int unread_count = q.value(1).toInt(); + + if (including_total_counts) { + int total_count = q.value(2).toInt(); + counts.insert(feed_id, QPair(unread_count, total_count)); + } + + else { + counts.insert(feed_id, QPair(unread_count, 0)); + } + } + + if (ok != nullptr) { + *ok = true; + } + } + + else { + if (ok != nullptr) { + *ok = false; + } + } + + return counts; +} + +int DatabaseQueries::getMessageCountsForFeed(QSqlDatabase db, int feed_custom_id, + int account_id, bool including_total_counts, bool* ok) { + QSqlQuery q(db); + q.setForwardOnly(true); + + if (including_total_counts) { + q.prepare("SELECT count(*) FROM Messages " + "WHERE feed = :feed AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;"); + } + + else { + q.prepare("SELECT count(*) FROM Messages " + "WHERE feed = :feed AND is_deleted = 0 AND is_pdeleted = 0 AND is_read = 0 AND account_id = :account_id;"); + } + + q.bindValue(QSL(":feed"), feed_custom_id); + q.bindValue(QSL(":account_id"), account_id); + + if (q.exec() && q.next()) { + if (ok != nullptr) { + *ok = true; + } + + return q.value(0).toInt(); + } + + else { + if (ok != nullptr) { + *ok = false; + } + + return 0; + } +} + +int DatabaseQueries::getMessageCountsForBin(QSqlDatabase db, int account_id, bool including_total_counts, bool* ok) { + QSqlQuery q(db); + q.setForwardOnly(true); + + if (including_total_counts) { + q.prepare("SELECT count(*) FROM Messages " + "WHERE is_deleted = 1 AND is_pdeleted = 0 AND account_id = :account_id;"); + } + + else { + q.prepare("SELECT count(*) FROM Messages " + "WHERE is_read = 0 AND is_deleted = 1 AND is_pdeleted = 0 AND account_id = :account_id;"); + } + + q.bindValue(QSL(":account_id"), account_id); + + if (q.exec() && q.next()) { + if (ok != nullptr) { + *ok = true; + } + + return q.value(0).toInt(); + } + + else { + if (ok != nullptr) { + *ok = false; + } + + return 0; + } +} + +QList DatabaseQueries::getUndeletedMessagesForFeed(QSqlDatabase db, int feed_custom_id, int account_id, bool* ok) { + QList messages; + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare("SELECT * " + "FROM Messages " + "WHERE is_deleted = 0 AND is_pdeleted = 0 AND feed = :feed AND account_id = :account_id;"); + q.bindValue(QSL(":feed"), feed_custom_id); + q.bindValue(QSL(":account_id"), account_id); + + if (q.exec()) { + while (q.next()) { + bool decoded; + Message message = Message::fromSqlRecord(q.record(), &decoded); + + if (decoded) { + messages.append(message); + } + } + + if (ok != nullptr) { + *ok = true; + } + } + + else { + if (ok != nullptr) { + *ok = false; + } + } + + return messages; +} + +QList DatabaseQueries::getUndeletedMessagesForBin(QSqlDatabase db, int account_id, bool* ok) { + QList messages; + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare("SELECT * " + "FROM Messages " + "WHERE is_deleted = 1 AND is_pdeleted = 0 AND account_id = :account_id;"); + q.bindValue(QSL(":account_id"), account_id); + + if (q.exec()) { + while (q.next()) { + bool decoded; + Message message = Message::fromSqlRecord(q.record(), &decoded); + + if (decoded) { + messages.append(message); + } + } + + if (ok != nullptr) { + *ok = true; + } + } + + else { + if (ok != nullptr) { + *ok = false; + } + } + + return messages; +} + +QList DatabaseQueries::getUndeletedMessagesForAccount(QSqlDatabase db, int account_id, bool* ok) { + QList messages; + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare("SELECT * " + "FROM Messages " + "WHERE is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;"); + q.bindValue(QSL(":account_id"), account_id); + + if (q.exec()) { + while (q.next()) { + bool decoded; + Message message = Message::fromSqlRecord(q.record(), &decoded); + + if (decoded) { + messages.append(message); + } + } + + if (ok != nullptr) { + *ok = true; + } + } + + else { + if (ok != nullptr) { + *ok = false; + } + } + + return messages; +} + +int DatabaseQueries::updateMessages(QSqlDatabase db, + const QList& messages, + int feed_custom_id, + int account_id, + const QString& url, + bool* any_message_changed, + bool* ok) { + if (messages.isEmpty()) { + *any_message_changed = false; + *ok = true; + return 0; + } + + bool use_transactions = qApp->settings()->value(GROUP(Database), SETTING(Database::UseTransactions)).toBool(); + // Does not make any difference, since each feed now has + // its own "custom ID" (standard feeds have their custom ID equal to primary key ID). + int updated_messages = 0; + // Prepare queries. + QSqlQuery query_select_with_url(db); + QSqlQuery query_select_with_id(db); + QSqlQuery query_update(db); + QSqlQuery query_insert(db); + QSqlQuery query_begin_transaction(db); + // Here we have query which will check for existence of the "same" message in given feed. + // The two message are the "same" if: + // 1) they belong to the same feed AND, + // 2) they have same URL AND, + // 3) they have same AUTHOR. + query_select_with_url.setForwardOnly(true); + query_select_with_url.prepare("SELECT id, date_created, is_read, is_important, contents FROM Messages " + "WHERE feed = :feed AND title = :title AND url = :url AND author = :author AND account_id = :account_id;"); + // When we have custom ID of the message, we can check directly for existence + // of that particular message. + query_select_with_id.setForwardOnly(true); + query_select_with_id.prepare("SELECT id, date_created, is_read, is_important, contents FROM Messages " + "WHERE custom_id = :custom_id AND account_id = :account_id;"); + // Used to insert new messages. + query_insert.setForwardOnly(true); + query_insert.prepare("INSERT INTO Messages " + "(feed, title, is_read, is_important, url, author, date_created, contents, enclosures, custom_id, custom_hash, account_id) " + "VALUES (:feed, :title, :is_read, :is_important, :url, :author, :date_created, :contents, :enclosures, :custom_id, :custom_hash, :account_id);"); + // Used to update existing messages. + query_update.setForwardOnly(true); + query_update.prepare("UPDATE Messages " + "SET title = :title, is_read = :is_read, is_important = :is_important, url = :url, author = :author, date_created = :date_created, contents = :contents, enclosures = :enclosures " + "WHERE id = :id;"); + + if (use_transactions && !query_begin_transaction.exec(qApp->database()->obtainBeginTransactionSql())) { + qCritical("Transaction start for message downloader failed: '%s'.", qPrintable(query_begin_transaction.lastError().text())); + return updated_messages; + } + + foreach (Message message, messages) { + // Check if messages contain relative URLs and if they do, then replace them. + if (message.m_url.startsWith(QL1S("//"))) { + message.m_url = QString(URI_SCHEME_HTTP) + message.m_url.mid(2); + } + + else if (message.m_url.startsWith(QL1S("/"))) { + QString new_message_url = QUrl(url).toString(QUrl::RemoveUserInfo | + QUrl::RemovePath | + QUrl::RemoveQuery | + QUrl::RemoveFilename | + QUrl::StripTrailingSlash); + new_message_url += message.m_url; + message.m_url = new_message_url; + } + + int id_existing_message = -1; + qint64 date_existing_message; + bool is_read_existing_message; + bool is_important_existing_message; + QString contents_existing_message; + + if (message.m_customId.isEmpty()) { + // We need to recognize existing messages according URL & AUTHOR. + // NOTE: This particularly concerns messages from standard account. + query_select_with_url.bindValue(QSL(":feed"), feed_custom_id); + query_select_with_url.bindValue(QSL(":title"), message.m_title); + query_select_with_url.bindValue(QSL(":url"), message.m_url); + query_select_with_url.bindValue(QSL(":author"), message.m_author); + query_select_with_url.bindValue(QSL(":account_id"), account_id); + + if (query_select_with_url.exec() && query_select_with_url.next()) { + id_existing_message = query_select_with_url.value(0).toInt(); + date_existing_message = query_select_with_url.value(1).value(); + is_read_existing_message = query_select_with_url.value(2).toBool(); + is_important_existing_message = query_select_with_url.value(3).toBool(); + contents_existing_message = query_select_with_url.value(4).toString(); + } + + else if (query_select_with_url.lastError().isValid()) { + qWarning("Failed to check for existing message in DB via URL: '%s'.", qPrintable(query_select_with_url.lastError().text())); + } + + query_select_with_url.finish(); + } + + else { + // We can recognize existing messages via their custom ID. + // NOTE: This concerns messages from custom accounts, like TT-RSS or ownCloud News. + query_select_with_id.bindValue(QSL(":account_id"), account_id); + query_select_with_id.bindValue(QSL(":custom_id"), message.m_customId); + + if (query_select_with_id.exec() && query_select_with_id.next()) { + id_existing_message = query_select_with_id.value(0).toInt(); + date_existing_message = query_select_with_id.value(1).value(); + is_read_existing_message = query_select_with_id.value(2).toBool(); + is_important_existing_message = query_select_with_id.value(3).toBool(); + contents_existing_message = query_select_with_id.value(4).toString(); + } + + else if (query_select_with_id.lastError().isValid()) { + qDebug("Failed to check for existing message in DB via ID: '%s'.", qPrintable(query_select_with_id.lastError().text())); + } + + query_select_with_id.finish(); + } + + // Now, check if this message is already in the DB. + if (id_existing_message >= 0) { + // Message is already in the DB. + // + // Now, we update it if at least one of next conditions is true: + // 1) Message has custom ID AND (its date OR read status OR starred status are changed). + // 2) Message has its date fetched from feed AND its date is different from date in DB and contents is changed. + if (/* 1 */ (!message.m_customId.isEmpty() && (message.m_created.toMSecsSinceEpoch() != date_existing_message || message.m_isRead != is_read_existing_message + || message.m_isImportant != is_important_existing_message)) || + /* 2 */ (message.m_createdFromFeed && message.m_created.toMSecsSinceEpoch() != date_existing_message && message.m_contents != contents_existing_message)) { + // Message exists, it is changed, update it. + query_update.bindValue(QSL(":title"), message.m_title); + query_update.bindValue(QSL(":is_read"), (int) message.m_isRead); + query_update.bindValue(QSL(":is_important"), (int) message.m_isImportant); + query_update.bindValue(QSL(":url"), message.m_url); + query_update.bindValue(QSL(":author"), message.m_author); + query_update.bindValue(QSL(":date_created"), message.m_created.toMSecsSinceEpoch()); + query_update.bindValue(QSL(":contents"), message.m_contents); + query_update.bindValue(QSL(":enclosures"), Enclosures::encodeEnclosuresToString(message.m_enclosures)); + query_update.bindValue(QSL(":id"), id_existing_message); + *any_message_changed = true; + + if (query_update.exec() && !message.m_isRead) { + updated_messages++; + } + + else if (query_update.lastError().isValid()) { + qWarning("Failed to update message in DB: '%s'.", qPrintable(query_update.lastError().text())); + } + + query_update.finish(); + qDebug("Updating message '%s' in DB.", qPrintable(message.m_title)); + } + } + + else { + // Message with this URL is not fetched in this feed yet. + query_insert.bindValue(QSL(":feed"), feed_custom_id); + query_insert.bindValue(QSL(":title"), message.m_title); + query_insert.bindValue(QSL(":is_read"), (int) message.m_isRead); + query_insert.bindValue(QSL(":is_important"), (int) message.m_isImportant); + query_insert.bindValue(QSL(":url"), message.m_url); + query_insert.bindValue(QSL(":author"), message.m_author); + query_insert.bindValue(QSL(":date_created"), message.m_created.toMSecsSinceEpoch()); + query_insert.bindValue(QSL(":contents"), message.m_contents); + query_insert.bindValue(QSL(":enclosures"), Enclosures::encodeEnclosuresToString(message.m_enclosures)); + query_insert.bindValue(QSL(":custom_id"), message.m_customId); + query_insert.bindValue(QSL(":custom_hash"), message.m_customHash); + query_insert.bindValue(QSL(":account_id"), account_id); + + if (query_insert.exec() && query_insert.numRowsAffected() == 1) { + updated_messages++; + qDebug("Added new message '%s' to DB.", qPrintable(message.m_title)); + } + + else if (query_insert.lastError().isValid()) { + qWarning("Failed to insert message to DB: '%s' - message title is '%s'.", + qPrintable(query_insert.lastError().text()), + qPrintable(message.m_title)); + } + + query_insert.finish(); + } + } + + // Now, fixup custom IDS for messages which initially did not have them, + // just to keep the data consistent. + if (db.exec("UPDATE Messages " + "SET custom_id = id " + "WHERE custom_id IS NULL OR custom_id = '';").lastError().isValid()) { + qWarning("Failed to set custom ID for all messages: '%s'.", qPrintable(db.lastError().text())); + } + + if (use_transactions && !db.commit()) { + qCritical("Transaction commit for message downloader failed: '%s'.", qPrintable(db.lastError().text())); + db.rollback(); + + if (ok != nullptr) { + *ok = false; + updated_messages = 0; + } + } + + else { + if (ok != nullptr) { + *ok = true; + } + } + + return updated_messages; +} + +bool DatabaseQueries::purgeMessagesFromBin(QSqlDatabase db, bool clear_only_read, int account_id) { + QSqlQuery q(db); + q.setForwardOnly(true); + + if (clear_only_read) { + q.prepare(QSL("UPDATE Messages SET is_pdeleted = 1 WHERE is_read = 1 AND is_deleted = 1 AND account_id = :account_id;")); + } + + else { + q.prepare(QSL("UPDATE Messages SET is_pdeleted = 1 WHERE is_deleted = 1 AND account_id = :account_id;")); + } + + q.bindValue(QSL(":account_id"), account_id); + return q.exec(); +} + +bool DatabaseQueries::deleteAccount(QSqlDatabase db, int account_id) { + QSqlQuery query(db); + query.setForwardOnly(true); + QStringList queries; + queries << QSL("DELETE FROM Messages WHERE account_id = :account_id;") << + QSL("DELETE FROM Feeds WHERE account_id = :account_id;") << + QSL("DELETE FROM Categories WHERE account_id = :account_id;") << + QSL("DELETE FROM Accounts WHERE id = :account_id;"); + + foreach (const QString& q, queries) { + query.prepare(q); + query.bindValue(QSL(":account_id"), account_id); + + if (!query.exec()) { + qCritical("Removing of account from DB failed, this is critical: '%s'.", qPrintable(query.lastError().text())); + return false; + } + + else { + query.finish(); + } + } + + return true; +} + +bool DatabaseQueries::deleteAccountData(QSqlDatabase db, int account_id, bool delete_messages_too) { + bool result = true; + QSqlQuery q(db); + q.setForwardOnly(true); + + if (delete_messages_too) { + q.prepare(QSL("DELETE FROM Messages WHERE account_id = :account_id;")); + q.bindValue(QSL(":account_id"), account_id); + result &= q.exec(); + } + + q.prepare(QSL("DELETE FROM Feeds WHERE account_id = :account_id;")); + q.bindValue(QSL(":account_id"), account_id); + result &= q.exec(); + q.prepare(QSL("DELETE FROM Categories WHERE account_id = :account_id;")); + q.bindValue(QSL(":account_id"), account_id); + result &= q.exec(); + return result; +} + +bool DatabaseQueries::cleanFeeds(QSqlDatabase db, const QStringList& ids, bool clean_read_only, int account_id) { + QSqlQuery q(db); + q.setForwardOnly(true); + + if (clean_read_only) { + q.prepare(QString("UPDATE Messages SET is_deleted = :deleted " + "WHERE feed IN (%1) AND is_deleted = 0 AND is_pdeleted = 0 AND is_read = 1 AND account_id = :account_id;") + .arg(ids.join(QSL(", ")))); + } + + else { + q.prepare(QString("UPDATE Messages SET is_deleted = :deleted " + "WHERE feed IN (%1) AND is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;") + .arg(ids.join(QSL(", ")))); + } + + q.bindValue(QSL(":deleted"), 1); + q.bindValue(QSL(":account_id"), account_id); + + if (!q.exec()) { + qDebug("Cleaning of feeds failed: '%s'.", qPrintable(q.lastError().text())); + return false; + } + + else { + return true; + } +} + +bool DatabaseQueries::purgeLeftoverMessages(QSqlDatabase db, int account_id) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare(QSL("DELETE FROM Messages WHERE account_id = :account_id AND feed NOT IN (SELECT custom_id FROM Feeds WHERE account_id = :account_id);")); + q.bindValue(QSL(":account_id"), account_id); + + if (!q.exec()) { + qWarning("Removing of left over messages failed: '%s'.", qPrintable(q.lastError().text())); + return false; + } + + else { + return true; + } +} + +bool DatabaseQueries::storeAccountTree(QSqlDatabase db, RootItem* tree_root, int account_id) { + QSqlQuery query_category(db); + QSqlQuery query_feed(db); + query_category.setForwardOnly(true); + query_feed.setForwardOnly(true); + query_category.prepare("INSERT INTO Categories (parent_id, title, account_id, custom_id) " + "VALUES (:parent_id, :title, :account_id, :custom_id);"); + query_feed.prepare("INSERT INTO Feeds (title, icon, category, protected, update_type, update_interval, account_id, custom_id) " + "VALUES (:title, :icon, :category, :protected, :update_type, :update_interval, :account_id, :custom_id);"); + + // Iterate all children. + foreach (RootItem* child, tree_root->getSubTree()) { + if (child->kind() == RootItemKind::Category) { + query_category.bindValue(QSL(":parent_id"), child->parent()->id()); + query_category.bindValue(QSL(":title"), child->title()); + query_category.bindValue(QSL(":account_id"), account_id); + query_category.bindValue(QSL(":custom_id"), QString::number(child->toCategory()->customId())); + + if (query_category.exec()) { + child->setId(query_category.lastInsertId().toInt()); + } + + else { + return false; + } + } + + else if (child->kind() == RootItemKind::Feed) { + Feed* feed = child->toFeed(); + query_feed.bindValue(QSL(":title"), feed->title()); + query_feed.bindValue(QSL(":icon"), qApp->icons()->toByteArray(feed->icon())); + query_feed.bindValue(QSL(":category"), feed->parent()->customId()); + query_feed.bindValue(QSL(":protected"), 0); + query_feed.bindValue(QSL(":update_type"), (int) feed->autoUpdateType()); + query_feed.bindValue(QSL(":update_interval"), feed->autoUpdateInitialInterval()); + query_feed.bindValue(QSL(":account_id"), account_id); + query_feed.bindValue(QSL(":custom_id"), feed->customId()); + + if (query_feed.exec()) { + feed->setId(query_feed.lastInsertId().toInt()); + } + + else { + return false; + } + } + } + + return true; +} + +QStringList DatabaseQueries::customIdsOfMessagesFromAccount(QSqlDatabase db, int account_id, bool* ok) { + QSqlQuery q(db); + QStringList ids; + q.setForwardOnly(true); + q.prepare(QSL("SELECT custom_id FROM Messages WHERE is_deleted = 0 AND is_pdeleted = 0 AND account_id = :account_id;")); + q.bindValue(QSL(":account_id"), account_id); + + if (ok != nullptr) { + *ok = q.exec(); + } + + else { + q.exec(); + } + + while (q.next()) { + ids.append(q.value(0).toString()); + } + + return ids; +} + +QStringList DatabaseQueries::customIdsOfMessagesFromBin(QSqlDatabase db, int account_id, bool* ok) { + QSqlQuery q(db); + QStringList ids; + q.setForwardOnly(true); + q.prepare(QSL("SELECT custom_id FROM Messages WHERE is_deleted = 1 AND is_pdeleted = 0 AND account_id = :account_id;")); + q.bindValue(QSL(":account_id"), account_id); + + if (ok != nullptr) { + *ok = q.exec(); + } + + else { + q.exec(); + } + + while (q.next()) { + ids.append(q.value(0).toString()); + } + + return ids; +} + +QStringList DatabaseQueries::customIdsOfMessagesFromFeed(QSqlDatabase db, int feed_custom_id, int account_id, bool* ok) { + QSqlQuery q(db); + QStringList ids; + q.setForwardOnly(true); + q.prepare(QSL("SELECT custom_id FROM Messages WHERE is_deleted = 0 AND is_pdeleted = 0 AND feed = :feed AND account_id = :account_id;")); + q.bindValue(QSL(":account_id"), account_id); + q.bindValue(QSL(":feed"), feed_custom_id); + + if (ok != nullptr) { + *ok = q.exec(); + } + + else { + q.exec(); + } + + while (q.next()) { + ids.append(q.value(0).toString()); + } + + return ids; +} + +QList DatabaseQueries::getOwnCloudAccounts(QSqlDatabase db, bool* ok) { + QSqlQuery query(db); + QList roots; + + if (query.exec("SELECT * FROM OwnCloudAccounts;")) { + while (query.next()) { + OwnCloudServiceRoot* root = new OwnCloudServiceRoot(); + root->setId(query.value(0).toInt()); + root->setAccountId(query.value(0).toInt()); + root->network()->setAuthUsername(query.value(1).toString()); + root->network()->setAuthPassword(TextFactory::decrypt(query.value(2).toString())); + root->network()->setUrl(query.value(3).toString()); + root->network()->setForceServerSideUpdate(query.value(4).toBool()); + root->updateTitle(); + roots.append(root); + } + + if (ok != nullptr) { + *ok = true; + } + } + + else { + qWarning("OwnCloud: Getting list of activated accounts failed: '%s'.", qPrintable(query.lastError().text())); + + if (ok != nullptr) { + *ok = false; + } + } + + return roots; +} + +QList DatabaseQueries::getTtRssAccounts(QSqlDatabase db, bool* ok) { + QSqlQuery query(db); + QList roots; + + if (query.exec("SELECT * FROM TtRssAccounts;")) { + while (query.next()) { + TtRssServiceRoot* root = new TtRssServiceRoot(); + root->setId(query.value(0).toInt()); + root->setAccountId(query.value(0).toInt()); + root->network()->setUsername(query.value(1).toString()); + root->network()->setPassword(TextFactory::decrypt(query.value(2).toString())); + root->network()->setAuthIsUsed(query.value(3).toBool()); + root->network()->setAuthUsername(query.value(4).toString()); + root->network()->setAuthPassword(TextFactory::decrypt(query.value(5).toString())); + root->network()->setUrl(query.value(6).toString()); + root->network()->setForceServerSideUpdate(query.value(7).toBool()); + root->updateTitle(); + roots.append(root); + } + + if (ok != nullptr) { + *ok = true; + } + } + + else { + qWarning("TT-RSS: Getting list of activated accounts failed: '%s'.", qPrintable(query.lastError().text())); + + if (ok != nullptr) { + *ok = false; + } + } + + return roots; +} + +bool DatabaseQueries::deleteOwnCloudAccount(QSqlDatabase db, int account_id) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare(QSL("DELETE FROM OwnCloudAccounts WHERE id = :id;")); + q.bindValue(QSL(":id"), account_id); + return q.exec(); +} + +bool DatabaseQueries::overwriteOwnCloudAccount(QSqlDatabase db, const QString& username, const QString& password, + const QString& url, bool force_server_side_feed_update, int account_id) { + QSqlQuery query(db); + query.prepare("UPDATE OwnCloudAccounts " + "SET username = :username, password = :password, url = :url, force_update = :force_update " + "WHERE id = :id;"); + query.bindValue(QSL(":username"), username); + query.bindValue(QSL(":password"), TextFactory::encrypt(password)); + query.bindValue(QSL(":url"), url); + query.bindValue(QSL(":force_update"), force_server_side_feed_update ? 1 : 0); + query.bindValue(QSL(":id"), account_id); + + if (query.exec()) { + return true; + } + + else { + qWarning("ownCloud: Updating account failed: '%s'.", qPrintable(query.lastError().text())); + return false; + } +} + +bool DatabaseQueries::createOwnCloudAccount(QSqlDatabase db, int id_to_assign, const QString& username, + const QString& password, const QString& url, + bool force_server_side_feed_update) { + QSqlQuery q(db); + q.prepare("INSERT INTO OwnCloudAccounts (id, username, password, url, force_update) " + "VALUES (:id, :username, :password, :url, :force_update);"); + q.bindValue(QSL(":id"), id_to_assign); + q.bindValue(QSL(":username"), username); + q.bindValue(QSL(":password"), TextFactory::encrypt(password)); + q.bindValue(QSL(":url"), url); + q.bindValue(QSL(":force_update"), force_server_side_feed_update ? 1 : 0); + + if (q.exec()) { + return true; + } + + else { + qWarning("ownCloud: Inserting of new account failed: '%s'.", qPrintable(q.lastError().text())); + return false; + } +} + +int DatabaseQueries::createAccount(QSqlDatabase db, const QString& code, bool* ok) { + QSqlQuery q(db); + + // First obtain the ID, which can be assigned to this new account. + if (!q.exec("SELECT max(id) FROM Accounts;") || !q.next()) { + qWarning("Getting max ID from Accounts table failed: '%s'.", qPrintable(q.lastError().text())); + + if (ok != nullptr) { + *ok = false; + } + + return 0; + } + + int id_to_assign = q.value(0).toInt() + 1; + q.prepare(QSL("INSERT INTO Accounts (id, type) VALUES (:id, :type);")); + q.bindValue(QSL(":id"), id_to_assign); + q.bindValue(QSL(":type"), code); + + if (q.exec()) { + if (ok != nullptr) { + *ok = true; + } + + return id_to_assign; + } + + else { + if (ok != nullptr) { + *ok = false; + } + + qWarning("Inserting of new account failed: '%s'.", qPrintable(q.lastError().text())); + return 0; + } +} + +Assignment DatabaseQueries::getOwnCloudCategories(QSqlDatabase db, int account_id, bool* ok) { + Assignment categories; + // Obtain data for categories from the database. + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare(QSL("SELECT * FROM Categories WHERE account_id = :account_id;")); + q.bindValue(QSL(":account_id"), account_id); + + if (!q.exec()) { + qFatal("ownCloud: Query for obtaining categories failed. Error message: '%s'.", qPrintable(q.lastError().text())); + + if (ok != nullptr) { + *ok = false; + } + } + + while (q.next()) { + AssignmentItem pair; + pair.first = q.value(CAT_DB_PARENT_ID_INDEX).toInt(); + pair.second = new OwnCloudCategory(q.record()); + categories << pair; + } + + if (ok != nullptr) { + *ok = true; + } + + return categories; +} + +Assignment DatabaseQueries::getOwnCloudFeeds(QSqlDatabase db, int account_id, bool* ok) { + Assignment feeds; + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare(QSL("SELECT * FROM Feeds WHERE account_id = :account_id;")); + q.bindValue(QSL(":account_id"), account_id); + + if (!q.exec()) { + qFatal("ownCloud: Query for obtaining feeds failed. Error message: '%s'.", qPrintable(q.lastError().text())); + + if (ok != nullptr) { + *ok = false; + } + } + + while (q.next()) { + AssignmentItem pair; + pair.first = q.value(FDS_DB_CATEGORY_INDEX).toInt(); + pair.second = new OwnCloudFeed(q.record()); + feeds << pair; + } + + if (ok != nullptr) { + *ok = true; + } + + return feeds; +} + +bool DatabaseQueries::deleteFeed(QSqlDatabase db, int feed_custom_id, int account_id) { + QSqlQuery q(db); + q.setForwardOnly(true); + // Remove all messages from this feed. + q.prepare(QSL("DELETE FROM Messages WHERE feed = :feed AND account_id = :account_id;")); + q.bindValue(QSL(":feed"), feed_custom_id); + q.bindValue(QSL(":account_id"), account_id); + + if (!q.exec()) { + return false; + } + + // Remove feed itself. + q.prepare(QSL("DELETE FROM Feeds WHERE custom_id = :feed AND account_id = :account_id;")); + q.bindValue(QSL(":feed"), feed_custom_id); + q.bindValue(QSL(":account_id"), account_id); + return q.exec(); +} + +bool DatabaseQueries::deleteCategory(QSqlDatabase db, int id) { + QSqlQuery q(db); + // Remove this category from database. + q.setForwardOnly(true); + q.prepare(QSL("DELETE FROM Categories WHERE id = :category;")); + q.bindValue(QSL(":category"), id); + return q.exec(); +} + +int DatabaseQueries::addCategory(QSqlDatabase db, int parent_id, int account_id, const QString& title, + const QString& description, QDateTime creation_date, const QIcon& icon, + bool* ok) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare("INSERT INTO Categories " + "(parent_id, title, description, date_created, icon, account_id) " + "VALUES (:parent_id, :title, :description, :date_created, :icon, :account_id);"); + q.bindValue(QSL(":parent_id"), parent_id); + q.bindValue(QSL(":title"), title); + q.bindValue(QSL(":description"), description); + q.bindValue(QSL(":date_created"), creation_date.toMSecsSinceEpoch()); + q.bindValue(QSL(":icon"), qApp->icons()->toByteArray(icon)); + q.bindValue(QSL(":account_id"), account_id); + + if (!q.exec()) { + qDebug("Failed to add category to database: '%s'.", qPrintable(q.lastError().text())); + + if (ok != nullptr) { + *ok = false; + } + + // Query failed. + return 0; + } + + else { + if (ok != nullptr) { + *ok = true; + } + + int new_id = q.lastInsertId().toInt(); + // Now set custom ID in the DB. + q.prepare(QSL("UPDATE Categories SET custom_id = :custom_id WHERE id = :id;")); + q.bindValue(QSL(":custom_id"), QString::number(new_id)); + q.bindValue(QSL(":id"), new_id); + q.exec(); + return new_id; + } +} + +bool DatabaseQueries::editCategory(QSqlDatabase db, int parent_id, int category_id, + const QString& title, const QString& description, const QIcon& icon) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare("UPDATE Categories " + "SET title = :title, description = :description, icon = :icon, parent_id = :parent_id " + "WHERE id = :id;"); + q.bindValue(QSL(":title"), title); + q.bindValue(QSL(":description"), description); + q.bindValue(QSL(":icon"), qApp->icons()->toByteArray(icon)); + q.bindValue(QSL(":parent_id"), parent_id); + q.bindValue(QSL(":id"), category_id); + return q.exec(); +} + +int DatabaseQueries::addFeed(QSqlDatabase db, int parent_id, int account_id, const QString& title, + const QString& description, QDateTime creation_date, const QIcon& icon, + const QString& encoding, const QString& url, bool is_protected, + const QString& username, const QString& password, + Feed::AutoUpdateType auto_update_type, + int auto_update_interval, StandardFeed::Type feed_format, bool* ok) { + QSqlQuery q(db); + qDebug() << "Adding feed with title '" << title.toUtf8() << "' to DB."; + q.setForwardOnly(true); + q.prepare("INSERT INTO Feeds " + "(title, description, date_created, icon, category, encoding, url, protected, username, password, update_type, update_interval, type, account_id) " + "VALUES (:title, :description, :date_created, :icon, :category, :encoding, :url, :protected, :username, :password, :update_type, :update_interval, :type, :account_id);"); + q.bindValue(QSL(":title"), title.toUtf8()); + q.bindValue(QSL(":description"), description.toUtf8()); + q.bindValue(QSL(":date_created"), creation_date.toMSecsSinceEpoch()); + q.bindValue(QSL(":icon"), qApp->icons()->toByteArray(icon)); + q.bindValue(QSL(":category"), parent_id); + q.bindValue(QSL(":encoding"), encoding); + q.bindValue(QSL(":url"), url); + q.bindValue(QSL(":protected"), is_protected ? 1 : 0); + q.bindValue(QSL(":username"), username); + q.bindValue(QSL(":account_id"), account_id); + + if (password.isEmpty()) { + q.bindValue(QSL(":password"), password); + } + + else { + q.bindValue(QSL(":password"), TextFactory::encrypt(password)); + } + + q.bindValue(QSL(":update_type"), (int) auto_update_type); + q.bindValue(QSL(":update_interval"), auto_update_interval); + q.bindValue(QSL(":type"), (int) feed_format); + + if (q.exec()) { + int new_id = q.lastInsertId().toInt(); + // Now set custom ID in the DB. + q.prepare(QSL("UPDATE Feeds SET custom_id = :custom_id WHERE id = :id;")); + q.bindValue(QSL(":custom_id"), QString::number(new_id)); + q.bindValue(QSL(":id"), new_id); + q.exec(); + + if (ok != nullptr) { + *ok = true; + } + + return new_id; + } + + else { + if (ok != nullptr) { + *ok = false; + } + + qDebug("Failed to add feed to database: '%s'.", qPrintable(q.lastError().text())); + return 0; + } +} + +bool DatabaseQueries::editFeed(QSqlDatabase db, int parent_id, int feed_id, const QString& title, + const QString& description, const QIcon& icon, + const QString& encoding, const QString& url, bool is_protected, + const QString& username, const QString& password, + Feed::AutoUpdateType auto_update_type, + int auto_update_interval, StandardFeed::Type feed_format) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare("UPDATE Feeds " + "SET title = :title, description = :description, icon = :icon, category = :category, encoding = :encoding, url = :url, protected = :protected, username = :username, password = :password, update_type = :update_type, update_interval = :update_interval, type = :type " + "WHERE id = :id;"); + q.bindValue(QSL(":title"), title); + q.bindValue(QSL(":description"), description); + q.bindValue(QSL(":icon"), qApp->icons()->toByteArray(icon)); + q.bindValue(QSL(":category"), parent_id); + q.bindValue(QSL(":encoding"), encoding); + q.bindValue(QSL(":url"), url); + q.bindValue(QSL(":protected"), is_protected ? 1 : 0); + q.bindValue(QSL(":username"), username); + + if (password.isEmpty()) { + q.bindValue(QSL(":password"), password); + } + + else { + q.bindValue(QSL(":password"), TextFactory::encrypt(password)); + } + + q.bindValue(QSL(":update_type"), (int) auto_update_type); + q.bindValue(QSL(":update_interval"), auto_update_interval); + q.bindValue(QSL(":type"), feed_format); + q.bindValue(QSL(":id"), feed_id); + return q.exec(); +} + +bool DatabaseQueries::editBaseFeed(QSqlDatabase db, int feed_id, Feed::AutoUpdateType auto_update_type, + int auto_update_interval) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare("UPDATE Feeds " + "SET update_type = :update_type, update_interval = :update_interval " + "WHERE id = :id;"); + q.bindValue(QSL(":update_type"), (int) auto_update_type); + q.bindValue(QSL(":update_interval"), auto_update_interval); + q.bindValue(QSL(":id"), feed_id); + return q.exec(); +} + +QList DatabaseQueries::getAccounts(QSqlDatabase db, bool* ok) { + QSqlQuery q(db); + QList roots; + q.setForwardOnly(true); + q.prepare(QSL("SELECT id FROM Accounts WHERE type = :type;")); + q.bindValue(QSL(":type"), SERVICE_CODE_STD_RSS); + + if (q.exec()) { + while (q.next()) { + StandardServiceRoot* root = new StandardServiceRoot(); + root->setAccountId(q.value(0).toInt()); + roots.append(root); + } + + if (ok != nullptr) { + *ok = true; + } + } + + else { + if (ok != nullptr) { + *ok = false; + } + } + + return roots; +} + +Assignment DatabaseQueries::getCategories(QSqlDatabase db, int account_id, bool* ok) { + Assignment categories; + // Obtain data for categories from the database. + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare(QSL("SELECT * FROM Categories WHERE account_id = :account_id;")); + q.bindValue(QSL(":account_id"), account_id); + + if (!q.exec()) { + qFatal("Query for obtaining categories failed. Error message: '%s'.", + qPrintable(q.lastError().text())); + + if (ok != nullptr) { + *ok = false; + } + } + + else { + if (ok != nullptr) { + *ok = true; + } + } + + while (q.next()) { + AssignmentItem pair; + pair.first = q.value(CAT_DB_PARENT_ID_INDEX).toInt(); + pair.second = new StandardCategory(q.record()); + categories << pair; + } + + return categories; +} + +Assignment DatabaseQueries::getFeeds(QSqlDatabase db, int account_id, bool* ok) { + Assignment feeds; + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare(QSL("SELECT * FROM Feeds WHERE account_id = :account_id;")); + q.bindValue(QSL(":account_id"), account_id); + + if (!q.exec()) { + qFatal("Query for obtaining feeds failed. Error message: '%s'.", + qPrintable(q.lastError().text())); + + if (ok != nullptr) { + *ok = false; + } + } + + if (ok != nullptr) { + *ok = true; + } + + while (q.next()) { + // Process this feed. + StandardFeed::Type type = static_cast(q.value(FDS_DB_TYPE_INDEX).toInt()); + + switch (type) { + case StandardFeed::Atom10: + case StandardFeed::Rdf: + case StandardFeed::Rss0X: + case StandardFeed::Rss2X: { + AssignmentItem pair; + pair.first = q.value(FDS_DB_CATEGORY_INDEX).toInt(); + pair.second = new StandardFeed(q.record()); + qobject_cast(pair.second)->setType(type); + feeds << pair; + break; + } + + default: + break; + } + } + + return feeds; +} + +bool DatabaseQueries::deleteTtRssAccount(QSqlDatabase db, int account_id) { + QSqlQuery q(db); + q.setForwardOnly(true); + q.prepare(QSL("DELETE FROM TtRssAccounts WHERE id = :id;")); + q.bindValue(QSL(":id"), account_id); + // Remove extra entry in "Tiny Tiny RSS accounts list" and then delete + // all the categories/feeds and messages. + return q.exec(); +} + +bool DatabaseQueries::overwriteTtRssAccount(QSqlDatabase db, const QString& username, const QString& password, + bool auth_protected, const QString& auth_username, const QString& auth_password, + const QString& url, bool force_server_side_feed_update, int account_id) { + QSqlQuery q(db); + q.prepare("UPDATE TtRssAccounts " + "SET username = :username, password = :password, url = :url, auth_protected = :auth_protected, " + "auth_username = :auth_username, auth_password = :auth_password, force_update = :force_update " + "WHERE id = :id;"); + q.bindValue(QSL(":username"), username); + q.bindValue(QSL(":password"), TextFactory::encrypt(password)); + q.bindValue(QSL(":url"), url); + q.bindValue(QSL(":auth_protected"), auth_protected ? 1 : 0); + q.bindValue(QSL(":auth_username"), auth_username); + q.bindValue(QSL(":auth_password"), TextFactory::encrypt(auth_password)); + q.bindValue(QSL(":force_update"), force_server_side_feed_update ? 1 : 0); + q.bindValue(QSL(":id"), account_id); + + if (q.exec()) { + return true; + } + + else { + qWarning("TT-RSS: Updating account failed: '%s'.", qPrintable(q.lastError().text())); + return false; + } +} + +bool DatabaseQueries::createTtRssAccount(QSqlDatabase db, int id_to_assign, const QString& username, + const QString& password, bool auth_protected, const QString& auth_username, + const QString& auth_password, const QString& url, + bool force_server_side_feed_update) { + QSqlQuery q(db); + q.prepare("INSERT INTO TtRssAccounts (id, username, password, auth_protected, auth_username, auth_password, url, force_update) " + "VALUES (:id, :username, :password, :auth_protected, :auth_username, :auth_password, :url, :force_update);"); + q.bindValue(QSL(":id"), id_to_assign); + q.bindValue(QSL(":username"), username); + q.bindValue(QSL(":password"), TextFactory::encrypt(password)); + q.bindValue(QSL(":auth_protected"), auth_protected ? 1 : 0); + q.bindValue(QSL(":auth_username"), auth_username); + q.bindValue(QSL(":auth_password"), TextFactory::encrypt(auth_password)); + q.bindValue(QSL(":url"), url); + q.bindValue(QSL(":force_update"), force_server_side_feed_update ? 1 : 0); + + if (q.exec()) { + return true; + } + + else { + qWarning("TT-RSS: Saving of new account failed: '%s'.", qPrintable(q.lastError().text())); + return false; + } +} + +Assignment DatabaseQueries::getTtRssCategories(QSqlDatabase db, int account_id, bool* ok) { + Assignment categories; + // Obtain data for categories from the database. + QSqlQuery query_categories(db); + query_categories.setForwardOnly(true); + query_categories.prepare(QSL("SELECT * FROM Categories WHERE account_id = :account_id;")); + query_categories.bindValue(QSL(":account_id"), account_id); + + if (!query_categories.exec()) { + qFatal("Query for obtaining categories failed. Error message: '%s'.", qPrintable(query_categories.lastError().text())); + + if (ok != nullptr) { + *ok = false; + } + } + + else { + if (ok != nullptr) { + *ok = true; + } + } + + while (query_categories.next()) { + AssignmentItem pair; + pair.first = query_categories.value(CAT_DB_PARENT_ID_INDEX).toInt(); + pair.second = new TtRssCategory(query_categories.record()); + categories << pair; + } + + return categories; +} + +Assignment DatabaseQueries::getTtRssFeeds(QSqlDatabase db, int account_id, bool* ok) { + Assignment feeds; + // All categories are now loaded. + QSqlQuery query_feeds(db); + query_feeds.setForwardOnly(true); + query_feeds.prepare(QSL("SELECT * FROM Feeds WHERE account_id = :account_id;")); + query_feeds.bindValue(QSL(":account_id"), account_id); + + if (!query_feeds.exec()) { + qFatal("Query for obtaining feeds failed. Error message: '%s'.", qPrintable(query_feeds.lastError().text())); + + if (ok != nullptr) { + *ok = false; + } + } + + else { + if (ok != nullptr) { + *ok = true; + } + } + + while (query_feeds.next()) { + AssignmentItem pair; + pair.first = query_feeds.value(FDS_DB_CATEGORY_INDEX).toInt(); + pair.second = new TtRssFeed(query_feeds.record()); + feeds << pair; + } + + return feeds; +} + +DatabaseQueries::DatabaseQueries() { +} diff --git a/src/miscellaneous/databasequeries.h b/src/miscellaneous/databasequeries.h index a4b0723d0..c8444c390 100755 --- a/src/miscellaneous/databasequeries.h +++ b/src/miscellaneous/databasequeries.h @@ -1,130 +1,130 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef DATABASEQUERIES_H -#define DATABASEQUERIES_H - -#include "services/abstract/rootitem.h" - -#include "services/abstract/serviceroot.h" -#include "services/standard/standardfeed.h" - -#include - - -class DatabaseQueries { - public: - // Mark read/unread/starred/delete messages. - static bool markMessagesReadUnread(QSqlDatabase db, const QStringList &ids, RootItem::ReadStatus read); - static bool markMessageImportant(QSqlDatabase db, int id, RootItem::Importance importance); - static bool markFeedsReadUnread(QSqlDatabase db, const QStringList &ids, int account_id, RootItem::ReadStatus read); - static bool markBinReadUnread(QSqlDatabase db, int account_id, RootItem::ReadStatus read); - static bool markAccountReadUnread(QSqlDatabase db, int account_id, RootItem::ReadStatus read); - static bool switchMessagesImportance(QSqlDatabase db, const QStringList &ids); - static bool permanentlyDeleteMessages(QSqlDatabase db, const QStringList &ids); - static bool deleteOrRestoreMessagesToFromBin(QSqlDatabase db, const QStringList &ids, bool deleted); - static bool restoreBin(QSqlDatabase db, int account_id); - - // Purge database. - static bool purgeImportantMessages(QSqlDatabase db); - static bool purgeReadMessages(QSqlDatabase db); - static bool purgeOldMessages(QSqlDatabase db, int older_than_days); - static bool purgeRecycleBin(QSqlDatabase db); - static bool purgeMessagesFromBin(QSqlDatabase db, bool clear_only_read, int account_id); - static bool purgeLeftoverMessages(QSqlDatabase db, int account_id); - - // Obtain counts of unread/all messages. - static QMap > getMessageCountsForCategory(QSqlDatabase db, int custom_id, int account_id, - bool including_total_counts, bool *ok = nullptr); - static QMap > getMessageCountsForAccount(QSqlDatabase db, int account_id, - bool including_total_counts, bool *ok = nullptr); - static int getMessageCountsForFeed(QSqlDatabase db, int feed_custom_id, int account_id, - bool including_total_counts, bool *ok = nullptr); - static int getMessageCountsForBin(QSqlDatabase db, int account_id, bool including_total_counts, bool *ok = nullptr); - - // Get messages (for newspaper view for example). - static QList getUndeletedMessagesForFeed(QSqlDatabase db, int feed_custom_id, int account_id, bool *ok = nullptr); - static QList getUndeletedMessagesForBin(QSqlDatabase db, int account_id, bool *ok = nullptr); - static QList getUndeletedMessagesForAccount(QSqlDatabase db, int account_id, bool *ok = nullptr); - - // Custom ID accumulators. - static QStringList customIdsOfMessagesFromAccount(QSqlDatabase db, int account_id, bool *ok = nullptr); - static QStringList customIdsOfMessagesFromBin(QSqlDatabase db, int account_id, bool *ok = nullptr); - static QStringList customIdsOfMessagesFromFeed(QSqlDatabase db, int feed_custom_id, int account_id, bool *ok = nullptr); - - // Common accounts methods. - static int updateMessages(QSqlDatabase db, const QList &messages, int feed_custom_id, - int account_id, const QString &url, bool *any_message_changed, bool *ok = nullptr); - static bool deleteAccount(QSqlDatabase db, int account_id); - static bool deleteAccountData(QSqlDatabase db, int account_id, bool delete_messages_too); - static bool cleanFeeds(QSqlDatabase db, const QStringList &ids, bool clean_read_only, int account_id); - - static bool storeAccountTree(QSqlDatabase db, RootItem *tree_root, int account_id); - static bool editBaseFeed(QSqlDatabase db, int feed_id, Feed::AutoUpdateType auto_update_type, - int auto_update_interval); - - // ownCloud account. - static QList getOwnCloudAccounts(QSqlDatabase db, bool *ok = nullptr); - static bool deleteOwnCloudAccount(QSqlDatabase db, int account_id); - static bool overwriteOwnCloudAccount(QSqlDatabase db, const QString &username, const QString &password, - const QString &url, bool force_server_side_feed_update, int account_id); - static bool createOwnCloudAccount(QSqlDatabase db, int id_to_assign, const QString &username, const QString &password, - const QString &url, bool force_server_side_feed_update); - static int createAccount(QSqlDatabase db, const QString &code, bool *ok = nullptr); - static Assignment getOwnCloudCategories(QSqlDatabase db, int account_id, bool *ok = nullptr); - static Assignment getOwnCloudFeeds(QSqlDatabase db, int account_id, bool *ok = nullptr); - - // Standard account. - static bool deleteFeed(QSqlDatabase db, int feed_custom_id, int account_id); - static bool deleteCategory(QSqlDatabase db, int id); - static int addCategory(QSqlDatabase db, int parent_id, int account_id, const QString &title, - const QString &description, QDateTime creation_date, const QIcon &icon, bool *ok = nullptr); - static bool editCategory(QSqlDatabase db, int parent_id, int category_id, - const QString &title, const QString &description, const QIcon &icon); - static int addFeed(QSqlDatabase db, int parent_id, int account_id, const QString &title, - const QString &description, QDateTime creation_date, const QIcon &icon, - const QString &encoding, const QString &url, bool is_protected, - const QString &username, const QString &password, - Feed::AutoUpdateType auto_update_type, - int auto_update_interval, StandardFeed::Type feed_format, bool *ok = nullptr); - static bool editFeed(QSqlDatabase db, int parent_id, int feed_id, const QString &title, - const QString &description, const QIcon &icon, - const QString &encoding, const QString &url, bool is_protected, - const QString &username, const QString &password, Feed::AutoUpdateType auto_update_type, - int auto_update_interval, StandardFeed::Type feed_format); - static QList getAccounts(QSqlDatabase db, bool *ok = nullptr); - static Assignment getCategories(QSqlDatabase db, int account_id, bool *ok = nullptr); - static Assignment getFeeds(QSqlDatabase db, int account_id, bool *ok = nullptr); - - // TT-RSS acccount. - static QList getTtRssAccounts(QSqlDatabase db, bool *ok = nullptr); - static bool deleteTtRssAccount(QSqlDatabase db, int account_id); - static bool overwriteTtRssAccount(QSqlDatabase db, const QString &username, const QString &password, - bool auth_protected, const QString &auth_username, const QString &auth_password, - const QString &url, bool force_server_side_feed_update, int account_id); - static bool createTtRssAccount(QSqlDatabase db, int id_to_assign, const QString &username, - const QString &password, bool auth_protected, const QString &auth_username, - const QString &auth_password, const QString &url, - bool force_server_side_feed_update); - static Assignment getTtRssCategories(QSqlDatabase db, int account_id, bool *ok = nullptr); - static Assignment getTtRssFeeds(QSqlDatabase db, int account_id, bool *ok = nullptr); - - private: - explicit DatabaseQueries(); -}; - -#endif // DATABASEQUERIES_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef DATABASEQUERIES_H +#define DATABASEQUERIES_H + +#include "services/abstract/rootitem.h" + +#include "services/abstract/serviceroot.h" +#include "services/standard/standardfeed.h" + +#include + + +class DatabaseQueries { + public: + // Mark read/unread/starred/delete messages. + static bool markMessagesReadUnread(QSqlDatabase db, const QStringList& ids, RootItem::ReadStatus read); + static bool markMessageImportant(QSqlDatabase db, int id, RootItem::Importance importance); + static bool markFeedsReadUnread(QSqlDatabase db, const QStringList& ids, int account_id, RootItem::ReadStatus read); + static bool markBinReadUnread(QSqlDatabase db, int account_id, RootItem::ReadStatus read); + static bool markAccountReadUnread(QSqlDatabase db, int account_id, RootItem::ReadStatus read); + static bool switchMessagesImportance(QSqlDatabase db, const QStringList& ids); + static bool permanentlyDeleteMessages(QSqlDatabase db, const QStringList& ids); + static bool deleteOrRestoreMessagesToFromBin(QSqlDatabase db, const QStringList& ids, bool deleted); + static bool restoreBin(QSqlDatabase db, int account_id); + + // Purge database. + static bool purgeImportantMessages(QSqlDatabase db); + static bool purgeReadMessages(QSqlDatabase db); + static bool purgeOldMessages(QSqlDatabase db, int older_than_days); + static bool purgeRecycleBin(QSqlDatabase db); + static bool purgeMessagesFromBin(QSqlDatabase db, bool clear_only_read, int account_id); + static bool purgeLeftoverMessages(QSqlDatabase db, int account_id); + + // Obtain counts of unread/all messages. + static QMap> getMessageCountsForCategory(QSqlDatabase db, int custom_id, int account_id, + bool including_total_counts, bool* ok = nullptr); + static QMap> getMessageCountsForAccount(QSqlDatabase db, int account_id, + bool including_total_counts, bool* ok = nullptr); + static int getMessageCountsForFeed(QSqlDatabase db, int feed_custom_id, int account_id, + bool including_total_counts, bool* ok = nullptr); + static int getMessageCountsForBin(QSqlDatabase db, int account_id, bool including_total_counts, bool* ok = nullptr); + + // Get messages (for newspaper view for example). + static QList getUndeletedMessagesForFeed(QSqlDatabase db, int feed_custom_id, int account_id, bool* ok = nullptr); + static QList getUndeletedMessagesForBin(QSqlDatabase db, int account_id, bool* ok = nullptr); + static QList getUndeletedMessagesForAccount(QSqlDatabase db, int account_id, bool* ok = nullptr); + + // Custom ID accumulators. + static QStringList customIdsOfMessagesFromAccount(QSqlDatabase db, int account_id, bool* ok = nullptr); + static QStringList customIdsOfMessagesFromBin(QSqlDatabase db, int account_id, bool* ok = nullptr); + static QStringList customIdsOfMessagesFromFeed(QSqlDatabase db, int feed_custom_id, int account_id, bool* ok = nullptr); + + // Common accounts methods. + static int updateMessages(QSqlDatabase db, const QList& messages, int feed_custom_id, + int account_id, const QString& url, bool* any_message_changed, bool* ok = nullptr); + static bool deleteAccount(QSqlDatabase db, int account_id); + static bool deleteAccountData(QSqlDatabase db, int account_id, bool delete_messages_too); + static bool cleanFeeds(QSqlDatabase db, const QStringList& ids, bool clean_read_only, int account_id); + + static bool storeAccountTree(QSqlDatabase db, RootItem* tree_root, int account_id); + static bool editBaseFeed(QSqlDatabase db, int feed_id, Feed::AutoUpdateType auto_update_type, + int auto_update_interval); + + // ownCloud account. + static QList getOwnCloudAccounts(QSqlDatabase db, bool* ok = nullptr); + static bool deleteOwnCloudAccount(QSqlDatabase db, int account_id); + static bool overwriteOwnCloudAccount(QSqlDatabase db, const QString& username, const QString& password, + const QString& url, bool force_server_side_feed_update, int account_id); + static bool createOwnCloudAccount(QSqlDatabase db, int id_to_assign, const QString& username, const QString& password, + const QString& url, bool force_server_side_feed_update); + static int createAccount(QSqlDatabase db, const QString& code, bool* ok = nullptr); + static Assignment getOwnCloudCategories(QSqlDatabase db, int account_id, bool* ok = nullptr); + static Assignment getOwnCloudFeeds(QSqlDatabase db, int account_id, bool* ok = nullptr); + + // Standard account. + static bool deleteFeed(QSqlDatabase db, int feed_custom_id, int account_id); + static bool deleteCategory(QSqlDatabase db, int id); + static int addCategory(QSqlDatabase db, int parent_id, int account_id, const QString& title, + const QString& description, QDateTime creation_date, const QIcon& icon, bool* ok = nullptr); + static bool editCategory(QSqlDatabase db, int parent_id, int category_id, + const QString& title, const QString& description, const QIcon& icon); + static int addFeed(QSqlDatabase db, int parent_id, int account_id, const QString& title, + const QString& description, QDateTime creation_date, const QIcon& icon, + const QString& encoding, const QString& url, bool is_protected, + const QString& username, const QString& password, + Feed::AutoUpdateType auto_update_type, + int auto_update_interval, StandardFeed::Type feed_format, bool* ok = nullptr); + static bool editFeed(QSqlDatabase db, int parent_id, int feed_id, const QString& title, + const QString& description, const QIcon& icon, + const QString& encoding, const QString& url, bool is_protected, + const QString& username, const QString& password, Feed::AutoUpdateType auto_update_type, + int auto_update_interval, StandardFeed::Type feed_format); + static QList getAccounts(QSqlDatabase db, bool* ok = nullptr); + static Assignment getCategories(QSqlDatabase db, int account_id, bool* ok = nullptr); + static Assignment getFeeds(QSqlDatabase db, int account_id, bool* ok = nullptr); + + // TT-RSS acccount. + static QList getTtRssAccounts(QSqlDatabase db, bool* ok = nullptr); + static bool deleteTtRssAccount(QSqlDatabase db, int account_id); + static bool overwriteTtRssAccount(QSqlDatabase db, const QString& username, const QString& password, + bool auth_protected, const QString& auth_username, const QString& auth_password, + const QString& url, bool force_server_side_feed_update, int account_id); + static bool createTtRssAccount(QSqlDatabase db, int id_to_assign, const QString& username, + const QString& password, bool auth_protected, const QString& auth_username, + const QString& auth_password, const QString& url, + bool force_server_side_feed_update); + static Assignment getTtRssCategories(QSqlDatabase db, int account_id, bool* ok = nullptr); + static Assignment getTtRssFeeds(QSqlDatabase db, int account_id, bool* ok = nullptr); + + private: + explicit DatabaseQueries(); +}; + +#endif // DATABASEQUERIES_H diff --git a/src/miscellaneous/debugging.cpp b/src/miscellaneous/debugging.cpp index e433edb1b..e7c879514 100755 --- a/src/miscellaneous/debugging.cpp +++ b/src/miscellaneous/debugging.cpp @@ -1,80 +1,79 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "miscellaneous/debugging.h" - -#include "miscellaneous/application.h" - -#include - -#include -#include -#include -#include - - -Debugging::Debugging() { -} - -void Debugging::performLog(const char *message, QtMsgType type, const char *file, const char *function, int line) { - const char *type_string = typeToString(type); - - std::time_t t = std::time(nullptr); - char mbstr[32]; - - std::strftime(mbstr, sizeof(mbstr), "%y/%d/%m %H:%M:%S", std::localtime(&t)); - - // Write to console. - if (file == 0 || function == 0 || line < 0) { - fprintf(stderr, "[%s] %s: %s (%s)\n", APP_LOW_NAME, type_string, message, mbstr); - } - else { - fprintf(stderr, "[%s] %s (%s)\n Type: %s\n File: %s (line %d)\n Function: %s\n\n", - APP_LOW_NAME, message, mbstr, type_string, file, line, function); - } - - if (type == QtFatalMsg) { - qApp->exit(EXIT_FAILURE); - } -} - -const char *Debugging::typeToString(QtMsgType type) { - switch (type) { - case QtDebugMsg: - return "DEBUG"; - - case QtWarningMsg: - return "WARNING"; - - case QtCriticalMsg: - return "CRITICAL"; - - case QtFatalMsg: - default: - return "FATAL (terminating application)"; - } -} - -void Debugging::debugHandler(QtMsgType type, const QMessageLogContext &placement, const QString &message) { -#ifndef QT_NO_DEBUG_OUTPUT - performLog(qPrintable(message), type, placement.file, placement.function, placement.line); -#else - Q_UNUSED(type) - Q_UNUSED(placement) - Q_UNUSED(message) -#endif -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "miscellaneous/debugging.h" + +#include "miscellaneous/application.h" + +#include + +#include +#include +#include +#include + + +Debugging::Debugging() { +} + +void Debugging::performLog(const char* message, QtMsgType type, const char* file, const char* function, int line) { + const char* type_string = typeToString(type); + std::time_t t = std::time(nullptr); + char mbstr[32]; + std::strftime(mbstr, sizeof(mbstr), "%y/%d/%m %H:%M:%S", std::localtime(&t)); + + // Write to console. + if (file == 0 || function == 0 || line < 0) { + fprintf(stderr, "[%s] %s: %s (%s)\n", APP_LOW_NAME, type_string, message, mbstr); + } + + else { + fprintf(stderr, "[%s] %s (%s)\n Type: %s\n File: %s (line %d)\n Function: %s\n\n", + APP_LOW_NAME, message, mbstr, type_string, file, line, function); + } + + if (type == QtFatalMsg) { + qApp->exit(EXIT_FAILURE); + } +} + +const char* Debugging::typeToString(QtMsgType type) { + switch (type) { + case QtDebugMsg: + return "DEBUG"; + + case QtWarningMsg: + return "WARNING"; + + case QtCriticalMsg: + return "CRITICAL"; + + case QtFatalMsg: + default: + return "FATAL (terminating application)"; + } +} + +void Debugging::debugHandler(QtMsgType type, const QMessageLogContext& placement, const QString& message) { +#ifndef QT_NO_DEBUG_OUTPUT + performLog(qPrintable(message), type, placement.file, placement.function, placement.line); +#else + Q_UNUSED(type) + Q_UNUSED(placement) + Q_UNUSED(message) +#endif +} diff --git a/src/miscellaneous/debugging.h b/src/miscellaneous/debugging.h index 6c91848ba..02f5e9a4f 100755 --- a/src/miscellaneous/debugging.h +++ b/src/miscellaneous/debugging.h @@ -1,37 +1,37 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef DEBUGGING_H -#define DEBUGGING_H - -#include - - -class Debugging { - public: - // Specifies format of output console messages. - // NOTE: QT_NO_DEBUG_OUTPUT - disables debug outputs completely!!! - static void debugHandler(QtMsgType type, const QMessageLogContext &placement, const QString &message); - static void performLog(const char *message, QtMsgType type, const char *file = 0, const char *function = 0, int line = -1); - static const char *typeToString(QtMsgType type); - - private: - // Constructor. - explicit Debugging(); -}; - -#endif // DEBUGGING_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef DEBUGGING_H +#define DEBUGGING_H + +#include + + +class Debugging { + public: + // Specifies format of output console messages. + // NOTE: QT_NO_DEBUG_OUTPUT - disables debug outputs completely!!! + static void debugHandler(QtMsgType type, const QMessageLogContext& placement, const QString& message); + static void performLog(const char* message, QtMsgType type, const char* file = 0, const char* function = 0, int line = -1); + static const char* typeToString(QtMsgType type); + + private: + // Constructor. + explicit Debugging(); +}; + +#endif // DEBUGGING_H diff --git a/src/miscellaneous/feedreader.cpp b/src/miscellaneous/feedreader.cpp index f53ce1a35..ab8ebcb31 100755 --- a/src/miscellaneous/feedreader.cpp +++ b/src/miscellaneous/feedreader.cpp @@ -36,290 +36,283 @@ #include -FeedReader::FeedReader(QObject *parent) - : QObject(parent), m_feedServices(QList()), - m_cacheSaveFutureWatcher(new QFutureWatcher(this)), m_autoUpdateTimer(new QTimer(this)), - m_feedDownloaderThread(nullptr), m_feedDownloader(nullptr), - m_dbCleanerThread(nullptr), m_dbCleaner(nullptr) { - m_feedsModel = new FeedsModel(this); - m_feedsProxyModel = new FeedsProxyModel(m_feedsModel, this); - m_messagesModel = new MessagesModel(this); - m_messagesProxyModel = new MessagesProxyModel(m_messagesModel, this); +FeedReader::FeedReader(QObject* parent) + : QObject(parent), m_feedServices(QList()), + m_cacheSaveFutureWatcher(new QFutureWatcher(this)), m_autoUpdateTimer(new QTimer(this)), + m_feedDownloaderThread(nullptr), m_feedDownloader(nullptr), + m_dbCleanerThread(nullptr), m_dbCleaner(nullptr) { + m_feedsModel = new FeedsModel(this); + m_feedsProxyModel = new FeedsProxyModel(m_feedsModel, this); + m_messagesModel = new MessagesModel(this); + m_messagesProxyModel = new MessagesProxyModel(m_messagesModel, this); + connect(m_cacheSaveFutureWatcher, &QFutureWatcher::finished, this, &FeedReader::asyncCacheSaveFinished); + connect(m_autoUpdateTimer, &QTimer::timeout, this, &FeedReader::executeNextAutoUpdate); + updateAutoUpdateStatus(); + asyncCacheSaveFinished(); - connect(m_cacheSaveFutureWatcher, &QFutureWatcher::finished, this, &FeedReader::asyncCacheSaveFinished); - connect(m_autoUpdateTimer, &QTimer::timeout, this, &FeedReader::executeNextAutoUpdate); - updateAutoUpdateStatus(); - asyncCacheSaveFinished(); - - if (qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateOnStartup)).toBool()) { - qDebug("Requesting update for all feeds on application startup."); - QTimer::singleShot(STARTUP_UPDATE_DELAY, this, SLOT(updateAllFeeds())); - } + if (qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::FeedsUpdateOnStartup)).toBool()) { + qDebug("Requesting update for all feeds on application startup."); + QTimer::singleShot(STARTUP_UPDATE_DELAY, this, SLOT(updateAllFeeds())); + } } FeedReader::~FeedReader() { - qDebug("Destroying FeedReader instance."); - qDeleteAll(m_feedServices); + qDebug("Destroying FeedReader instance."); + qDeleteAll(m_feedServices); } QList FeedReader::feedServices() { - if (m_feedServices.isEmpty()) { - // NOTE: All installed services create their entry points here. - m_feedServices.append(new StandardServiceEntryPoint()); - m_feedServices.append(new TtRssServiceEntryPoint()); - m_feedServices.append(new OwnCloudServiceEntryPoint()); - } + if (m_feedServices.isEmpty()) { + // NOTE: All installed services create their entry points here. + m_feedServices.append(new StandardServiceEntryPoint()); + m_feedServices.append(new TtRssServiceEntryPoint()); + m_feedServices.append(new OwnCloudServiceEntryPoint()); + } - return m_feedServices; + return m_feedServices; } -void FeedReader::updateFeeds(const QList &feeds) { - if (!qApp->feedUpdateLock()->tryLock()) { - qApp->showGuiMessage(tr("Cannot update all items"), - tr("You cannot update all items because another critical operation is ongoing."), - QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); - return; - } +void FeedReader::updateFeeds(const QList& feeds) { + if (!qApp->feedUpdateLock()->tryLock()) { + qApp->showGuiMessage(tr("Cannot update all items"), + tr("You cannot update all items because another critical operation is ongoing."), + QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); + return; + } - if (m_feedDownloader == nullptr) { - m_feedDownloader = new FeedDownloader(); - m_feedDownloaderThread = new QThread(); + if (m_feedDownloader == nullptr) { + m_feedDownloader = new FeedDownloader(); + m_feedDownloaderThread = new QThread(); + // Downloader setup. + qRegisterMetaType>("QList"); + m_feedDownloader->moveToThread(m_feedDownloaderThread); + connect(m_feedDownloaderThread, &QThread::finished, m_feedDownloaderThread, &QThread::deleteLater); + connect(m_feedDownloader, &FeedDownloader::updateFinished, this, &FeedReader::feedUpdatesFinished); + connect(m_feedDownloader, &FeedDownloader::updateProgress, this, &FeedReader::feedUpdatesProgress); + connect(m_feedDownloader, &FeedDownloader::updateStarted, this, &FeedReader::feedUpdatesStarted); + connect(m_feedDownloader, &FeedDownloader::updateFinished, qApp->feedUpdateLock(), &Mutex::unlock); + // Connections are made, start the feed downloader thread. + m_feedDownloaderThread->start(); + } - // Downloader setup. - qRegisterMetaType >("QList"); - m_feedDownloader->moveToThread(m_feedDownloaderThread); - - connect(m_feedDownloaderThread, &QThread::finished, m_feedDownloaderThread, &QThread::deleteLater); - connect(m_feedDownloader, &FeedDownloader::updateFinished, this, &FeedReader::feedUpdatesFinished); - connect(m_feedDownloader, &FeedDownloader::updateProgress, this, &FeedReader::feedUpdatesProgress); - connect(m_feedDownloader, &FeedDownloader::updateStarted, this, &FeedReader::feedUpdatesStarted); - connect(m_feedDownloader, &FeedDownloader::updateFinished, qApp->feedUpdateLock(), &Mutex::unlock); - - // Connections are made, start the feed downloader thread. - m_feedDownloaderThread->start(); - } - - QMetaObject::invokeMethod(m_feedDownloader, "updateFeeds", Q_ARG(QList, feeds)); + QMetaObject::invokeMethod(m_feedDownloader, "updateFeeds", Q_ARG(QList, feeds)); } void FeedReader::updateAutoUpdateStatus() { - // Restore global intervals. - // NOTE: Specific per-feed interval are left intact. - m_globalAutoUpdateInitialInterval = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateInterval)).toInt(); - m_globalAutoUpdateRemainingInterval = m_globalAutoUpdateInitialInterval; - m_globalAutoUpdateEnabled = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateEnabled)).toBool(); + // Restore global intervals. + // NOTE: Specific per-feed interval are left intact. + m_globalAutoUpdateInitialInterval = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateInterval)).toInt(); + m_globalAutoUpdateRemainingInterval = m_globalAutoUpdateInitialInterval; + m_globalAutoUpdateEnabled = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::AutoUpdateEnabled)).toBool(); - // Start global auto-update timer if it is not running yet. - // NOTE: The timer must run even if global auto-update - // is not enabled because user can still enable auto-update - // for individual feeds. - if (!m_autoUpdateTimer->isActive()) { - m_autoUpdateTimer->setInterval(AUTO_UPDATE_INTERVAL); - m_autoUpdateTimer->start(); - qDebug("Auto-update timer started with interval %d.", m_autoUpdateTimer->interval()); - } - else { - qDebug("Auto-update timer is already running."); - } + // Start global auto-update timer if it is not running yet. + // NOTE: The timer must run even if global auto-update + // is not enabled because user can still enable auto-update + // for individual feeds. + if (!m_autoUpdateTimer->isActive()) { + m_autoUpdateTimer->setInterval(AUTO_UPDATE_INTERVAL); + m_autoUpdateTimer->start(); + qDebug("Auto-update timer started with interval %d.", m_autoUpdateTimer->interval()); + } + + else { + qDebug("Auto-update timer is already running."); + } } bool FeedReader::autoUpdateEnabled() const { - return m_globalAutoUpdateEnabled; + return m_globalAutoUpdateEnabled; } int FeedReader::autoUpdateRemainingInterval() const { - return m_globalAutoUpdateRemainingInterval; + return m_globalAutoUpdateRemainingInterval; } int FeedReader::autoUpdateInitialInterval() const { - return m_globalAutoUpdateInitialInterval; + return m_globalAutoUpdateInitialInterval; } void FeedReader::updateAllFeeds() { - updateFeeds(m_feedsModel->rootItem()->getSubTreeFeeds()); + updateFeeds(m_feedsModel->rootItem()->getSubTreeFeeds()); } void FeedReader::stopRunningFeedUpdate() { - if (m_feedDownloader != nullptr) { - QMetaObject::invokeMethod(m_feedDownloader, "stopRunningUpdate"); - } + if (m_feedDownloader != nullptr) { + QMetaObject::invokeMethod(m_feedDownloader, "stopRunningUpdate"); + } } bool FeedReader::isFeedUpdateRunning() const { - return m_feedDownloader != nullptr && m_feedDownloader->isUpdateRunning(); + return m_feedDownloader != nullptr && m_feedDownloader->isUpdateRunning(); } -DatabaseCleaner *FeedReader::databaseCleaner() { - if (m_dbCleaner == nullptr) { - m_dbCleaner = new DatabaseCleaner(); - m_dbCleanerThread = new QThread(); +DatabaseCleaner* FeedReader::databaseCleaner() { + if (m_dbCleaner == nullptr) { + m_dbCleaner = new DatabaseCleaner(); + m_dbCleanerThread = new QThread(); + // Downloader setup. + qRegisterMetaType("CleanerOrders"); + m_dbCleaner->moveToThread(m_dbCleanerThread); + connect(m_dbCleanerThread, SIGNAL(finished()), m_dbCleanerThread, SLOT(deleteLater())); + // Connections are made, start the feed downloader thread. + m_dbCleanerThread->start(); + } - // Downloader setup. - qRegisterMetaType("CleanerOrders"); - m_dbCleaner->moveToThread(m_dbCleanerThread); - connect(m_dbCleanerThread, SIGNAL(finished()), m_dbCleanerThread, SLOT(deleteLater())); - - // Connections are made, start the feed downloader thread. - m_dbCleanerThread->start(); - } - - return m_dbCleaner; + return m_dbCleaner; } -FeedDownloader *FeedReader::feedDownloader() const { - return m_feedDownloader; +FeedDownloader* FeedReader::feedDownloader() const { + return m_feedDownloader; } -FeedsModel *FeedReader::feedsModel() const { - return m_feedsModel; +FeedsModel* FeedReader::feedsModel() const { + return m_feedsModel; } -MessagesModel *FeedReader::messagesModel() const { - return m_messagesModel; +MessagesModel* FeedReader::messagesModel() const { + return m_messagesModel; } void FeedReader::executeNextAutoUpdate() { - if (!qApp->feedUpdateLock()->tryLock()) { - qDebug("Delaying scheduled feed auto-updates for one minute due to another running update."); + if (!qApp->feedUpdateLock()->tryLock()) { + qDebug("Delaying scheduled feed auto-updates for one minute due to another running update."); + // Cannot update, quit. + return; + } - // Cannot update, quit. - return; - } + // If global auto-update is enabled and its interval counter reached zero, + // then we need to restore it. + if (m_globalAutoUpdateEnabled && --m_globalAutoUpdateRemainingInterval < 0) { + // We should start next auto-update interval. + m_globalAutoUpdateRemainingInterval = m_globalAutoUpdateInitialInterval; + } - // If global auto-update is enabled and its interval counter reached zero, - // then we need to restore it. - if (m_globalAutoUpdateEnabled && --m_globalAutoUpdateRemainingInterval < 0) { - // We should start next auto-update interval. - m_globalAutoUpdateRemainingInterval = m_globalAutoUpdateInitialInterval; - } + qDebug("Starting auto-update event, pass %d/%d.", m_globalAutoUpdateRemainingInterval, m_globalAutoUpdateInitialInterval); + // Pass needed interval data and lets the model decide which feeds + // should be updated in this pass. + QList feeds_for_update = m_feedsModel->feedsForScheduledUpdate(m_globalAutoUpdateEnabled && + m_globalAutoUpdateRemainingInterval == 0); + qApp->feedUpdateLock()->unlock(); - qDebug("Starting auto-update event, pass %d/%d.", m_globalAutoUpdateRemainingInterval, m_globalAutoUpdateInitialInterval); + if (!feeds_for_update.isEmpty()) { + // Request update for given feeds. + updateFeeds(feeds_for_update); - // Pass needed interval data and lets the model decide which feeds - // should be updated in this pass. - QList feeds_for_update = m_feedsModel->feedsForScheduledUpdate(m_globalAutoUpdateEnabled && - m_globalAutoUpdateRemainingInterval == 0); - - qApp->feedUpdateLock()->unlock(); - - if (!feeds_for_update.isEmpty()) { - // Request update for given feeds. - updateFeeds(feeds_for_update); - - // NOTE: OSD/bubble informing about performing - // of scheduled update can be shown now. - if (qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::EnableAutoUpdateNotification)).toBool()) { - qApp->showGuiMessage(tr("Starting auto-update of some feeds"), - tr("I will auto-update %n feed(s).", 0, feeds_for_update.size()), - QSystemTrayIcon::Information); - } - } + // NOTE: OSD/bubble informing about performing + // of scheduled update can be shown now. + if (qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::EnableAutoUpdateNotification)).toBool()) { + qApp->showGuiMessage(tr("Starting auto-update of some feeds"), + tr("I will auto-update %n feed(s).", 0, feeds_for_update.size()), + QSystemTrayIcon::Information); + } + } } void FeedReader::checkServicesForAsyncOperations() { - checkServicesForAsyncOperations(false); + checkServicesForAsyncOperations(false); } void FeedReader::checkServicesForAsyncOperations(bool wait_for_future) { - if (m_cacheSaveFutureWatcher->future().isRunning()) { - qDebug("Previous future is still running."); + if (m_cacheSaveFutureWatcher->future().isRunning()) { + qDebug("Previous future is still running."); - // If we want to wait for future synchronously, we want to make sure that - // we save all cached data (app exit). - if (wait_for_future) { - qWarning("Waiting for previously started saving of cached service data."); - m_cacheSaveFutureWatcher->future().waitForFinished(); - } - else { - qWarning("Some cached service data are being saved now, so aborting this saving cycle."); - // Some cache saving is now running. - return; - } - } + // If we want to wait for future synchronously, we want to make sure that + // we save all cached data (app exit). + if (wait_for_future) { + qWarning("Waiting for previously started saving of cached service data."); + m_cacheSaveFutureWatcher->future().waitForFinished(); + } - QFuture future = QtConcurrent::run([&] { - foreach (ServiceRoot *service, m_feedsModel->serviceRoots()) { - // Store any cached data. - service->saveAllCachedData(); - } - }); + else { + qWarning("Some cached service data are being saved now, so aborting this saving cycle."); + // Some cache saving is now running. + return; + } + } - if (wait_for_future) { - qDebug("Waiting for saving of cached service data to finish."); - future.waitForFinished(); - } - else { - m_cacheSaveFutureWatcher->setFuture(future); - } + QFuture future = QtConcurrent::run([&] { + foreach (ServiceRoot* service, m_feedsModel->serviceRoots()) { + // Store any cached data. + service->saveAllCachedData(); + } + }); + + if (wait_for_future) { + qDebug("Waiting for saving of cached service data to finish."); + future.waitForFinished(); + } + + else { + m_cacheSaveFutureWatcher->setFuture(future); + } } void FeedReader::asyncCacheSaveFinished() { - qDebug("I will start next check for cached service data in 30 seconds."); - - QTimer::singleShot(30000, [&] { - qDebug("Starting next check for cached service data in NOW."); - checkServicesForAsyncOperations(false); - }); + qDebug("I will start next check for cached service data in 30 seconds."); + QTimer::singleShot(30000, [&] { + qDebug("Starting next check for cached service data in NOW."); + checkServicesForAsyncOperations(false); + }); } void FeedReader::quit() { - if (m_autoUpdateTimer->isActive()) { - m_autoUpdateTimer->stop(); - } + if (m_autoUpdateTimer->isActive()) { + m_autoUpdateTimer->stop(); + } - checkServicesForAsyncOperations(true); + checkServicesForAsyncOperations(true); - // Close worker threads. - if (m_feedDownloaderThread != nullptr && m_feedDownloaderThread->isRunning()) { - m_feedDownloader->stopRunningUpdate(); + // Close worker threads. + if (m_feedDownloaderThread != nullptr && m_feedDownloaderThread->isRunning()) { + m_feedDownloader->stopRunningUpdate(); - if (m_feedDownloader->isUpdateRunning()) { - QEventLoop loop(this); - connect(m_feedDownloader, &FeedDownloader::updateFinished, &loop, &QEventLoop::quit); - loop.exec(); - } + if (m_feedDownloader->isUpdateRunning()) { + QEventLoop loop(this); + connect(m_feedDownloader, &FeedDownloader::updateFinished, &loop, &QEventLoop::quit); + loop.exec(); + } - qDebug("Quitting feed downloader thread."); - m_feedDownloaderThread->quit(); + qDebug("Quitting feed downloader thread."); + m_feedDownloaderThread->quit(); - if (!m_feedDownloaderThread->wait(CLOSE_LOCK_TIMEOUT)) { - qCritical("Feed downloader thread is running despite it was told to quit. Terminating it."); - m_feedDownloaderThread->terminate(); - } - } + if (!m_feedDownloaderThread->wait(CLOSE_LOCK_TIMEOUT)) { + qCritical("Feed downloader thread is running despite it was told to quit. Terminating it."); + m_feedDownloaderThread->terminate(); + } + } - if (m_dbCleanerThread != nullptr && m_dbCleanerThread->isRunning()) { - qDebug("Quitting database cleaner thread."); - m_dbCleanerThread->quit(); + if (m_dbCleanerThread != nullptr && m_dbCleanerThread->isRunning()) { + qDebug("Quitting database cleaner thread."); + m_dbCleanerThread->quit(); - if (!m_dbCleanerThread->wait(CLOSE_LOCK_TIMEOUT)) { - qCritical("Database cleaner thread is running despite it was told to quit. Terminating it."); - m_dbCleanerThread->terminate(); - } - } + if (!m_dbCleanerThread->wait(CLOSE_LOCK_TIMEOUT)) { + qCritical("Database cleaner thread is running despite it was told to quit. Terminating it."); + m_dbCleanerThread->terminate(); + } + } - // Close workers. - if (m_feedDownloader != nullptr) { - qDebug("Feed downloader exists. Deleting it from memory."); - m_feedDownloader->deleteLater(); - } + // Close workers. + if (m_feedDownloader != nullptr) { + qDebug("Feed downloader exists. Deleting it from memory."); + m_feedDownloader->deleteLater(); + } - if (m_dbCleaner != nullptr) { - qDebug("Database cleaner exists. Deleting it from memory."); - m_dbCleaner->deleteLater(); - } + if (m_dbCleaner != nullptr) { + qDebug("Database cleaner exists. Deleting it from memory."); + m_dbCleaner->deleteLater(); + } - if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::ClearReadOnExit)).toBool()) { - m_feedsModel->markItemCleared(m_feedsModel->rootItem(), true); - } + if (qApp->settings()->value(GROUP(Messages), SETTING(Messages::ClearReadOnExit)).toBool()) { + m_feedsModel->markItemCleared(m_feedsModel->rootItem(), true); + } - m_feedsModel->stopServiceAccounts(); + m_feedsModel->stopServiceAccounts(); } -MessagesProxyModel *FeedReader::messagesProxyModel() const { - return m_messagesProxyModel; +MessagesProxyModel* FeedReader::messagesProxyModel() const { + return m_messagesProxyModel; } -FeedsProxyModel *FeedReader::feedsProxyModel() const { - return m_feedsProxyModel; +FeedsProxyModel* FeedReader::feedsProxyModel() const { + return m_feedsProxyModel; } diff --git a/src/miscellaneous/feedreader.h b/src/miscellaneous/feedreader.h index 868b9a616..77ea6756c 100755 --- a/src/miscellaneous/feedreader.h +++ b/src/miscellaneous/feedreader.h @@ -36,80 +36,80 @@ class DatabaseCleaner; class QTimer; class FeedReader : public QObject { - Q_OBJECT + Q_OBJECT - public: - explicit FeedReader(QObject *parent = 0); - virtual ~FeedReader(); + public: + explicit FeedReader(QObject* parent = 0); + virtual ~FeedReader(); - // List of all installed "feed service plugins", including obligatory - // "standard" service entry point. - QList feedServices(); + // List of all installed "feed service plugins", including obligatory + // "standard" service entry point. + QList feedServices(); - // Access to DB cleaner. - DatabaseCleaner *databaseCleaner(); + // Access to DB cleaner. + DatabaseCleaner* databaseCleaner(); - FeedDownloader *feedDownloader() const; - FeedsModel *feedsModel() const; - MessagesModel *messagesModel() const; - FeedsProxyModel *feedsProxyModel() const; - MessagesProxyModel *messagesProxyModel() const; + FeedDownloader* feedDownloader() const; + FeedsModel* feedsModel() const; + MessagesModel* messagesModel() const; + FeedsProxyModel* feedsProxyModel() const; + MessagesProxyModel* messagesProxyModel() const; - // Schedules given feeds for update. - void updateFeeds(const QList &feeds); + // Schedules given feeds for update. + void updateFeeds(const QList& feeds); - // True if feed update is running right now. - bool isFeedUpdateRunning() const; + // True if feed update is running right now. + bool isFeedUpdateRunning() const; - // Resets global auto-update intervals according to settings - // and starts/stop the timer as needed. - void updateAutoUpdateStatus(); + // Resets global auto-update intervals according to settings + // and starts/stop the timer as needed. + void updateAutoUpdateStatus(); - bool autoUpdateEnabled() const; - int autoUpdateRemainingInterval() const; - int autoUpdateInitialInterval() const; + bool autoUpdateEnabled() const; + int autoUpdateRemainingInterval() const; + int autoUpdateInitialInterval() const; - public slots: - // Schedules all feeds from all accounts for update. - void updateAllFeeds(); - void stopRunningFeedUpdate(); - void quit(); + public slots: + // Schedules all feeds from all accounts for update. + void updateAllFeeds(); + void stopRunningFeedUpdate(); + void quit(); - private slots: - // Is executed when next auto-update round could be done. - void executeNextAutoUpdate(); - void checkServicesForAsyncOperations(); - void checkServicesForAsyncOperations(bool wait_for_future); - void asyncCacheSaveFinished(); + private slots: + // Is executed when next auto-update round could be done. + void executeNextAutoUpdate(); + void checkServicesForAsyncOperations(); + void checkServicesForAsyncOperations(bool wait_for_future); + void asyncCacheSaveFinished(); - signals: - void feedUpdatesStarted(); - void feedUpdatesFinished(FeedDownloadResults updated_feeds); - void feedUpdatesProgress(const Feed *feed, int current, int total); + signals: + void feedUpdatesStarted(); + void feedUpdatesFinished(FeedDownloadResults updated_feeds); + void feedUpdatesProgress(const Feed* feed, int current, int total); - private: - QList m_feedServices; + private: + QList m_feedServices; - FeedsModel *m_feedsModel; - FeedsProxyModel *m_feedsProxyModel; - MessagesModel *m_messagesModel; - MessagesProxyModel *m_messagesProxyModel; + FeedsModel* m_feedsModel; + FeedsProxyModel* m_feedsProxyModel; + MessagesModel* m_messagesModel; + MessagesProxyModel* m_messagesProxyModel; - QFutureWatcher *m_cacheSaveFutureWatcher; + QFutureWatcher* m_cacheSaveFutureWatcher; - // Auto-update stuff. - QTimer *m_autoUpdateTimer; - bool m_globalAutoUpdateEnabled; - int m_globalAutoUpdateInitialInterval; - int m_globalAutoUpdateRemainingInterval; + // Auto-update stuff. + QTimer* m_autoUpdateTimer; + bool m_globalAutoUpdateEnabled; + int m_globalAutoUpdateInitialInterval; + int m_globalAutoUpdateRemainingInterval; - ServiceOperator *m_serviceOperator; + ServiceOperator* m_serviceOperator; - QThread *m_feedDownloaderThread; - FeedDownloader *m_feedDownloader; + QThread* m_feedDownloaderThread; + FeedDownloader* m_feedDownloader; - QThread *m_dbCleanerThread; - DatabaseCleaner *m_dbCleaner; + QThread* m_dbCleanerThread; + DatabaseCleaner* m_dbCleaner; }; #endif // FEEDREADER_H diff --git a/src/miscellaneous/iconfactory.cpp b/src/miscellaneous/iconfactory.cpp index 492178684..3ba309b4f 100755 --- a/src/miscellaneous/iconfactory.cpp +++ b/src/miscellaneous/iconfactory.cpp @@ -22,130 +22,125 @@ #include -IconFactory::IconFactory(QObject *parent) : QObject(parent) { +IconFactory::IconFactory(QObject* parent) : QObject(parent) { } IconFactory::~IconFactory() { - qDebug("Destroying IconFactory instance."); + qDebug("Destroying IconFactory instance."); } QIcon IconFactory::fromByteArray(QByteArray array) { - array = QByteArray::fromBase64(array); - - QIcon icon; - QBuffer buffer(&array); - buffer.open(QIODevice::ReadOnly); - - QDataStream in(&buffer); - in.setVersion(QDataStream::Qt_4_7); - in >> icon; - - buffer.close(); - return icon; + array = QByteArray::fromBase64(array); + QIcon icon; + QBuffer buffer(&array); + buffer.open(QIODevice::ReadOnly); + QDataStream in(&buffer); + in.setVersion(QDataStream::Qt_4_7); + in >> icon; + buffer.close(); + return icon; } -QByteArray IconFactory::toByteArray(const QIcon &icon) { - QByteArray array; - QBuffer buffer(&array); - buffer.open(QIODevice::WriteOnly); - - QDataStream out(&buffer); - out.setVersion(QDataStream::Qt_4_7); - out << icon; - - buffer.close(); - return array.toBase64(); +QByteArray IconFactory::toByteArray(const QIcon& icon) { + QByteArray array; + QBuffer buffer(&array); + buffer.open(QIODevice::WriteOnly); + QDataStream out(&buffer); + out.setVersion(QDataStream::Qt_4_7); + out << icon; + buffer.close(); + return array.toBase64(); } -QPixmap IconFactory::pixmap(const QString &name) { - if (QIcon::themeName() == APP_NO_THEME) { - return QPixmap(); - } - else { - return QIcon::fromTheme(name).pixmap(64, 64); - } +QPixmap IconFactory::pixmap(const QString& name) { + if (QIcon::themeName() == APP_NO_THEME) { + return QPixmap(); + } + + else { + return QIcon::fromTheme(name).pixmap(64, 64); + } } -QIcon IconFactory::fromTheme(const QString &name) { - return QIcon::fromTheme(name); +QIcon IconFactory::fromTheme(const QString& name) { + return QIcon::fromTheme(name); } -QPixmap IconFactory::miscPixmap(const QString &name) { - return QPixmap(QString(APP_THEME_PATH) + QDir::separator() + "misc" + QDir::separator() + name + ".png"); +QPixmap IconFactory::miscPixmap(const QString& name) { + return QPixmap(QString(APP_THEME_PATH) + QDir::separator() + "misc" + QDir::separator() + name + ".png"); } -QIcon IconFactory::miscIcon(const QString &name) { - return QIcon(QString(APP_THEME_PATH) + QDir::separator() + "misc" + QDir::separator() + name + ".png"); +QIcon IconFactory::miscIcon(const QString& name) { + return QIcon(QString(APP_THEME_PATH) + QDir::separator() + "misc" + QDir::separator() + name + ".png"); } void IconFactory::setupSearchPaths() { - QIcon::setThemeSearchPaths(QIcon::themeSearchPaths() << APP_THEME_PATH); - qDebug("Available icon theme paths: %s.", - qPrintable(QIcon::themeSearchPaths() - .replaceInStrings(QRegExp(QSL("^|$")), QSL("\'")) - .replaceInStrings(QRegExp(QSL("/")), QDir::separator()).join(QSL(", ")))); + QIcon::setThemeSearchPaths(QIcon::themeSearchPaths() << APP_THEME_PATH); + qDebug("Available icon theme paths: %s.", + qPrintable(QIcon::themeSearchPaths() + .replaceInStrings(QRegExp(QSL("^|$")), QSL("\'")) + .replaceInStrings(QRegExp(QSL("/")), QDir::separator()).join(QSL(", ")))); } -void IconFactory::setCurrentIconTheme(const QString &theme_name) { - qApp->settings()->setValue(GROUP(GUI), GUI::IconTheme, theme_name); +void IconFactory::setCurrentIconTheme(const QString& theme_name) { + qApp->settings()->setValue(GROUP(GUI), GUI::IconTheme, theme_name); } void IconFactory::loadCurrentIconTheme() { - const QStringList installed_themes = installedIconThemes(); - const QString theme_name_from_settings = qApp->settings()->value(GROUP(GUI), SETTING(GUI::IconTheme)).toString(); + const QStringList installed_themes = installedIconThemes(); + const QString theme_name_from_settings = qApp->settings()->value(GROUP(GUI), SETTING(GUI::IconTheme)).toString(); - if (QIcon::themeName() == theme_name_from_settings) { - qDebug("Icon theme '%s' already loaded.", qPrintable(theme_name_from_settings)); - return; - } + if (QIcon::themeName() == theme_name_from_settings) { + qDebug("Icon theme '%s' already loaded.", qPrintable(theme_name_from_settings)); + return; + } - // Display list of installed themes. - qDebug("Installed icon themes are: %s.", - qPrintable(QStringList(installed_themes) - .replaceInStrings(QRegExp(QSL("^|$")), QSL("\'")) - .replaceInStrings(QRegExp(QSL("^\\'$")), QSL("\'\'")).join(QSL(", ")))); + // Display list of installed themes. + qDebug("Installed icon themes are: %s.", + qPrintable(QStringList(installed_themes) + .replaceInStrings(QRegExp(QSL("^|$")), QSL("\'")) + .replaceInStrings(QRegExp(QSL("^\\'$")), QSL("\'\'")).join(QSL(", ")))); - if (installed_themes.contains(theme_name_from_settings)) { - // Desired icon theme is installed and can be loaded. - qDebug("Loading icon theme '%s'.", qPrintable(theme_name_from_settings)); - QIcon::setThemeName(theme_name_from_settings); - } - else { - // Desired icon theme is not currently available. - // Install "default" icon theme instead. - qWarning("Icon theme '%s' cannot be loaded because it is not installed. No icon theme (or default icon theme) is loaded now.", - qPrintable(theme_name_from_settings)); - QIcon::setThemeName(APP_NO_THEME); - } + if (installed_themes.contains(theme_name_from_settings)) { + // Desired icon theme is installed and can be loaded. + qDebug("Loading icon theme '%s'.", qPrintable(theme_name_from_settings)); + QIcon::setThemeName(theme_name_from_settings); + } + + else { + // Desired icon theme is not currently available. + // Install "default" icon theme instead. + qWarning("Icon theme '%s' cannot be loaded because it is not installed. No icon theme (or default icon theme) is loaded now.", + qPrintable(theme_name_from_settings)); + QIcon::setThemeName(APP_NO_THEME); + } } QStringList IconFactory::installedIconThemes() const { - QStringList icon_theme_names; icon_theme_names << APP_NO_THEME; + QStringList icon_theme_names; + icon_theme_names << APP_NO_THEME; + // Iterate all directories with icon themes. + QStringList icon_themes_paths = QIcon::themeSearchPaths(); + QStringList filters_index; + filters_index.append("index.theme"); + icon_themes_paths.removeDuplicates(); - // Iterate all directories with icon themes. - QStringList icon_themes_paths = QIcon::themeSearchPaths(); - QStringList filters_index; + foreach (const QString& icon_path, icon_themes_paths) { + const QDir icon_dir(icon_path); - filters_index.append("index.theme"); - icon_themes_paths.removeDuplicates(); + // Iterate all icon themes in this directory. + foreach (const QFileInfo& icon_theme_path, icon_dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | + QDir::Readable | QDir::CaseSensitive | + QDir::NoSymLinks, + QDir::Time)) { + QDir icon_theme_dir = QDir(icon_theme_path.absoluteFilePath()); - foreach (const QString &icon_path, icon_themes_paths) { - const QDir icon_dir(icon_path); + if (icon_theme_dir.exists(filters_index.at(0))) { + icon_theme_names << icon_theme_dir.dirName(); + } + } + } - // Iterate all icon themes in this directory. - foreach (const QFileInfo &icon_theme_path, icon_dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | - QDir::Readable | QDir::CaseSensitive | - QDir::NoSymLinks, - QDir::Time)) { - QDir icon_theme_dir = QDir(icon_theme_path.absoluteFilePath()); - - if (icon_theme_dir.exists(filters_index.at(0))) { - icon_theme_names << icon_theme_dir.dirName(); - } - } - } - - icon_theme_names.removeDuplicates(); - - return icon_theme_names; + icon_theme_names.removeDuplicates(); + return icon_theme_names; } diff --git a/src/miscellaneous/iconfactory.h b/src/miscellaneous/iconfactory.h index 894a1b682..a8f07f2f0 100755 --- a/src/miscellaneous/iconfactory.h +++ b/src/miscellaneous/iconfactory.h @@ -30,47 +30,47 @@ class IconFactory : public QObject { - Q_OBJECT + Q_OBJECT - public: - // Constructor. - explicit IconFactory(QObject *parent = 0); + public: + // Constructor. + explicit IconFactory(QObject* parent = 0); - // Destructor. - virtual ~IconFactory(); + // Destructor. + virtual ~IconFactory(); - // Used to store/retrieve QIcons from/to Base64-encoded - // byte array. - static QIcon fromByteArray(QByteArray array); - static QByteArray toByteArray(const QIcon &icon); + // Used to store/retrieve QIcons from/to Base64-encoded + // byte array. + static QIcon fromByteArray(QByteArray array); + static QByteArray toByteArray(const QIcon& icon); - QPixmap pixmap(const QString &name); + QPixmap pixmap(const QString& name); - // Returns icon from active theme or invalid icon if - // "no icon theme" is set. - QIcon fromTheme(const QString &name); + // Returns icon from active theme or invalid icon if + // "no icon theme" is set. + QIcon fromTheme(const QString& name); - QPixmap miscPixmap(const QString &name); - QIcon miscIcon(const QString &name); + QPixmap miscPixmap(const QString& name); + QIcon miscIcon(const QString& name); - // Adds custom application path to be search for icons. - void setupSearchPaths(); + // Adds custom application path to be search for icons. + void setupSearchPaths(); - // Returns list of installed themes, including "default" theme. - QStringList installedIconThemes() const; + // Returns list of installed themes, including "default" theme. + QStringList installedIconThemes() const; - // Loads name of selected icon theme (from settings) for the application and - // activates it. If that particular theme is not installed, then - // "default" theme is loaded. - void loadCurrentIconTheme(); + // Loads name of selected icon theme (from settings) for the application and + // activates it. If that particular theme is not installed, then + // "default" theme is loaded. + void loadCurrentIconTheme(); - // Returns name of currently activated theme for the application. - inline QString currentIconTheme() const { - return QIcon::themeName(); - } + // Returns name of currently activated theme for the application. + inline QString currentIconTheme() const { + return QIcon::themeName(); + } - // Sets icon theme with given name as the active one and loads it. - void setCurrentIconTheme(const QString &theme_name); + // Sets icon theme with given name as the active one and loads it. + void setCurrentIconTheme(const QString& theme_name); }; #endif // ICONFACTORY_H diff --git a/src/miscellaneous/iofactory.cpp b/src/miscellaneous/iofactory.cpp index 4344de32c..247486ebe 100755 --- a/src/miscellaneous/iofactory.cpp +++ b/src/miscellaneous/iofactory.cpp @@ -1,129 +1,128 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "miscellaneous/iofactory.h" - -#include "definitions/definitions.h" -#include "exceptions/ioexception.h" - -#include -#include -#include -#include -#include -#include - - -IOFactory::IOFactory() { -} - -bool IOFactory::isFolderWritable(const QString &folder) { - QString real_file = folder; - - if (!real_file.endsWith(QDir::separator())) { - real_file += QDir::separator(); - } - - real_file += "test-permissions-file"; - - return QTemporaryFile(real_file).open(); -} - -QString IOFactory::getSystemFolder(QStandardPaths::StandardLocation location) { - return QStandardPaths::writableLocation(location); -} - -QString IOFactory::ensureUniqueFilename(const QString &name, const QString &append_format) { - if (!QFile::exists(name)) { - return name; - } - - QString tmp_filename = name; - int i = 1; - - while (QFile::exists(tmp_filename)) { - tmp_filename = name; - const int index = tmp_filename.lastIndexOf(QL1C('.')); - const QString append_string = append_format.arg(i++); - - if (index < 0) { - tmp_filename.append(append_string); - } - else { - tmp_filename = tmp_filename.left(index) + append_string + tmp_filename.mid(index); - } - } - - return tmp_filename; -} - -QString IOFactory::filterBadCharsFromFilename(const QString &name) { - QString value = name; - - value.replace(QL1C('/'), QL1C('-')); - value.remove(QL1C('\\')); - value.remove(QL1C(':')); - value.remove(QL1C('*')); - value.remove(QL1C('?')); - value.remove(QL1C('"')); - value.remove(QL1C('<')); - value.remove(QL1C('>')); - value.remove(QL1C('|')); - - return value; -} - -QByteArray IOFactory::readTextFile(const QString &file_path) { - QFile input_file(file_path); - QByteArray input_data; - - if (input_file.open(QIODevice::Text | QIODevice::Unbuffered | QIODevice::ReadOnly)) { - input_data = input_file.readAll(); - input_file.close(); - return input_data; - } - else { - throw IOException(tr("Cannot open file '%1' for reading.").arg(QDir::toNativeSeparators(file_path))); - } -} - -void IOFactory::writeTextFile(const QString &file_path, const QByteArray &data, const QString &encoding) { - Q_UNUSED(encoding) - - QFile input_file(file_path); - QTextStream stream(&input_file); - - if (input_file.open(QIODevice::Text | QIODevice::WriteOnly)) { - stream << data; - stream.flush(); - input_file.flush(); - input_file.close(); - } - else { - throw IOException(tr("Cannot open file '%1' for writting.").arg(QDir::toNativeSeparators(file_path))); - } -} - -bool IOFactory::copyFile(const QString &source, const QString &destination) { - if (QFile::exists(destination)) { - if (!QFile::remove(destination)) { - return false; - } - } - - return QFile::copy(source, destination); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "miscellaneous/iofactory.h" + +#include "definitions/definitions.h" +#include "exceptions/ioexception.h" + +#include +#include +#include +#include +#include +#include + + +IOFactory::IOFactory() { +} + +bool IOFactory::isFolderWritable(const QString& folder) { + QString real_file = folder; + + if (!real_file.endsWith(QDir::separator())) { + real_file += QDir::separator(); + } + + real_file += "test-permissions-file"; + return QTemporaryFile(real_file).open(); +} + +QString IOFactory::getSystemFolder(QStandardPaths::StandardLocation location) { + return QStandardPaths::writableLocation(location); +} + +QString IOFactory::ensureUniqueFilename(const QString& name, const QString& append_format) { + if (!QFile::exists(name)) { + return name; + } + + QString tmp_filename = name; + int i = 1; + + while (QFile::exists(tmp_filename)) { + tmp_filename = name; + const int index = tmp_filename.lastIndexOf(QL1C('.')); + const QString append_string = append_format.arg(i++); + + if (index < 0) { + tmp_filename.append(append_string); + } + + else { + tmp_filename = tmp_filename.left(index) + append_string + tmp_filename.mid(index); + } + } + + return tmp_filename; +} + +QString IOFactory::filterBadCharsFromFilename(const QString& name) { + QString value = name; + value.replace(QL1C('/'), QL1C('-')); + value.remove(QL1C('\\')); + value.remove(QL1C(':')); + value.remove(QL1C('*')); + value.remove(QL1C('?')); + value.remove(QL1C('"')); + value.remove(QL1C('<')); + value.remove(QL1C('>')); + value.remove(QL1C('|')); + return value; +} + +QByteArray IOFactory::readTextFile(const QString& file_path) { + QFile input_file(file_path); + QByteArray input_data; + + if (input_file.open(QIODevice::Text | QIODevice::Unbuffered | QIODevice::ReadOnly)) { + input_data = input_file.readAll(); + input_file.close(); + return input_data; + } + + else { + throw IOException(tr("Cannot open file '%1' for reading.").arg(QDir::toNativeSeparators(file_path))); + } +} + +void IOFactory::writeTextFile(const QString& file_path, const QByteArray& data, const QString& encoding) { + Q_UNUSED(encoding) + QFile input_file(file_path); + QTextStream stream(&input_file); + + if (input_file.open(QIODevice::Text | QIODevice::WriteOnly)) { + stream << data; + stream.flush(); + input_file.flush(); + input_file.close(); + } + + else { + throw IOException(tr("Cannot open file '%1' for writting.").arg(QDir::toNativeSeparators(file_path))); + } +} + +bool IOFactory::copyFile(const QString& source, const QString& destination) { + if (QFile::exists(destination)) { + if (!QFile::remove(destination)) { + return false; + } + } + + return QFile::copy(source, destination); +} diff --git a/src/miscellaneous/iofactory.h b/src/miscellaneous/iofactory.h index 8b32ed2ec..8d24bce47 100755 --- a/src/miscellaneous/iofactory.h +++ b/src/miscellaneous/iofactory.h @@ -1,57 +1,57 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef IOFACTORY_H -#define IOFACTORY_H - -#include - -#include "definitions/definitions.h" - -#include - - -class IOFactory { - Q_DECLARE_TR_FUNCTIONS(IOFactory) - - private: - IOFactory(); - - public: - static bool isFolderWritable(const QString &folder); - - // Returns system-wide folder according to type. - static QString getSystemFolder(QStandardPaths::StandardLocation location); - - // Checks given file if it exists and if it does, then generates non-existing new file - // according to format. - static QString ensureUniqueFilename(const QString &name, const QString &append_format = QSL("(%1)")); - - // Filters out shit characters from filename. - static QString filterBadCharsFromFilename(const QString &name); - - // Returns contents of a file. - // Throws exception when no such file exists. - static QByteArray readTextFile(const QString &file_path); - - static void writeTextFile(const QString &file_path, const QByteArray &data, const QString &encoding = QSL("UTF-8")); - - // Copies file, overwrites destination. - static bool copyFile(const QString &source, const QString &destination); -}; - -#endif // IOFACTORY_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef IOFACTORY_H +#define IOFACTORY_H + +#include + +#include "definitions/definitions.h" + +#include + + +class IOFactory { + Q_DECLARE_TR_FUNCTIONS(IOFactory) + + private: + IOFactory(); + + public: + static bool isFolderWritable(const QString& folder); + + // Returns system-wide folder according to type. + static QString getSystemFolder(QStandardPaths::StandardLocation location); + + // Checks given file if it exists and if it does, then generates non-existing new file + // according to format. + static QString ensureUniqueFilename(const QString& name, const QString& append_format = QSL("(%1)")); + + // Filters out shit characters from filename. + static QString filterBadCharsFromFilename(const QString& name); + + // Returns contents of a file. + // Throws exception when no such file exists. + static QByteArray readTextFile(const QString& file_path); + + static void writeTextFile(const QString& file_path, const QByteArray& data, const QString& encoding = QSL("UTF-8")); + + // Copies file, overwrites destination. + static bool copyFile(const QString& source, const QString& destination); +}; + +#endif // IOFACTORY_H diff --git a/src/miscellaneous/localization.cpp b/src/miscellaneous/localization.cpp index cfce9af50..09908de6b 100755 --- a/src/miscellaneous/localization.cpp +++ b/src/miscellaneous/localization.cpp @@ -1,95 +1,94 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "miscellaneous/localization.h" - -#include "miscellaneous/application.h" - -#include -#include -#include -#include - - -Localization::Localization(QObject *parent) - : QObject(parent) { -} - -Localization::~Localization() { -} - -QString Localization::desiredLanguage() const { - return qApp->settings()->value(GROUP(General), SETTING(General::Language)).toString(); -} - -void Localization::loadActiveLanguage() { - QTranslator *qt_translator = new QTranslator(qApp); - QTranslator *app_translator = new QTranslator(qApp); - QString desired_localization = desiredLanguage(); - - qDebug("Starting to load active localization. Desired localization is '%s'.", qPrintable(desired_localization)); - - if (app_translator->load(QLocale(desired_localization), "rssguard", QSL("_"), APP_LANG_PATH)) { - const QString real_loaded_locale = app_translator->translate("QObject", "LANG_ABBREV"); - - Application::installTranslator(app_translator); - qDebug("Application localization '%s' loaded successfully, specifically sublocalization '%s' was loaded.", - qPrintable(desired_localization), - qPrintable(real_loaded_locale)); - desired_localization = real_loaded_locale; - } - else { - qWarning("Application localization '%s' was not loaded. Loading '%s' instead.", - qPrintable(desired_localization), - DEFAULT_LOCALE); - desired_localization = DEFAULT_LOCALE; - } - - if (qt_translator->load(QLocale(desired_localization), "qtbase", QSL("_"), APP_LANG_PATH)) { - Application::installTranslator(qt_translator); - qDebug("Qt localization '%s' loaded successfully.", qPrintable(desired_localization)); - } - else { - qWarning("Qt localization '%s' was not loaded.", qPrintable(desired_localization)); - } - - m_loadedLanguage = desired_localization; - m_loadedLocale = QLocale(desired_localization); - QLocale::setDefault(m_loadedLocale); -} - -QList Localization::installedLanguages() const { - QList languages; - const QDir file_dir(APP_LANG_PATH); - QTranslator translator; - - // Iterate all found language files. - foreach (const QFileInfo &file, file_dir.entryInfoList(QStringList() << "rssguard_*.qm", QDir::Files, QDir::Name)) { - if (translator.load(file.absoluteFilePath())) { - Language new_language; - new_language.m_code = translator.translate("QObject", "LANG_ABBREV"); - new_language.m_author = translator.translate("QObject", "LANG_AUTHOR"); - new_language.m_email = translator.translate("QObject", "LANG_EMAIL"); - new_language.m_name = QLocale(new_language.m_code).nativeLanguageName(); - - languages << new_language; - } - } - - return languages; -} - +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "miscellaneous/localization.h" + +#include "miscellaneous/application.h" + +#include +#include +#include +#include + + +Localization::Localization(QObject* parent) + : QObject(parent) { +} + +Localization::~Localization() { +} + +QString Localization::desiredLanguage() const { + return qApp->settings()->value(GROUP(General), SETTING(General::Language)).toString(); +} + +void Localization::loadActiveLanguage() { + QTranslator* qt_translator = new QTranslator(qApp); + QTranslator* app_translator = new QTranslator(qApp); + QString desired_localization = desiredLanguage(); + qDebug("Starting to load active localization. Desired localization is '%s'.", qPrintable(desired_localization)); + + if (app_translator->load(QLocale(desired_localization), "rssguard", QSL("_"), APP_LANG_PATH)) { + const QString real_loaded_locale = app_translator->translate("QObject", "LANG_ABBREV"); + Application::installTranslator(app_translator); + qDebug("Application localization '%s' loaded successfully, specifically sublocalization '%s' was loaded.", + qPrintable(desired_localization), + qPrintable(real_loaded_locale)); + desired_localization = real_loaded_locale; + } + + else { + qWarning("Application localization '%s' was not loaded. Loading '%s' instead.", + qPrintable(desired_localization), + DEFAULT_LOCALE); + desired_localization = DEFAULT_LOCALE; + } + + if (qt_translator->load(QLocale(desired_localization), "qtbase", QSL("_"), APP_LANG_PATH)) { + Application::installTranslator(qt_translator); + qDebug("Qt localization '%s' loaded successfully.", qPrintable(desired_localization)); + } + + else { + qWarning("Qt localization '%s' was not loaded.", qPrintable(desired_localization)); + } + + m_loadedLanguage = desired_localization; + m_loadedLocale = QLocale(desired_localization); + QLocale::setDefault(m_loadedLocale); +} + +QList Localization::installedLanguages() const { + QList languages; + const QDir file_dir(APP_LANG_PATH); + QTranslator translator; + + // Iterate all found language files. + foreach (const QFileInfo& file, file_dir.entryInfoList(QStringList() << "rssguard_*.qm", QDir::Files, QDir::Name)) { + if (translator.load(file.absoluteFilePath())) { + Language new_language; + new_language.m_code = translator.translate("QObject", "LANG_ABBREV"); + new_language.m_author = translator.translate("QObject", "LANG_AUTHOR"); + new_language.m_email = translator.translate("QObject", "LANG_EMAIL"); + new_language.m_name = QLocale(new_language.m_code).nativeLanguageName(); + languages << new_language; + } + } + + return languages; +} + diff --git a/src/miscellaneous/localization.h b/src/miscellaneous/localization.h index 8fed5a0bb..8160d4db7 100755 --- a/src/miscellaneous/localization.h +++ b/src/miscellaneous/localization.h @@ -1,71 +1,71 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef LOCALIZATION_H -#define LOCALIZATION_H - -#include - -#include -#include - - -struct Language { - QString m_name; - QString m_code; - QString m_author; - QString m_email; -}; - -class Localization : public QObject { - Q_OBJECT - - public: - // Constructor. - explicit Localization(QObject *parent = 0); - - // Destructor. - virtual ~Localization(); - - // Returns code of language that should - // be loaded according to settings. - QString desiredLanguage() const; - - // Loads currently active language. - void loadActiveLanguage(); - - // Returns list of installed application localizations. - // This list is used ie. in settings dialog. - QList installedLanguages() const; - - // Returns empty string or loaded language - // name if it is really loaded. - inline QString loadedLanguage() const { - return m_loadedLanguage; - } - - inline QLocale loadedLocale() const { - return m_loadedLocale; - } - - private: - // Code of loaded language. - QString m_loadedLanguage; - QLocale m_loadedLocale; -}; - -#endif // LOCALIZATION_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef LOCALIZATION_H +#define LOCALIZATION_H + +#include + +#include +#include + + +struct Language { + QString m_name; + QString m_code; + QString m_author; + QString m_email; +}; + +class Localization : public QObject { + Q_OBJECT + + public: + // Constructor. + explicit Localization(QObject* parent = 0); + + // Destructor. + virtual ~Localization(); + + // Returns code of language that should + // be loaded according to settings. + QString desiredLanguage() const; + + // Loads currently active language. + void loadActiveLanguage(); + + // Returns list of installed application localizations. + // This list is used ie. in settings dialog. + QList installedLanguages() const; + + // Returns empty string or loaded language + // name if it is really loaded. + inline QString loadedLanguage() const { + return m_loadedLanguage; + } + + inline QLocale loadedLocale() const { + return m_loadedLocale; + } + + private: + // Code of loaded language. + QString m_loadedLanguage; + QLocale m_loadedLocale; +}; + +#endif // LOCALIZATION_H diff --git a/src/miscellaneous/mutex.cpp b/src/miscellaneous/mutex.cpp index e0a0fddd3..a66cc950f 100755 --- a/src/miscellaneous/mutex.cpp +++ b/src/miscellaneous/mutex.cpp @@ -1,74 +1,74 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "miscellaneous/mutex.h" - - -Mutex::Mutex(QMutex::RecursionMode mode, QObject *parent) : QObject(parent), m_mutex(new QMutex(mode)), m_isLocked(false) { -} - -Mutex::~Mutex() { - qDebug("Destroying Mutex instance."); -} - -void Mutex::lock() { - m_mutex->lock(); - setLocked(); -} - -bool Mutex::tryLock() { - bool result; - - if ((result = m_mutex->tryLock())) { - setLocked(); - } - - return result; -} - -bool Mutex::tryLock(int timeout) { - bool result; - - if ((result = m_mutex->tryLock(timeout))) { - setLocked(); - } - - return result; -} - -void Mutex::unlock() { - m_mutex->unlock(); - setUnlocked(); -} - -void Mutex::setLocked() { - m_isLocked = true; - emit locked(); -} - -void Mutex::setUnlocked() { - m_isLocked = false; - emit unlocked(); -} - -bool Mutex::isLocked() const { - return m_isLocked; -} - -Mutex::operator QMutex*() const { - return m_mutex.data(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "miscellaneous/mutex.h" + + +Mutex::Mutex(QMutex::RecursionMode mode, QObject* parent) : QObject(parent), m_mutex(new QMutex(mode)), m_isLocked(false) { +} + +Mutex::~Mutex() { + qDebug("Destroying Mutex instance."); +} + +void Mutex::lock() { + m_mutex->lock(); + setLocked(); +} + +bool Mutex::tryLock() { + bool result; + + if ((result = m_mutex->tryLock())) { + setLocked(); + } + + return result; +} + +bool Mutex::tryLock(int timeout) { + bool result; + + if ((result = m_mutex->tryLock(timeout))) { + setLocked(); + } + + return result; +} + +void Mutex::unlock() { + m_mutex->unlock(); + setUnlocked(); +} + +void Mutex::setLocked() { + m_isLocked = true; + emit locked(); +} + +void Mutex::setUnlocked() { + m_isLocked = false; + emit unlocked(); +} + +bool Mutex::isLocked() const { + return m_isLocked; +} + +Mutex::operator QMutex* () const { + return m_mutex.data(); +} diff --git a/src/miscellaneous/mutex.h b/src/miscellaneous/mutex.h index 128746030..33a8a57a3 100755 --- a/src/miscellaneous/mutex.h +++ b/src/miscellaneous/mutex.h @@ -1,60 +1,60 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef MUTEX_H -#define MUTEX_H - -#include -#include - - -class Mutex : public QObject { - Q_OBJECT - - public: - // Constructors. - explicit Mutex(QMutex::RecursionMode mode = QMutex::NonRecursive, QObject *parent = 0); - virtual ~Mutex(); - - // Main methods. - bool tryLock(); - bool tryLock(int timeout); - - // Identifies if mutes is locked or not. - bool isLocked() const; - - operator QMutex*() const; - - public slots: - void lock(); - void unlock(); - - protected: - // These methods set proper value for m_isLocked and emit signals. - void setLocked(); - void setUnlocked(); - - signals: - void locked(); - void unlocked(); - - private: - QScopedPointer m_mutex; - bool m_isLocked; -}; - -#endif // MUTEX_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef MUTEX_H +#define MUTEX_H + +#include +#include + + +class Mutex : public QObject { + Q_OBJECT + + public: + // Constructors. + explicit Mutex(QMutex::RecursionMode mode = QMutex::NonRecursive, QObject* parent = 0); + virtual ~Mutex(); + + // Main methods. + bool tryLock(); + bool tryLock(int timeout); + + // Identifies if mutes is locked or not. + bool isLocked() const; + + operator QMutex* () const; + + public slots: + void lock(); + void unlock(); + + protected: + // These methods set proper value for m_isLocked and emit signals. + void setLocked(); + void setUnlocked(); + + signals: + void locked(); + void unlocked(); + + private: + QScopedPointer m_mutex; + bool m_isLocked; +}; + +#endif // MUTEX_H diff --git a/src/miscellaneous/serviceoperator.cpp b/src/miscellaneous/serviceoperator.cpp index cbb07c04f..2d9425577 100755 --- a/src/miscellaneous/serviceoperator.cpp +++ b/src/miscellaneous/serviceoperator.cpp @@ -1,25 +1,25 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "miscellaneous/serviceoperator.h" - - -ServiceOperator::ServiceOperator(QObject *parent) : QObject(parent) { -} - -ServiceOperator::~ServiceOperator() { -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "miscellaneous/serviceoperator.h" + + +ServiceOperator::ServiceOperator(QObject* parent) : QObject(parent) { +} + +ServiceOperator::~ServiceOperator() { +} diff --git a/src/miscellaneous/serviceoperator.h b/src/miscellaneous/serviceoperator.h index 352ae29df..de7085aee 100755 --- a/src/miscellaneous/serviceoperator.h +++ b/src/miscellaneous/serviceoperator.h @@ -1,36 +1,36 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SERVICEOPERATOR_H -#define SERVICEOPERATOR_H - -#include - - -class ServiceOperator : public QObject { - Q_OBJECT - - public: - explicit ServiceOperator(QObject *parent = 0); - virtual ~ServiceOperator(); - - signals: - - public slots: -}; - -#endif // SERVICEOPERATOR_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SERVICEOPERATOR_H +#define SERVICEOPERATOR_H + +#include + + +class ServiceOperator : public QObject { + Q_OBJECT + + public: + explicit ServiceOperator(QObject* parent = 0); + virtual ~ServiceOperator(); + + signals: + + public slots: +}; + +#endif // SERVICEOPERATOR_H diff --git a/src/miscellaneous/settings.cpp b/src/miscellaneous/settings.cpp index 9de51175f..0f2d9d82b 100755 --- a/src/miscellaneous/settings.cpp +++ b/src/miscellaneous/settings.cpp @@ -1,387 +1,384 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "miscellaneous/settings.h" - -#include "miscellaneous/application.h" -#include "miscellaneous/iofactory.h" -#include -#include -#include -#include - - -// AdBlock. -DKEY AdBlock::ID = "adblock"; - -DKEY AdBlock::DisabledRules = "disabled_rules"; -DVALUE(QStringList) AdBlock::DisabledRulesDef = QStringList(); - -DKEY AdBlock::AdBlockEnabled = "enabled"; -DVALUE(bool) AdBlock::AdBlockEnabledDef = false; - -DKEY AdBlock::LastUpdatedOn = "last_updated_on"; -DVALUE(QDateTime) AdBlock::LastUpdatedOnDef = QDateTime(); - -// Feeds. -DKEY Feeds::ID = "feeds"; - -DKEY Feeds::UpdateTimeout = "feed_update_timeout"; -DVALUE(int) Feeds::UpdateTimeoutDef = DOWNLOAD_TIMEOUT; - -DKEY Feeds::EnableAutoUpdateNotification = "enable_auto_update_notification"; -DVALUE(bool) Feeds::EnableAutoUpdateNotificationDef = true; - -DKEY Feeds::CountFormat = "count_format"; -DVALUE(char*) Feeds::CountFormatDef = "(%unread)"; - -DKEY Feeds::AutoUpdateInterval = "auto_update_interval"; -DVALUE(int) Feeds::AutoUpdateIntervalDef = DEFAULT_AUTO_UPDATE_INTERVAL; - -DKEY Feeds::AutoUpdateEnabled = "auto_update_enabled"; -DVALUE(bool) Feeds::AutoUpdateEnabledDef = false; - -DKEY Feeds::FeedsUpdateOnStartup = "feeds_update_on_startup"; -DVALUE(bool) Feeds::FeedsUpdateOnStartupDef = false; - -DKEY Feeds::ShowOnlyUnreadFeeds = "show_only_unread_feeds"; -DVALUE(bool) Feeds::ShowOnlyUnreadFeedsDef = false; - -// Messages. -DKEY Messages::ID = "messages"; - -DKEY Messages::MessageHeadImageHeight = "message_head_image_height"; -DVALUE(int) Messages::MessageHeadImageHeightDef = 36; - -DKEY Messages::UseCustomDate = "use_custom_date"; -DVALUE(bool) Messages::UseCustomDateDef = false; - -DKEY Messages::CustomDateFormat = "custom_date_format"; -DVALUE(char*) Messages::CustomDateFormatDef = ""; - -DKEY Messages::ClearReadOnExit = "clear_read_on_exit"; -DVALUE(bool) Messages::ClearReadOnExitDef = false; - -DKEY Messages::KeepCursorInCenter = "keep_cursor_center"; -DVALUE(bool) Messages::KeepCursorInCenterDef = false; - -DKEY Messages::PreviewerFontStandard = "previewer_font_standard"; -NON_CONST_DVALUE(QString) Messages::PreviewerFontStandardDef = QFont(QFont().family(), 12).toString(); - -// GUI. -DKEY GUI::ID = "gui"; - -DKEY GUI::MessageViewState = "msg_view_state"; -DVALUE(QString) GUI::MessageViewStateDef = QString(); - -DKEY GUI::SplitterFeeds = "splitter_feeds"; -DVALUE(char*) GUI::SplitterFeedsDef = ""; - -DKEY GUI::SplitterMessages = "splitter_messages"; -DVALUE(char*) GUI::SplitterMessagesDef = ""; - -DKEY GUI::ToolbarStyle = "toolbar_style"; -DVALUE(Qt::ToolButtonStyle) GUI::ToolbarStyleDef = Qt::ToolButtonIconOnly; - -DKEY GUI::FeedsToolbarActions = "feeds_toolbar"; -DVALUE(char*) GUI::FeedsToolbarActionsDef = "m_actionUpdateAllItems,m_actionStopRunningItemsUpdate,m_actionMarkAllItemsRead"; - -DKEY GUI::StatusbarActions = "status_bar"; -DVALUE(char*) GUI::StatusbarActionsDef = "m_lblProgressFeedsAction,m_barProgressFeedsAction,m_actionUpdateAllItems,m_actionUpdateSelectedItems,m_actionStopRunningItemsUpdate,m_actionFullscreen,m_actionQuit"; - -DKEY GUI::MainWindowInitialSize = "window_size"; -DKEY GUI::MainWindowInitialPosition = "window_position"; - -DKEY GUI::IsMainWindowMaximizedBeforeFullscreen = "is_window_maximized_before_fullscreen"; -DVALUE(bool) GUI::IsMainWindowMaximizedBeforeFullscreenDef = false; - -DKEY GUI::MainWindowStartsFullscreen = "start_in_fullscreen"; -DVALUE(bool) GUI::MainWindowStartsFullscreenDef = false; - -DKEY GUI::MainWindowStartsHidden = "start_hidden"; -DVALUE(bool) GUI::MainWindowStartsHiddenDef = false; - -DKEY GUI::MainWindowStartsMaximized = "window_is_maximized"; -DVALUE(bool) GUI::MainWindowStartsMaximizedDef = false; - -DKEY GUI::MainMenuVisible = "main_menu_visible"; -DVALUE(bool) GUI::MainMenuVisibleDef = true; - -DKEY GUI::ToolbarsVisible = "enable_toolbars"; -DVALUE(bool) GUI::ToolbarsVisibleDef = true; - -DKEY GUI::ListHeadersVisible = "enable_list_headers"; -DVALUE(bool) GUI::ListHeadersVisibleDef = true; - -DKEY GUI::StatusBarVisible = "enable_status_bar"; -DVALUE(bool) GUI::StatusBarVisibleDef = true; - -DKEY GUI::HideMainWindowWhenMinimized = "hide_when_minimized"; -DVALUE(bool) GUI::HideMainWindowWhenMinimizedDef = false; - -DKEY GUI::UseTrayIcon = "use_tray_icon"; -DVALUE(bool) GUI::UseTrayIconDef = true; - -DKEY GUI::EnableNotifications = "enable_notifications"; -DVALUE(bool) GUI::EnableNotificationsDef = true; - -DKEY GUI::TabCloseMiddleClick = "tab_close_mid_button"; -DVALUE(bool) GUI::TabCloseMiddleClickDef = true; - -DKEY GUI::TabCloseDoubleClick = "tab_close_double_button"; -DVALUE(bool) GUI::TabCloseDoubleClickDef = true; - -DKEY GUI::TabNewDoubleClick = "tab_new_double_button"; -DVALUE(bool) GUI::TabNewDoubleClickDef = true; - -DKEY GUI::HideTabBarIfOnlyOneTab = "hide_tabbar_one_tab"; -DVALUE(bool) GUI::HideTabBarIfOnlyOneTabDef = false; - -DKEY GUI::MessagesToolbarDefaultButtons = "messages_toolbar"; -DVALUE(char*) GUI::MessagesToolbarDefaultButtonsDef = "m_actionMarkSelectedMessagesAsRead,m_actionMarkSelectedMessagesAsUnread,m_actionSwitchImportanceOfSelectedMessages,separator,highlighter,spacer,search"; - -DKEY GUI::DefaultSortColumnFeeds = "default_sort_column_feeds"; -DVALUE(int) GUI::DefaultSortColumnFeedsDef = FDS_MODEL_TITLE_INDEX; - -DKEY GUI::DefaultSortOrderFeeds = "default_sort_order_feeds"; -DVALUE(Qt::SortOrder) GUI::DefaultSortOrderFeedsDef = Qt::AscendingOrder; - -DKEY GUI::IconTheme = "icon_theme_name"; -DVALUE(char*) GUI::IconThemeDef = APP_THEME_DEFAULT; - -DKEY GUI::Skin = "skin"; -DVALUE(char*) GUI::SkinDef = APP_SKIN_DEFAULT; - -DKEY GUI::Style = "style"; -DVALUE(char*) GUI::StyleDef = APP_STYLE_DEFAULT; - - -// General. -DKEY General::ID = "main"; - -DKEY General::UpdateOnStartup = "update_on_start"; -DVALUE(bool) General::UpdateOnStartupDef = true; - -DKEY General::RemoveTrolltechJunk = "remove_trolltech_junk"; -DVALUE(bool) General::RemoveTrolltechJunkDef = false; - -DKEY General::FirstRun = "first_run"; -DVALUE(bool) General::FirstRunDef = true; - -DKEY General::Language = "language"; -DVALUE(QString) General::LanguageDef = QLocale::system().name(); - -// Downloads. -DKEY Downloads::ID = "download_manager"; - -DKEY Downloads::AlwaysPromptForFilename = "prompt_for_filename"; -DVALUE(bool) Downloads::AlwaysPromptForFilenameDef = false; - -DKEY Downloads::TargetDirectory = "target_directory"; -DVALUE(QString) Downloads::TargetDirectoryDef = IOFactory::getSystemFolder(QStandardPaths::DesktopLocation); - -DKEY Downloads::RemovePolicy = "remove_policy"; -DVALUE(int) Downloads::RemovePolicyDef = DownloadManager::Never; - -DKEY Downloads::TargetExplicitDirectory = "target_explicit_directory"; -DVALUE(QString) Downloads::TargetExplicitDirectoryDef = IOFactory::getSystemFolder(QStandardPaths::DesktopLocation); - -DKEY Downloads::ShowDownloadsWhenNewDownloadStarts = "show_downloads_on_new_download_start"; -DVALUE(bool) Downloads::ShowDownloadsWhenNewDownloadStartsDef = true; - -DKEY Downloads::ItemUrl = "download_%1_url"; -DKEY Downloads::ItemLocation = "download_%1_location"; -DKEY Downloads::ItemDone = "download_%1_done"; - -// Proxy. -DKEY Proxy::ID = "proxy"; - -DKEY Proxy::Type = "proxy_type"; -DVALUE(QNetworkProxy::ProxyType) Proxy::TypeDef = QNetworkProxy::NoProxy; - -DKEY Proxy::Host = "host"; -DVALUE(QString) Proxy::HostDef = QString(); - -DKEY Proxy::Username = "username"; -DVALUE(QString) Proxy::UsernameDef = QString(); - -DKEY Proxy::Password = "password"; -DVALUE(QString) Proxy::PasswordDef = QString(); - -DKEY Proxy::Port = "port"; -DVALUE(int) Proxy::PortDef = 80; - -// Database. -DKEY Database::ID = "database"; - -DKEY Database::UseTransactions = "use_transactions"; -DVALUE(bool) Database::UseTransactionsDef = false; - -DKEY Database::UseInMemory = "use_in_memory_db"; -DVALUE(bool) Database::UseInMemoryDef = false; - -DKEY Database::MySQLHostname = "mysql_hostname"; -DVALUE(QString) Database::MySQLHostnameDef = QString(); - -DKEY Database::MySQLUsername = "mysql_username"; -DVALUE(QString) Database::MySQLUsernameDef = QString(); - -DKEY Database::MySQLPassword = "mysql_password"; -DVALUE(QString) Database::MySQLPasswordDef = QString(); - -DKEY Database::MySQLDatabase = "mysql_database"; -DVALUE(char*) Database::MySQLDatabaseDef = APP_LOW_NAME; - -DKEY Database::MySQLPort = "mysql_port"; -DVALUE(int) Database::MySQLPortDef = APP_DB_MYSQL_PORT; - -DKEY Database::ActiveDriver = "database_driver"; -DVALUE(char*) Database::ActiveDriverDef = APP_DB_SQLITE_DRIVER; - -// Keyboard. -DKEY Keyboard::ID = "keyboard"; - -// Web browser. -DKEY Browser::ID = "browser"; - -KEY Browser::SendDNT = "send_dnt"; -VALUE(bool) Browser::SendDNTDef = false; - -DKEY Browser::OpenLinksInExternalBrowserRightAway = "open_link_externally_wo_confirmation"; -DVALUE(bool) Browser::OpenLinksInExternalBrowserRightAwayDef = false; - -DKEY Browser::CustomExternalBrowserEnabled = "custom_external_browser"; -DVALUE(bool) Browser::CustomExternalBrowserEnabledDef = false; - -DKEY Browser::CustomExternalBrowserExecutable = "external_browser_executable"; -DVALUE(QString) Browser::CustomExternalBrowserExecutableDef = QString(); - -DKEY Browser::CustomExternalBrowserArguments = "external_browser_arguments"; -DVALUE(char*) Browser::CustomExternalBrowserArgumentsDef = "%1"; - -DKEY Browser::CustomExternalEmailEnabled = "custom_external_email"; -DVALUE(bool) Browser::CustomExternalEmailEnabledDef = false; - -DKEY Browser::CustomExternalEmailExecutable = "external_email_executable"; -DVALUE(QString) Browser::CustomExternalEmailExecutableDef = QString(); - -DKEY Browser::CustomExternalEmailArguments = "external_email_arguments"; -DVALUE(char*) Browser::CustomExternalEmailArgumentsDef = ""; - - -// Categories. -DKEY CategoriesExpandStates::ID = "categories_expand_states"; - -Settings::Settings(const QString &file_name, Format format, const SettingsProperties::SettingsType &status, QObject *parent) - : QSettings(file_name, format, parent), m_initializationStatus(status) { -} - -Settings::~Settings() { -} - -QString Settings::pathName() const { - return QFileInfo(fileName()).absolutePath(); -} - -QSettings::Status Settings::checkSettings() { - qDebug("Syncing settings."); - - sync(); - return status(); -} - -bool Settings::initiateRestoration(const QString &settings_backup_file_path) { - return IOFactory::copyFile(settings_backup_file_path, - QFileInfo(fileName()).absolutePath() + QDir::separator() + - BACKUP_NAME_SETTINGS + BACKUP_SUFFIX_SETTINGS); -} - -void Settings::finishRestoration(const QString &desired_settings_file_path) { - const QString backup_settings_file = QFileInfo(desired_settings_file_path).absolutePath() + QDir::separator() + - BACKUP_NAME_SETTINGS + BACKUP_SUFFIX_SETTINGS; - - if (QFile::exists(backup_settings_file)) { - qWarning("Backup settings file '%s' was detected. Restoring it.", qPrintable(QDir::toNativeSeparators(backup_settings_file))); - - if (IOFactory::copyFile(backup_settings_file, desired_settings_file_path)) { - QFile::remove(backup_settings_file); - qDebug("Settings file was restored successully."); - } - else { - qCritical("Settings file was NOT restored due to error when copying the file."); - } - } -} - -Settings *Settings::setupSettings(QObject *parent) { - Settings *new_settings; - - // If settings file exists (and is writable) in executable file working directory - // (in subdirectory APP_CFG_PATH), then use it (portable settings). - // Otherwise use settings file stored in home path. - const SettingsProperties properties = determineProperties(); - - finishRestoration(properties.m_absoluteSettingsFileName); - - // Portable settings are available, use them. - new_settings = new Settings(properties.m_absoluteSettingsFileName, QSettings::IniFormat, properties.m_type, parent); - - // Check if portable settings are available. - if (properties.m_type == SettingsProperties::Portable) { - qDebug("Initializing settings in '%s' (portable way).", qPrintable(QDir::toNativeSeparators(properties.m_absoluteSettingsFileName))); - } - else { - qDebug("Initializing settings in '%s' (non-portable way).", qPrintable(QDir::toNativeSeparators(properties.m_absoluteSettingsFileName))); - } - - return new_settings; -} - -SettingsProperties Settings::determineProperties() { - SettingsProperties properties; - - properties.m_settingsSuffix = QDir::separator() + QSL(APP_CFG_PATH) + QDir::separator() + QSL(APP_CFG_FILE); - - const QString app_path = qApp->getUserDataAppPath(); - const QString home_path = qApp->getUserDataHomePath(); - - // We will use PORTABLE settings only and only if it is available and NON-PORTABLE - // settings was not initialized before. -#if defined (Q_OS_LINUX) || defined (Q_OS_MACOS) - // DO NOT use portable settings for Linux, it is really not used on that platform. - const bool will_we_use_portable_settings = false; -#else - const QString exe_path = qApp->applicationDirPath(); - const QString home_path_file = home_path + properties.m_settingsSuffix; - const bool portable_settings_available = IOFactory::isFolderWritable(exe_path); - const bool non_portable_settings_exist = QFile::exists(home_path_file); - const bool will_we_use_portable_settings = portable_settings_available && !non_portable_settings_exist; -#endif - - if (will_we_use_portable_settings) { - properties.m_type = SettingsProperties::Portable; - properties.m_baseDirectory = app_path; - } - else { - properties.m_type = SettingsProperties::NonPortable; - properties.m_baseDirectory = home_path; - } - - properties.m_absoluteSettingsFileName = properties.m_baseDirectory + properties.m_settingsSuffix; - - return properties; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "miscellaneous/settings.h" + +#include "miscellaneous/application.h" +#include "miscellaneous/iofactory.h" +#include +#include +#include +#include + + +// AdBlock. +DKEY AdBlock::ID = "adblock"; + +DKEY AdBlock::DisabledRules = "disabled_rules"; +DVALUE(QStringList) AdBlock::DisabledRulesDef = QStringList(); + +DKEY AdBlock::AdBlockEnabled = "enabled"; +DVALUE(bool) AdBlock::AdBlockEnabledDef = false; + +DKEY AdBlock::LastUpdatedOn = "last_updated_on"; +DVALUE(QDateTime) AdBlock::LastUpdatedOnDef = QDateTime(); + +// Feeds. +DKEY Feeds::ID = "feeds"; + +DKEY Feeds::UpdateTimeout = "feed_update_timeout"; +DVALUE(int) Feeds::UpdateTimeoutDef = DOWNLOAD_TIMEOUT; + +DKEY Feeds::EnableAutoUpdateNotification = "enable_auto_update_notification"; +DVALUE(bool) Feeds::EnableAutoUpdateNotificationDef = true; + +DKEY Feeds::CountFormat = "count_format"; +DVALUE(char*) Feeds::CountFormatDef = "(%unread)"; + +DKEY Feeds::AutoUpdateInterval = "auto_update_interval"; +DVALUE(int) Feeds::AutoUpdateIntervalDef = DEFAULT_AUTO_UPDATE_INTERVAL; + +DKEY Feeds::AutoUpdateEnabled = "auto_update_enabled"; +DVALUE(bool) Feeds::AutoUpdateEnabledDef = false; + +DKEY Feeds::FeedsUpdateOnStartup = "feeds_update_on_startup"; +DVALUE(bool) Feeds::FeedsUpdateOnStartupDef = false; + +DKEY Feeds::ShowOnlyUnreadFeeds = "show_only_unread_feeds"; +DVALUE(bool) Feeds::ShowOnlyUnreadFeedsDef = false; + +// Messages. +DKEY Messages::ID = "messages"; + +DKEY Messages::MessageHeadImageHeight = "message_head_image_height"; +DVALUE(int) Messages::MessageHeadImageHeightDef = 36; + +DKEY Messages::UseCustomDate = "use_custom_date"; +DVALUE(bool) Messages::UseCustomDateDef = false; + +DKEY Messages::CustomDateFormat = "custom_date_format"; +DVALUE(char*) Messages::CustomDateFormatDef = ""; + +DKEY Messages::ClearReadOnExit = "clear_read_on_exit"; +DVALUE(bool) Messages::ClearReadOnExitDef = false; + +DKEY Messages::KeepCursorInCenter = "keep_cursor_center"; +DVALUE(bool) Messages::KeepCursorInCenterDef = false; + +DKEY Messages::PreviewerFontStandard = "previewer_font_standard"; +NON_CONST_DVALUE(QString) Messages::PreviewerFontStandardDef = QFont(QFont().family(), 12).toString(); + +// GUI. +DKEY GUI::ID = "gui"; + +DKEY GUI::MessageViewState = "msg_view_state"; +DVALUE(QString) GUI::MessageViewStateDef = QString(); + +DKEY GUI::SplitterFeeds = "splitter_feeds"; +DVALUE(char*) GUI::SplitterFeedsDef = ""; + +DKEY GUI::SplitterMessages = "splitter_messages"; +DVALUE(char*) GUI::SplitterMessagesDef = ""; + +DKEY GUI::ToolbarStyle = "toolbar_style"; +DVALUE(Qt::ToolButtonStyle) GUI::ToolbarStyleDef = Qt::ToolButtonIconOnly; + +DKEY GUI::FeedsToolbarActions = "feeds_toolbar"; +DVALUE(char*) GUI::FeedsToolbarActionsDef = "m_actionUpdateAllItems,m_actionStopRunningItemsUpdate,m_actionMarkAllItemsRead"; + +DKEY GUI::StatusbarActions = "status_bar"; +DVALUE(char*) GUI::StatusbarActionsDef = + "m_lblProgressFeedsAction,m_barProgressFeedsAction,m_actionUpdateAllItems,m_actionUpdateSelectedItems,m_actionStopRunningItemsUpdate,m_actionFullscreen,m_actionQuit"; + +DKEY GUI::MainWindowInitialSize = "window_size"; +DKEY GUI::MainWindowInitialPosition = "window_position"; + +DKEY GUI::IsMainWindowMaximizedBeforeFullscreen = "is_window_maximized_before_fullscreen"; +DVALUE(bool) GUI::IsMainWindowMaximizedBeforeFullscreenDef = false; + +DKEY GUI::MainWindowStartsFullscreen = "start_in_fullscreen"; +DVALUE(bool) GUI::MainWindowStartsFullscreenDef = false; + +DKEY GUI::MainWindowStartsHidden = "start_hidden"; +DVALUE(bool) GUI::MainWindowStartsHiddenDef = false; + +DKEY GUI::MainWindowStartsMaximized = "window_is_maximized"; +DVALUE(bool) GUI::MainWindowStartsMaximizedDef = false; + +DKEY GUI::MainMenuVisible = "main_menu_visible"; +DVALUE(bool) GUI::MainMenuVisibleDef = true; + +DKEY GUI::ToolbarsVisible = "enable_toolbars"; +DVALUE(bool) GUI::ToolbarsVisibleDef = true; + +DKEY GUI::ListHeadersVisible = "enable_list_headers"; +DVALUE(bool) GUI::ListHeadersVisibleDef = true; + +DKEY GUI::StatusBarVisible = "enable_status_bar"; +DVALUE(bool) GUI::StatusBarVisibleDef = true; + +DKEY GUI::HideMainWindowWhenMinimized = "hide_when_minimized"; +DVALUE(bool) GUI::HideMainWindowWhenMinimizedDef = false; + +DKEY GUI::UseTrayIcon = "use_tray_icon"; +DVALUE(bool) GUI::UseTrayIconDef = true; + +DKEY GUI::EnableNotifications = "enable_notifications"; +DVALUE(bool) GUI::EnableNotificationsDef = true; + +DKEY GUI::TabCloseMiddleClick = "tab_close_mid_button"; +DVALUE(bool) GUI::TabCloseMiddleClickDef = true; + +DKEY GUI::TabCloseDoubleClick = "tab_close_double_button"; +DVALUE(bool) GUI::TabCloseDoubleClickDef = true; + +DKEY GUI::TabNewDoubleClick = "tab_new_double_button"; +DVALUE(bool) GUI::TabNewDoubleClickDef = true; + +DKEY GUI::HideTabBarIfOnlyOneTab = "hide_tabbar_one_tab"; +DVALUE(bool) GUI::HideTabBarIfOnlyOneTabDef = false; + +DKEY GUI::MessagesToolbarDefaultButtons = "messages_toolbar"; +DVALUE(char*) GUI::MessagesToolbarDefaultButtonsDef = + "m_actionMarkSelectedMessagesAsRead,m_actionMarkSelectedMessagesAsUnread,m_actionSwitchImportanceOfSelectedMessages,separator,highlighter,spacer,search"; + +DKEY GUI::DefaultSortColumnFeeds = "default_sort_column_feeds"; +DVALUE(int) GUI::DefaultSortColumnFeedsDef = FDS_MODEL_TITLE_INDEX; + +DKEY GUI::DefaultSortOrderFeeds = "default_sort_order_feeds"; +DVALUE(Qt::SortOrder) GUI::DefaultSortOrderFeedsDef = Qt::AscendingOrder; + +DKEY GUI::IconTheme = "icon_theme_name"; +DVALUE(char*) GUI::IconThemeDef = APP_THEME_DEFAULT; + +DKEY GUI::Skin = "skin"; +DVALUE(char*) GUI::SkinDef = APP_SKIN_DEFAULT; + +DKEY GUI::Style = "style"; +DVALUE(char*) GUI::StyleDef = APP_STYLE_DEFAULT; + + +// General. +DKEY General::ID = "main"; + +DKEY General::UpdateOnStartup = "update_on_start"; +DVALUE(bool) General::UpdateOnStartupDef = true; + +DKEY General::RemoveTrolltechJunk = "remove_trolltech_junk"; +DVALUE(bool) General::RemoveTrolltechJunkDef = false; + +DKEY General::FirstRun = "first_run"; +DVALUE(bool) General::FirstRunDef = true; + +DKEY General::Language = "language"; +DVALUE(QString) General::LanguageDef = QLocale::system().name(); + +// Downloads. +DKEY Downloads::ID = "download_manager"; + +DKEY Downloads::AlwaysPromptForFilename = "prompt_for_filename"; +DVALUE(bool) Downloads::AlwaysPromptForFilenameDef = false; + +DKEY Downloads::TargetDirectory = "target_directory"; +DVALUE(QString) Downloads::TargetDirectoryDef = IOFactory::getSystemFolder(QStandardPaths::DesktopLocation); + +DKEY Downloads::RemovePolicy = "remove_policy"; +DVALUE(int) Downloads::RemovePolicyDef = DownloadManager::Never; + +DKEY Downloads::TargetExplicitDirectory = "target_explicit_directory"; +DVALUE(QString) Downloads::TargetExplicitDirectoryDef = IOFactory::getSystemFolder(QStandardPaths::DesktopLocation); + +DKEY Downloads::ShowDownloadsWhenNewDownloadStarts = "show_downloads_on_new_download_start"; +DVALUE(bool) Downloads::ShowDownloadsWhenNewDownloadStartsDef = true; + +DKEY Downloads::ItemUrl = "download_%1_url"; +DKEY Downloads::ItemLocation = "download_%1_location"; +DKEY Downloads::ItemDone = "download_%1_done"; + +// Proxy. +DKEY Proxy::ID = "proxy"; + +DKEY Proxy::Type = "proxy_type"; +DVALUE(QNetworkProxy::ProxyType) Proxy::TypeDef = QNetworkProxy::NoProxy; + +DKEY Proxy::Host = "host"; +DVALUE(QString) Proxy::HostDef = QString(); + +DKEY Proxy::Username = "username"; +DVALUE(QString) Proxy::UsernameDef = QString(); + +DKEY Proxy::Password = "password"; +DVALUE(QString) Proxy::PasswordDef = QString(); + +DKEY Proxy::Port = "port"; +DVALUE(int) Proxy::PortDef = 80; + +// Database. +DKEY Database::ID = "database"; + +DKEY Database::UseTransactions = "use_transactions"; +DVALUE(bool) Database::UseTransactionsDef = false; + +DKEY Database::UseInMemory = "use_in_memory_db"; +DVALUE(bool) Database::UseInMemoryDef = false; + +DKEY Database::MySQLHostname = "mysql_hostname"; +DVALUE(QString) Database::MySQLHostnameDef = QString(); + +DKEY Database::MySQLUsername = "mysql_username"; +DVALUE(QString) Database::MySQLUsernameDef = QString(); + +DKEY Database::MySQLPassword = "mysql_password"; +DVALUE(QString) Database::MySQLPasswordDef = QString(); + +DKEY Database::MySQLDatabase = "mysql_database"; +DVALUE(char*) Database::MySQLDatabaseDef = APP_LOW_NAME; + +DKEY Database::MySQLPort = "mysql_port"; +DVALUE(int) Database::MySQLPortDef = APP_DB_MYSQL_PORT; + +DKEY Database::ActiveDriver = "database_driver"; +DVALUE(char*) Database::ActiveDriverDef = APP_DB_SQLITE_DRIVER; + +// Keyboard. +DKEY Keyboard::ID = "keyboard"; + +// Web browser. +DKEY Browser::ID = "browser"; + +KEY Browser::SendDNT = "send_dnt"; +VALUE(bool) Browser::SendDNTDef = false; + +DKEY Browser::OpenLinksInExternalBrowserRightAway = "open_link_externally_wo_confirmation"; +DVALUE(bool) Browser::OpenLinksInExternalBrowserRightAwayDef = false; + +DKEY Browser::CustomExternalBrowserEnabled = "custom_external_browser"; +DVALUE(bool) Browser::CustomExternalBrowserEnabledDef = false; + +DKEY Browser::CustomExternalBrowserExecutable = "external_browser_executable"; +DVALUE(QString) Browser::CustomExternalBrowserExecutableDef = QString(); + +DKEY Browser::CustomExternalBrowserArguments = "external_browser_arguments"; +DVALUE(char*) Browser::CustomExternalBrowserArgumentsDef = "%1"; + +DKEY Browser::CustomExternalEmailEnabled = "custom_external_email"; +DVALUE(bool) Browser::CustomExternalEmailEnabledDef = false; + +DKEY Browser::CustomExternalEmailExecutable = "external_email_executable"; +DVALUE(QString) Browser::CustomExternalEmailExecutableDef = QString(); + +DKEY Browser::CustomExternalEmailArguments = "external_email_arguments"; +DVALUE(char*) Browser::CustomExternalEmailArgumentsDef = ""; + + +// Categories. +DKEY CategoriesExpandStates::ID = "categories_expand_states"; + +Settings::Settings(const QString& file_name, Format format, const SettingsProperties::SettingsType& status, QObject* parent) + : QSettings(file_name, format, parent), m_initializationStatus(status) { +} + +Settings::~Settings() { +} + +QString Settings::pathName() const { + return QFileInfo(fileName()).absolutePath(); +} + +QSettings::Status Settings::checkSettings() { + qDebug("Syncing settings."); + sync(); + return status(); +} + +bool Settings::initiateRestoration(const QString& settings_backup_file_path) { + return IOFactory::copyFile(settings_backup_file_path, + QFileInfo(fileName()).absolutePath() + QDir::separator() + + BACKUP_NAME_SETTINGS + BACKUP_SUFFIX_SETTINGS); +} + +void Settings::finishRestoration(const QString& desired_settings_file_path) { + const QString backup_settings_file = QFileInfo(desired_settings_file_path).absolutePath() + QDir::separator() + + BACKUP_NAME_SETTINGS + BACKUP_SUFFIX_SETTINGS; + + if (QFile::exists(backup_settings_file)) { + qWarning("Backup settings file '%s' was detected. Restoring it.", qPrintable(QDir::toNativeSeparators(backup_settings_file))); + + if (IOFactory::copyFile(backup_settings_file, desired_settings_file_path)) { + QFile::remove(backup_settings_file); + qDebug("Settings file was restored successully."); + } + + else { + qCritical("Settings file was NOT restored due to error when copying the file."); + } + } +} + +Settings* Settings::setupSettings(QObject* parent) { + Settings* new_settings; + // If settings file exists (and is writable) in executable file working directory + // (in subdirectory APP_CFG_PATH), then use it (portable settings). + // Otherwise use settings file stored in home path. + const SettingsProperties properties = determineProperties(); + finishRestoration(properties.m_absoluteSettingsFileName); + // Portable settings are available, use them. + new_settings = new Settings(properties.m_absoluteSettingsFileName, QSettings::IniFormat, properties.m_type, parent); + + // Check if portable settings are available. + if (properties.m_type == SettingsProperties::Portable) { + qDebug("Initializing settings in '%s' (portable way).", qPrintable(QDir::toNativeSeparators(properties.m_absoluteSettingsFileName))); + } + + else { + qDebug("Initializing settings in '%s' (non-portable way).", qPrintable(QDir::toNativeSeparators(properties.m_absoluteSettingsFileName))); + } + + return new_settings; +} + +SettingsProperties Settings::determineProperties() { + SettingsProperties properties; + properties.m_settingsSuffix = QDir::separator() + QSL(APP_CFG_PATH) + QDir::separator() + QSL(APP_CFG_FILE); + const QString app_path = qApp->getUserDataAppPath(); + const QString home_path = qApp->getUserDataHomePath(); + // We will use PORTABLE settings only and only if it is available and NON-PORTABLE + // settings was not initialized before. +#if defined (Q_OS_LINUX) || defined (Q_OS_MACOS) + // DO NOT use portable settings for Linux, it is really not used on that platform. + const bool will_we_use_portable_settings = false; +#else + const QString exe_path = qApp->applicationDirPath(); + const QString home_path_file = home_path + properties.m_settingsSuffix; + const bool portable_settings_available = IOFactory::isFolderWritable(exe_path); + const bool non_portable_settings_exist = QFile::exists(home_path_file); + const bool will_we_use_portable_settings = portable_settings_available && !non_portable_settings_exist; +#endif + + if (will_we_use_portable_settings) { + properties.m_type = SettingsProperties::Portable; + properties.m_baseDirectory = app_path; + } + + else { + properties.m_type = SettingsProperties::NonPortable; + properties.m_baseDirectory = home_path; + } + + properties.m_absoluteSettingsFileName = properties.m_baseDirectory + properties.m_settingsSuffix; + return properties; +} diff --git a/src/miscellaneous/settings.h b/src/miscellaneous/settings.h index 72c863dad..ee397157e 100755 --- a/src/miscellaneous/settings.h +++ b/src/miscellaneous/settings.h @@ -1,379 +1,379 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SETTINGS_H -#define SETTINGS_H - -#include - -#include "definitions/definitions.h" - -#include "miscellaneous/settingsproperties.h" - -#include -#include -#include -#include -#include - -#define KEY extern const char* -#define DKEY const char* -#define VALUE(x) extern const x -#define NON_CONST_VALUE(x) extern x -#define DVALUE(x) const x -#define NON_CONST_DVALUE(x) x -#define SETTING(x) x, x##Def -#define DEFAULT_VALUE(x) x##Def -#define GROUP(x) x::ID - - -namespace AdBlock { - KEY ID; - - KEY AdBlockEnabled; - VALUE(bool) AdBlockEnabledDef; - - KEY DisabledRules; - VALUE(QStringList) DisabledRulesDef; - - KEY LastUpdatedOn; - VALUE(QDateTime) LastUpdatedOnDef; -} - -// Feeds. -namespace Feeds { - KEY ID; - - KEY UpdateTimeout; - VALUE(int) UpdateTimeoutDef; - - KEY EnableAutoUpdateNotification; - VALUE(bool) EnableAutoUpdateNotificationDef; - - KEY CountFormat; - VALUE(char*) CountFormatDef; - - KEY AutoUpdateInterval; - VALUE(int) AutoUpdateIntervalDef; - - KEY AutoUpdateEnabled; - VALUE(bool) AutoUpdateEnabledDef; - - KEY FeedsUpdateOnStartup; - VALUE(bool) FeedsUpdateOnStartupDef; - - KEY ShowOnlyUnreadFeeds; - VALUE(bool) ShowOnlyUnreadFeedsDef; -} - -// Messages. -namespace Messages { - KEY ID; - - KEY MessageHeadImageHeight; - VALUE(int) MessageHeadImageHeightDef; - - KEY UseCustomDate; - VALUE(bool) UseCustomDateDef; - - KEY CustomDateFormat; - VALUE(char*) CustomDateFormatDef; - - KEY ClearReadOnExit; - VALUE(bool) ClearReadOnExitDef; - - KEY KeepCursorInCenter; - VALUE(bool) KeepCursorInCenterDef; - - KEY PreviewerFontStandard; - NON_CONST_VALUE(QString) PreviewerFontStandardDef; -} - -// GUI. -namespace GUI { - KEY ID; - - KEY MessageViewState; - VALUE(QString) MessageViewStateDef; - - KEY SplitterFeeds; - VALUE(char*) SplitterFeedsDef; - - KEY SplitterMessages; - VALUE(char*) SplitterMessagesDef; - - KEY ToolbarStyle; - VALUE(Qt::ToolButtonStyle) ToolbarStyleDef; - - KEY FeedsToolbarActions; - VALUE(char*) FeedsToolbarActionsDef; - - KEY StatusbarActions; - VALUE(char*) StatusbarActionsDef; - - KEY MainWindowInitialSize; - KEY MainWindowInitialPosition; - - KEY IsMainWindowMaximizedBeforeFullscreen; - VALUE(bool) IsMainWindowMaximizedBeforeFullscreenDef; - - KEY MainWindowStartsFullscreen; - VALUE(bool) MainWindowStartsFullscreenDef; - - KEY MainWindowStartsHidden; - VALUE(bool) MainWindowStartsHiddenDef; - - KEY MainWindowStartsMaximized; - VALUE(bool) MainWindowStartsMaximizedDef; - - KEY MainMenuVisible; - VALUE(bool) MainMenuVisibleDef; - - KEY ToolbarsVisible; - VALUE(bool) ToolbarsVisibleDef; - - KEY ListHeadersVisible; - VALUE(bool) ListHeadersVisibleDef; - - KEY StatusBarVisible; - VALUE(bool) StatusBarVisibleDef; - - KEY HideMainWindowWhenMinimized; - VALUE(bool) HideMainWindowWhenMinimizedDef; - - KEY UseTrayIcon; - VALUE(bool) UseTrayIconDef; - - KEY EnableNotifications; - VALUE(bool) EnableNotificationsDef; - - KEY TabCloseMiddleClick; - VALUE(bool) TabCloseMiddleClickDef; - - KEY TabCloseDoubleClick; - VALUE(bool) TabCloseDoubleClickDef; - - KEY TabNewDoubleClick; - VALUE(bool) TabNewDoubleClickDef; - - KEY HideTabBarIfOnlyOneTab; - VALUE(bool) HideTabBarIfOnlyOneTabDef; - - KEY MessagesToolbarDefaultButtons; - VALUE(char*) MessagesToolbarDefaultButtonsDef; - - KEY DefaultSortColumnFeeds; - VALUE(int) DefaultSortColumnFeedsDef; - - KEY DefaultSortOrderFeeds; - VALUE(Qt::SortOrder) DefaultSortOrderFeedsDef; - - KEY IconTheme; - VALUE(char*) IconThemeDef; - - KEY Skin; - VALUE(char*) SkinDef; - - KEY Style; - VALUE(char*) StyleDef; -} - -// General. -namespace General { - KEY ID; - - KEY UpdateOnStartup; - VALUE(bool) UpdateOnStartupDef; - - KEY RemoveTrolltechJunk; - VALUE(bool) RemoveTrolltechJunkDef; - - KEY FirstRun; - VALUE(bool) FirstRunDef; - - KEY Language; - VALUE(QString) LanguageDef; -} - -// Downloads. -namespace Downloads { - KEY ID; - - KEY AlwaysPromptForFilename; - VALUE(bool) AlwaysPromptForFilenameDef; - - KEY TargetDirectory; - VALUE(QString) TargetDirectoryDef; - - KEY RemovePolicy; - VALUE(int) RemovePolicyDef; - - KEY TargetExplicitDirectory; - VALUE(QString) TargetExplicitDirectoryDef; - - KEY ShowDownloadsWhenNewDownloadStarts; - VALUE(bool) ShowDownloadsWhenNewDownloadStartsDef; - - KEY ItemUrl; - KEY ItemLocation; - KEY ItemDone; -} - -// Proxy. -namespace Proxy { - KEY ID; - - KEY Type; - VALUE(QNetworkProxy::ProxyType) TypeDef; - - KEY Host; - VALUE(QString) HostDef; - - KEY Username; - VALUE(QString) UsernameDef; - - KEY Password; - VALUE(QString) PasswordDef; - - KEY Port; - VALUE(int) PortDef; -} - -// Database. -namespace Database { - KEY ID; - - KEY UseTransactions; - VALUE(bool) UseTransactionsDef; - - KEY UseInMemory; - VALUE(bool) UseInMemoryDef; - - KEY MySQLHostname; - VALUE(QString) MySQLHostnameDef; - - KEY MySQLUsername; - VALUE(QString) MySQLUsernameDef; - - KEY MySQLPassword; - VALUE(QString) MySQLPasswordDef; - - KEY MySQLPort; - VALUE(int) MySQLPortDef; - - KEY MySQLDatabase; - VALUE(char*) MySQLDatabaseDef; - - KEY ActiveDriver; - VALUE(char*) ActiveDriverDef; -} - -// Keyboard. -namespace Keyboard { - KEY ID; -} - -// Web browser. -namespace Browser { - KEY ID; - - KEY SendDNT; - VALUE(bool) SendDNTDef; - - KEY OpenLinksInExternalBrowserRightAway; - VALUE(bool) OpenLinksInExternalBrowserRightAwayDef; - - KEY CustomExternalBrowserEnabled; - VALUE(bool) CustomExternalBrowserEnabledDef; - - KEY CustomExternalBrowserExecutable; - VALUE(QString) CustomExternalBrowserExecutableDef; - - KEY CustomExternalBrowserArguments; - VALUE(char*) CustomExternalBrowserArgumentsDef; - - KEY CustomExternalEmailEnabled; - VALUE(bool) CustomExternalEmailEnabledDef; - - KEY CustomExternalEmailExecutable; - VALUE(QString) CustomExternalEmailExecutableDef; - - KEY CustomExternalEmailArguments; - VALUE(char*) CustomExternalEmailArgumentsDef; -} - -// Categories. -namespace CategoriesExpandStates { - KEY ID; -} - -class Settings : public QSettings { - Q_OBJECT - - public: - // Destructor. - virtual ~Settings(); - - // Type of used settings. - inline SettingsProperties::SettingsType type() const { - return m_initializationStatus; - } - - // Getters/setters for settings values. - inline QVariant value(const QString §ion, const QString &key, const QVariant &default_value = QVariant()) const { - return QSettings::value(QString(QSL("%1/%2")).arg(section, key), default_value); - } - - inline void setValue(const QString §ion, const QString &key, const QVariant &value) { - QSettings::setValue(QString(QSL("%1/%2")).arg(section, key), value); - } - - inline void setValue(const QString &key, const QVariant &value) { - QSettings::setValue(key, value); - } - - inline bool contains(const QString §ion, const QString &key) const { - return QSettings::contains(QString(QSL("%1/%2")).arg(section, key)); - } - - inline void remove(const QString §ion, const QString &key) { - QSettings::remove(QString(QSL("%1/%2")).arg(section, key)); - } - - // Returns the path which contains the settings. - QString pathName() const; - - // Synchronizes settings. - QSettings::Status checkSettings(); - - bool initiateRestoration(const QString &settings_backup_file_path); - static void finishRestoration(const QString &desired_settings_file_path); - - // Creates settings file in correct location. - static Settings *setupSettings(QObject *parent); - - // Returns properties of the actual application-wide settings. - static SettingsProperties determineProperties(); - - private: - // Constructor. - explicit Settings(const QString &file_name, Format format, const SettingsProperties::SettingsType &type, QObject *parent = 0); - - SettingsProperties::SettingsType m_initializationStatus; -}; - -#endif // SETTINGS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SETTINGS_H +#define SETTINGS_H + +#include + +#include "definitions/definitions.h" + +#include "miscellaneous/settingsproperties.h" + +#include +#include +#include +#include +#include + +#define KEY extern const char* +#define DKEY const char* +#define VALUE(x) extern const x +#define NON_CONST_VALUE(x) extern x +#define DVALUE(x) const x +#define NON_CONST_DVALUE(x) x +#define SETTING(x) x, x##Def +#define DEFAULT_VALUE(x) x##Def +#define GROUP(x) x::ID + + +namespace AdBlock { + KEY ID; + + KEY AdBlockEnabled; + VALUE(bool) AdBlockEnabledDef; + + KEY DisabledRules; + VALUE(QStringList) DisabledRulesDef; + + KEY LastUpdatedOn; + VALUE(QDateTime) LastUpdatedOnDef; +} + +// Feeds. +namespace Feeds { + KEY ID; + + KEY UpdateTimeout; + VALUE(int) UpdateTimeoutDef; + + KEY EnableAutoUpdateNotification; + VALUE(bool) EnableAutoUpdateNotificationDef; + + KEY CountFormat; + VALUE(char*) CountFormatDef; + + KEY AutoUpdateInterval; + VALUE(int) AutoUpdateIntervalDef; + + KEY AutoUpdateEnabled; + VALUE(bool) AutoUpdateEnabledDef; + + KEY FeedsUpdateOnStartup; + VALUE(bool) FeedsUpdateOnStartupDef; + + KEY ShowOnlyUnreadFeeds; + VALUE(bool) ShowOnlyUnreadFeedsDef; +} + +// Messages. +namespace Messages { + KEY ID; + + KEY MessageHeadImageHeight; + VALUE(int) MessageHeadImageHeightDef; + + KEY UseCustomDate; + VALUE(bool) UseCustomDateDef; + + KEY CustomDateFormat; + VALUE(char*) CustomDateFormatDef; + + KEY ClearReadOnExit; + VALUE(bool) ClearReadOnExitDef; + + KEY KeepCursorInCenter; + VALUE(bool) KeepCursorInCenterDef; + + KEY PreviewerFontStandard; + NON_CONST_VALUE(QString) PreviewerFontStandardDef; +} + +// GUI. +namespace GUI { + KEY ID; + + KEY MessageViewState; + VALUE(QString) MessageViewStateDef; + + KEY SplitterFeeds; + VALUE(char*) SplitterFeedsDef; + + KEY SplitterMessages; + VALUE(char*) SplitterMessagesDef; + + KEY ToolbarStyle; + VALUE(Qt::ToolButtonStyle) ToolbarStyleDef; + + KEY FeedsToolbarActions; + VALUE(char*) FeedsToolbarActionsDef; + + KEY StatusbarActions; + VALUE(char*) StatusbarActionsDef; + + KEY MainWindowInitialSize; + KEY MainWindowInitialPosition; + + KEY IsMainWindowMaximizedBeforeFullscreen; + VALUE(bool) IsMainWindowMaximizedBeforeFullscreenDef; + + KEY MainWindowStartsFullscreen; + VALUE(bool) MainWindowStartsFullscreenDef; + + KEY MainWindowStartsHidden; + VALUE(bool) MainWindowStartsHiddenDef; + + KEY MainWindowStartsMaximized; + VALUE(bool) MainWindowStartsMaximizedDef; + + KEY MainMenuVisible; + VALUE(bool) MainMenuVisibleDef; + + KEY ToolbarsVisible; + VALUE(bool) ToolbarsVisibleDef; + + KEY ListHeadersVisible; + VALUE(bool) ListHeadersVisibleDef; + + KEY StatusBarVisible; + VALUE(bool) StatusBarVisibleDef; + + KEY HideMainWindowWhenMinimized; + VALUE(bool) HideMainWindowWhenMinimizedDef; + + KEY UseTrayIcon; + VALUE(bool) UseTrayIconDef; + + KEY EnableNotifications; + VALUE(bool) EnableNotificationsDef; + + KEY TabCloseMiddleClick; + VALUE(bool) TabCloseMiddleClickDef; + + KEY TabCloseDoubleClick; + VALUE(bool) TabCloseDoubleClickDef; + + KEY TabNewDoubleClick; + VALUE(bool) TabNewDoubleClickDef; + + KEY HideTabBarIfOnlyOneTab; + VALUE(bool) HideTabBarIfOnlyOneTabDef; + + KEY MessagesToolbarDefaultButtons; + VALUE(char*) MessagesToolbarDefaultButtonsDef; + + KEY DefaultSortColumnFeeds; + VALUE(int) DefaultSortColumnFeedsDef; + + KEY DefaultSortOrderFeeds; + VALUE(Qt::SortOrder) DefaultSortOrderFeedsDef; + + KEY IconTheme; + VALUE(char*) IconThemeDef; + + KEY Skin; + VALUE(char*) SkinDef; + + KEY Style; + VALUE(char*) StyleDef; +} + +// General. +namespace General { + KEY ID; + + KEY UpdateOnStartup; + VALUE(bool) UpdateOnStartupDef; + + KEY RemoveTrolltechJunk; + VALUE(bool) RemoveTrolltechJunkDef; + + KEY FirstRun; + VALUE(bool) FirstRunDef; + + KEY Language; + VALUE(QString) LanguageDef; +} + +// Downloads. +namespace Downloads { + KEY ID; + + KEY AlwaysPromptForFilename; + VALUE(bool) AlwaysPromptForFilenameDef; + + KEY TargetDirectory; + VALUE(QString) TargetDirectoryDef; + + KEY RemovePolicy; + VALUE(int) RemovePolicyDef; + + KEY TargetExplicitDirectory; + VALUE(QString) TargetExplicitDirectoryDef; + + KEY ShowDownloadsWhenNewDownloadStarts; + VALUE(bool) ShowDownloadsWhenNewDownloadStartsDef; + + KEY ItemUrl; + KEY ItemLocation; + KEY ItemDone; +} + +// Proxy. +namespace Proxy { + KEY ID; + + KEY Type; + VALUE(QNetworkProxy::ProxyType) TypeDef; + + KEY Host; + VALUE(QString) HostDef; + + KEY Username; + VALUE(QString) UsernameDef; + + KEY Password; + VALUE(QString) PasswordDef; + + KEY Port; + VALUE(int) PortDef; +} + +// Database. +namespace Database { + KEY ID; + + KEY UseTransactions; + VALUE(bool) UseTransactionsDef; + + KEY UseInMemory; + VALUE(bool) UseInMemoryDef; + + KEY MySQLHostname; + VALUE(QString) MySQLHostnameDef; + + KEY MySQLUsername; + VALUE(QString) MySQLUsernameDef; + + KEY MySQLPassword; + VALUE(QString) MySQLPasswordDef; + + KEY MySQLPort; + VALUE(int) MySQLPortDef; + + KEY MySQLDatabase; + VALUE(char*) MySQLDatabaseDef; + + KEY ActiveDriver; + VALUE(char*) ActiveDriverDef; +} + +// Keyboard. +namespace Keyboard { + KEY ID; +} + +// Web browser. +namespace Browser { + KEY ID; + + KEY SendDNT; + VALUE(bool) SendDNTDef; + + KEY OpenLinksInExternalBrowserRightAway; + VALUE(bool) OpenLinksInExternalBrowserRightAwayDef; + + KEY CustomExternalBrowserEnabled; + VALUE(bool) CustomExternalBrowserEnabledDef; + + KEY CustomExternalBrowserExecutable; + VALUE(QString) CustomExternalBrowserExecutableDef; + + KEY CustomExternalBrowserArguments; + VALUE(char*) CustomExternalBrowserArgumentsDef; + + KEY CustomExternalEmailEnabled; + VALUE(bool) CustomExternalEmailEnabledDef; + + KEY CustomExternalEmailExecutable; + VALUE(QString) CustomExternalEmailExecutableDef; + + KEY CustomExternalEmailArguments; + VALUE(char*) CustomExternalEmailArgumentsDef; +} + +// Categories. +namespace CategoriesExpandStates { + KEY ID; +} + +class Settings : public QSettings { + Q_OBJECT + + public: + // Destructor. + virtual ~Settings(); + + // Type of used settings. + inline SettingsProperties::SettingsType type() const { + return m_initializationStatus; + } + + // Getters/setters for settings values. + inline QVariant value(const QString& section, const QString& key, const QVariant& default_value = QVariant()) const { + return QSettings::value(QString(QSL("%1/%2")).arg(section, key), default_value); + } + + inline void setValue(const QString& section, const QString& key, const QVariant& value) { + QSettings::setValue(QString(QSL("%1/%2")).arg(section, key), value); + } + + inline void setValue(const QString& key, const QVariant& value) { + QSettings::setValue(key, value); + } + + inline bool contains(const QString& section, const QString& key) const { + return QSettings::contains(QString(QSL("%1/%2")).arg(section, key)); + } + + inline void remove(const QString& section, const QString& key) { + QSettings::remove(QString(QSL("%1/%2")).arg(section, key)); + } + + // Returns the path which contains the settings. + QString pathName() const; + + // Synchronizes settings. + QSettings::Status checkSettings(); + + bool initiateRestoration(const QString& settings_backup_file_path); + static void finishRestoration(const QString& desired_settings_file_path); + + // Creates settings file in correct location. + static Settings* setupSettings(QObject* parent); + + // Returns properties of the actual application-wide settings. + static SettingsProperties determineProperties(); + + private: + // Constructor. + explicit Settings(const QString& file_name, Format format, const SettingsProperties::SettingsType& type, QObject* parent = 0); + + SettingsProperties::SettingsType m_initializationStatus; +}; + +#endif // SETTINGS_H diff --git a/src/miscellaneous/settingsproperties.h b/src/miscellaneous/settingsproperties.h index 2c7384299..9d2d96984 100755 --- a/src/miscellaneous/settingsproperties.h +++ b/src/miscellaneous/settingsproperties.h @@ -1,22 +1,22 @@ -#ifndef SETTINGSPROPERTIES_H -#define SETTINGSPROPERTIES_H - -#include - - -// Describes possible types of loaded settings. - -// Describes characteristics of settings. -struct SettingsProperties { - enum SettingsType { - Portable, - NonPortable - }; - - SettingsType m_type; - QString m_baseDirectory; - QString m_settingsSuffix; - QString m_absoluteSettingsFileName; -}; - -#endif // SETTINGSPROPERTIES_H +#ifndef SETTINGSPROPERTIES_H +#define SETTINGSPROPERTIES_H + +#include + + +// Describes possible types of loaded settings. + +// Describes characteristics of settings. +struct SettingsProperties { + enum SettingsType { + Portable, + NonPortable + }; + + SettingsType m_type; + QString m_baseDirectory; + QString m_settingsSuffix; + QString m_absoluteSettingsFileName; +}; + +#endif // SETTINGSPROPERTIES_H diff --git a/src/miscellaneous/simplecrypt/simplecrypt.cpp b/src/miscellaneous/simplecrypt/simplecrypt.cpp old mode 100644 new mode 100755 index 73e844959..a78584cf0 --- a/src/miscellaneous/simplecrypt/simplecrypt.cpp +++ b/src/miscellaneous/simplecrypt/simplecrypt.cpp @@ -1,246 +1,249 @@ -/* -Copyright (c) 2011, Andre Somers -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Rathenau Instituut, Andre Somers nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "miscellaneous/simplecrypt/simplecrypt.h" - -#include -#include -#include -#include -#include -#include - - -SimpleCrypt::SimpleCrypt(): - m_key(0), - m_compressionMode(CompressionAlways), - m_protectionMode(ProtectionHash), - m_lastError(ErrorNoError) { - qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); -} - -SimpleCrypt::SimpleCrypt(quint64 key): - m_key(key), - m_compressionMode(CompressionAlways), - m_protectionMode(ProtectionHash), - m_lastError(ErrorNoError) { - qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); - splitKey(); -} - -void SimpleCrypt::setKey(quint64 key) { - m_key = key; - splitKey(); -} - -void SimpleCrypt::splitKey() { - m_keyParts.clear(); - m_keyParts.resize(8); - for (int i=0;i<8;i++) { - quint64 part = m_key; - - for (int j=i; j>0; j--) { - part = part >> 8; - } - - part = part & 0xff; - m_keyParts[i] = static_cast(part); - } -} - -QByteArray SimpleCrypt::encryptToByteArray(const QString& plaintext) { - return encryptToByteArray(plaintext.toUtf8()); -} - -QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext) { - if (m_keyParts.isEmpty()) { - qWarning() << "No key set."; - m_lastError = ErrorNoKeySet; - return QByteArray(); - } - - QByteArray ba = plaintext; - CryptoFlags flags = CryptoFlagNone; - - if (m_compressionMode == CompressionAlways) { - ba = qCompress(ba, 9); //maximum compression - flags |= CryptoFlagCompression; - } - else if (m_compressionMode == CompressionAuto) { - QByteArray compressed = qCompress(ba, 9); - - if (compressed.count() < ba.count()) { - ba = compressed; - flags |= CryptoFlagCompression; - } - } - - QByteArray integrityProtection; - if (m_protectionMode == ProtectionChecksum) { - flags |= CryptoFlagChecksum; - QDataStream s(&integrityProtection, QIODevice::WriteOnly); - s << qChecksum(ba.constData(), ba.length()); - } - else if (m_protectionMode == ProtectionHash) { - flags |= CryptoFlagHash; - QCryptographicHash hash(QCryptographicHash::Sha1); - hash.addData(ba); - integrityProtection += hash.result(); - } - - //prepend a random char to the string - char randomChar = char(qrand() & 0xFF); - ba = randomChar + integrityProtection + ba; - - int pos(0); - char lastChar(0); - int cnt = ba.count(); - - while (pos < cnt) { - ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar; - lastChar = ba.at(pos); - ++pos; - } - - QByteArray resultArray; - resultArray.append(char(0x03)); //version for future updates to algorithm - resultArray.append(char(flags)); //encryption flags - resultArray.append(ba); - - m_lastError = ErrorNoError; - return resultArray; -} - -QString SimpleCrypt::encryptToString(const QString& plaintext) { - QByteArray plaintextArray = plaintext.toUtf8(); - QByteArray cypher = encryptToByteArray(plaintextArray); - QString cypherString = QString::fromLatin1(cypher.toBase64()); - return cypherString; -} - -QString SimpleCrypt::encryptToString(QByteArray plaintext) { - QByteArray cypher = encryptToByteArray(plaintext); - QString cypherString = QString::fromLatin1(cypher.toBase64()); - return cypherString; -} - -QString SimpleCrypt::decryptToString(const QString &cyphertext) { - QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); - QByteArray plaintextArray = decryptToByteArray(cyphertextArray); - QString plaintext = QString::fromUtf8(plaintextArray, plaintextArray.size()); - - return plaintext; -} - -QString SimpleCrypt::decryptToString(QByteArray cypher) { - QByteArray ba = decryptToByteArray(cypher); - QString plaintext = QString::fromUtf8(ba, ba.size()); - - return plaintext; -} - -QByteArray SimpleCrypt::decryptToByteArray(const QString& cyphertext) { - QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); - QByteArray ba = decryptToByteArray(cyphertextArray); - - return ba; -} - -QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher) { - if (m_keyParts.isEmpty()) { - qWarning() << "No key set."; - m_lastError = ErrorNoKeySet; - return QByteArray(); - } - - QByteArray ba = cypher; - - if (cypher.count() < 3) { - return QByteArray(); - } - - char version = ba.at(0); - - if (version !=3) { //we only work with version 3 - m_lastError = ErrorUnknownVersion; - qWarning() << "Invalid version or not a cyphertext."; - return QByteArray(); - } - - CryptoFlags flags = CryptoFlags(ba.at(1)); - - ba = ba.mid(2); - int pos(0); - int cnt(ba.count()); - char lastChar = 0; - - while (pos < cnt) { - char currentChar = ba[pos]; - ba[pos] = ba.at(pos) ^ lastChar ^ m_keyParts.at(pos % 8); - lastChar = currentChar; - ++pos; - } - - ba = ba.mid(1); //chop off the random number at the start - - bool integrityOk(true); - if (flags.testFlag(CryptoFlagChecksum)) { - if (ba.length() < 2) { - m_lastError = ErrorIntegrityFailed; - return QByteArray(); - } - quint16 storedChecksum; - { - QDataStream s(&ba, QIODevice::ReadOnly); - s >> storedChecksum; - } - ba = ba.mid(2); - quint16 checksum = qChecksum(ba.constData(), ba.length()); - integrityOk = (checksum == storedChecksum); - } else if (flags.testFlag(CryptoFlagHash)) { - if (ba.length() < 20) { - m_lastError = ErrorIntegrityFailed; - return QByteArray(); - } - QByteArray storedHash = ba.left(20); - ba = ba.mid(20); - QCryptographicHash hash(QCryptographicHash::Sha1); - hash.addData(ba); - integrityOk = (hash.result() == storedHash); - } - - if (!integrityOk) { - m_lastError = ErrorIntegrityFailed; - return QByteArray(); - } - - if (flags.testFlag(CryptoFlagCompression)) - ba = qUncompress(ba); - - m_lastError = ErrorNoError; - return ba; -} +/* +Copyright (c) 2011, Andre Somers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Rathenau Instituut, Andre Somers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "miscellaneous/simplecrypt/simplecrypt.h" + +#include +#include +#include +#include +#include +#include + + +SimpleCrypt::SimpleCrypt(): + m_key(0), + m_compressionMode(CompressionAlways), + m_protectionMode(ProtectionHash), + m_lastError(ErrorNoError) { + qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); +} + +SimpleCrypt::SimpleCrypt(quint64 key): + m_key(key), + m_compressionMode(CompressionAlways), + m_protectionMode(ProtectionHash), + m_lastError(ErrorNoError) { + qsrand(uint(QDateTime::currentMSecsSinceEpoch() & 0xFFFF)); + splitKey(); +} + +void SimpleCrypt::setKey(quint64 key) { + m_key = key; + splitKey(); +} + +void SimpleCrypt::splitKey() { + m_keyParts.clear(); + m_keyParts.resize(8); + + for (int i = 0; i < 8; i++) { + quint64 part = m_key; + + for (int j = i; j > 0; j--) { + part = part >> 8; + } + + part = part & 0xff; + m_keyParts[i] = static_cast(part); + } +} + +QByteArray SimpleCrypt::encryptToByteArray(const QString& plaintext) { + return encryptToByteArray(plaintext.toUtf8()); +} + +QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext) { + if (m_keyParts.isEmpty()) { + qWarning() << "No key set."; + m_lastError = ErrorNoKeySet; + return QByteArray(); + } + + QByteArray ba = plaintext; + CryptoFlags flags = CryptoFlagNone; + + if (m_compressionMode == CompressionAlways) { + ba = qCompress(ba, 9); //maximum compression + flags |= CryptoFlagCompression; + } + + else if (m_compressionMode == CompressionAuto) { + QByteArray compressed = qCompress(ba, 9); + + if (compressed.count() < ba.count()) { + ba = compressed; + flags |= CryptoFlagCompression; + } + } + + QByteArray integrityProtection; + + if (m_protectionMode == ProtectionChecksum) { + flags |= CryptoFlagChecksum; + QDataStream s(&integrityProtection, QIODevice::WriteOnly); + s << qChecksum(ba.constData(), ba.length()); + } + + else if (m_protectionMode == ProtectionHash) { + flags |= CryptoFlagHash; + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(ba); + integrityProtection += hash.result(); + } + + //prepend a random char to the string + char randomChar = char(qrand() & 0xFF); + ba = randomChar + integrityProtection + ba; + int pos(0); + char lastChar(0); + int cnt = ba.count(); + + while (pos < cnt) { + ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar; + lastChar = ba.at(pos); + ++pos; + } + + QByteArray resultArray; + resultArray.append(char(0x03)); //version for future updates to algorithm + resultArray.append(char(flags)); //encryption flags + resultArray.append(ba); + m_lastError = ErrorNoError; + return resultArray; +} + +QString SimpleCrypt::encryptToString(const QString& plaintext) { + QByteArray plaintextArray = plaintext.toUtf8(); + QByteArray cypher = encryptToByteArray(plaintextArray); + QString cypherString = QString::fromLatin1(cypher.toBase64()); + return cypherString; +} + +QString SimpleCrypt::encryptToString(QByteArray plaintext) { + QByteArray cypher = encryptToByteArray(plaintext); + QString cypherString = QString::fromLatin1(cypher.toBase64()); + return cypherString; +} + +QString SimpleCrypt::decryptToString(const QString& cyphertext) { + QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); + QByteArray plaintextArray = decryptToByteArray(cyphertextArray); + QString plaintext = QString::fromUtf8(plaintextArray, plaintextArray.size()); + return plaintext; +} + +QString SimpleCrypt::decryptToString(QByteArray cypher) { + QByteArray ba = decryptToByteArray(cypher); + QString plaintext = QString::fromUtf8(ba, ba.size()); + return plaintext; +} + +QByteArray SimpleCrypt::decryptToByteArray(const QString& cyphertext) { + QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); + QByteArray ba = decryptToByteArray(cyphertextArray); + return ba; +} + +QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher) { + if (m_keyParts.isEmpty()) { + qWarning() << "No key set."; + m_lastError = ErrorNoKeySet; + return QByteArray(); + } + + QByteArray ba = cypher; + + if (cypher.count() < 3) { + return QByteArray(); + } + + char version = ba.at(0); + + if (version != 3) { //we only work with version 3 + m_lastError = ErrorUnknownVersion; + qWarning() << "Invalid version or not a cyphertext."; + return QByteArray(); + } + + CryptoFlags flags = CryptoFlags(ba.at(1)); + ba = ba.mid(2); + int pos(0); + int cnt(ba.count()); + char lastChar = 0; + + while (pos < cnt) { + char currentChar = ba[pos]; + ba[pos] = ba.at(pos) ^ lastChar ^ m_keyParts.at(pos % 8); + lastChar = currentChar; + ++pos; + } + + ba = ba.mid(1); //chop off the random number at the start + bool integrityOk(true); + + if (flags.testFlag(CryptoFlagChecksum)) { + if (ba.length() < 2) { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + + quint16 storedChecksum; + { + QDataStream s(&ba, QIODevice::ReadOnly); + s >> storedChecksum; + } + ba = ba.mid(2); + quint16 checksum = qChecksum(ba.constData(), ba.length()); + integrityOk = (checksum == storedChecksum); + } + + else if (flags.testFlag(CryptoFlagHash)) { + if (ba.length() < 20) { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + + QByteArray storedHash = ba.left(20); + ba = ba.mid(20); + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(ba); + integrityOk = (hash.result() == storedHash); + } + + if (!integrityOk) { + m_lastError = ErrorIntegrityFailed; + return QByteArray(); + } + + if (flags.testFlag(CryptoFlagCompression)) { + ba = qUncompress(ba); + } + + m_lastError = ErrorNoError; + return ba; +} diff --git a/src/miscellaneous/simplecrypt/simplecrypt.h b/src/miscellaneous/simplecrypt/simplecrypt.h old mode 100644 new mode 100755 index fc0c2412b..ede64995b --- a/src/miscellaneous/simplecrypt/simplecrypt.h +++ b/src/miscellaneous/simplecrypt/simplecrypt.h @@ -1,227 +1,239 @@ -/* -Copyright (c) 2011, Andre Somers -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Rathenau Instituut, Andre Somers nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef SIMPLECRYPT_H -#define SIMPLECRYPT_H - -#include -#include -#include - - -/** - @short Simple encryption and decryption of strings and byte arrays - - This class provides a simple implementation of encryption and decryption - of strings and byte arrays. - - @warning The encryption provided by this class is NOT strong encryption. It may - help to shield things from curious eyes, but it will NOT stand up to someone - determined to break the encryption. Don't say you were not warned. - - The class uses a 64 bit key. Simply create an instance of the class, set the key, - and use the encryptToString() method to calculate an encrypted version of the input string. - To decrypt that string again, use an instance of SimpleCrypt initialized with - the same key, and call the decryptToString() method with the encrypted string. If the key - matches, the decrypted version of the string will be returned again. - - If you do not provide a key, or if something else is wrong, the encryption and - decryption function will return an empty string or will return a string containing nonsense. - lastError() will return a value indicating if the method was succesful, and if not, why not. - - SimpleCrypt is prepared for the case that the encryption and decryption - algorithm is changed in a later version, by prepending a version identifier to the cypertext. - */ -class SimpleCrypt { - public: - /** - CompressionMode describes if compression will be applied to the data to be - encrypted. - */ - enum CompressionMode { - CompressionAuto, /*!< Only apply compression if that results in a shorter plaintext. */ - CompressionAlways, /*!< Always apply compression. Note that for short inputs, a compression may result in longer data */ - CompressionNever /*!< Never apply compression. */ - }; - /** - IntegrityProtectionMode describes measures taken to make it possible to detect problems with the data - or wrong decryption keys. - - Measures involve adding a checksum or a cryptograhpic hash to the data to be encrypted. This - increases the length of the resulting cypertext, but makes it possible to check if the plaintext - appears to be valid after decryption. - */ - enum IntegrityProtectionMode { - ProtectionNone, /*!< The integerity of the encrypted data is not protected. It is not really possible to detect a wrong key, for instance. */ - ProtectionChecksum,/*!< A simple checksum is used to verify that the data is in order. If not, an empty string is returned. */ - ProtectionHash /*!< A cryptographic hash is used to verify the integrity of the data. This method produces a much stronger, but longer check */ - }; - /** - Error describes the type of error that occured. - */ - enum Error { - ErrorNoError, /*!< No error occurred. */ - ErrorNoKeySet, /*!< No key was set. You can not encrypt or decrypt without a valid key. */ - ErrorUnknownVersion, /*!< The version of this data is unknown, or the data is otherwise not valid. */ - ErrorIntegrityFailed /*!< The integrity check of the data failed. Perhaps the wrong key was used. */ - }; - - /** - Constructor. - - Constructs a SimpleCrypt instance without a valid key set on it. - */ - SimpleCrypt(); - /** - Constructor. - - Constructs a SimpleCrypt instance and initializes it with the given @arg key. - */ - explicit SimpleCrypt(quint64 key); - - /** - (Re-) initializes the key with the given @arg key. - */ - void setKey(quint64 key); - /** - Returns true if SimpleCrypt has been initialized with a key. - */ - bool hasKey() const {return !m_keyParts.isEmpty();} - - /** - Sets the compression mode to use when encrypting data. The default mode is Auto. - - Note that decryption is not influenced by this mode, as the decryption recognizes - what mode was used when encrypting. - */ - void setCompressionMode(CompressionMode mode) {m_compressionMode = mode;} - /** - Returns the CompressionMode that is currently in use. - */ - CompressionMode compressionMode() const {return m_compressionMode;} - - /** - Sets the integrity mode to use when encrypting data. The default mode is Checksum. - - Note that decryption is not influenced by this mode, as the decryption recognizes - what mode was used when encrypting. - */ - void setIntegrityProtectionMode(IntegrityProtectionMode mode) {m_protectionMode = mode;} - /** - Returns the IntegrityProtectionMode that is currently in use. - */ - IntegrityProtectionMode integrityProtectionMode() const {return m_protectionMode;} - - /** - Returns the last error that occurred. - */ - Error lastError() const {return m_lastError;} - - /** - Encrypts the @arg plaintext string with the key the class was initialized with, and returns - a cyphertext the result. The result is a base64 encoded version of the binary array that is the - actual result of the string, so it can be stored easily in a text format. - */ - QString encryptToString(const QString& plaintext) ; - /** - Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns - a cyphertext the result. The result is a base64 encoded version of the binary array that is the - actual result of the encryption, so it can be stored easily in a text format. - */ - QString encryptToString(QByteArray plaintext) ; - /** - Encrypts the @arg plaintext string with the key the class was initialized with, and returns - a binary cyphertext in a QByteArray the result. - - This method returns a byte array, that is useable for storing a binary format. If you need - a string you can store in a text file, use encryptToString() instead. - */ - QByteArray encryptToByteArray(const QString& plaintext) ; - /** - Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns - a binary cyphertext in a QByteArray the result. - - This method returns a byte array, that is useable for storing a binary format. If you need - a string you can store in a text file, use encryptToString() instead. - */ - QByteArray encryptToByteArray(QByteArray plaintext) ; - - /** - Decrypts a cyphertext string encrypted with this class with the set key back to the - plain text version. - - If an error occured, such as non-matching keys between encryption and decryption, - an empty string or a string containing nonsense may be returned. - */ - QString decryptToString(const QString& cyphertext) ; - /** - Decrypts a cyphertext string encrypted with this class with the set key back to the - plain text version. - - If an error occured, such as non-matching keys between encryption and decryption, - an empty string or a string containing nonsense may be returned. - */ - QByteArray decryptToByteArray(const QString& cyphertext) ; - /** - Decrypts a cyphertext binary encrypted with this class with the set key back to the - plain text version. - - If an error occured, such as non-matching keys between encryption and decryption, - an empty string or a string containing nonsense may be returned. - */ - QString decryptToString(QByteArray cypher) ; - /** - Decrypts a cyphertext binary encrypted with this class with the set key back to the - plain text version. - - If an error occured, such as non-matching keys between encryption and decryption, - an empty string or a string containing nonsense may be returned. - */ - QByteArray decryptToByteArray(QByteArray cypher) ; - - //enum to describe options that have been used for the encryption. Currently only one, but - //that only leaves room for future extensions like adding a cryptographic hash... - enum CryptoFlag{CryptoFlagNone = 0, - CryptoFlagCompression = 0x01, - CryptoFlagChecksum = 0x02, - CryptoFlagHash = 0x04 - }; - - Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag) - - private: - void splitKey(); - - quint64 m_key; - QVector m_keyParts; - CompressionMode m_compressionMode; - IntegrityProtectionMode m_protectionMode; - Error m_lastError; -}; -Q_DECLARE_OPERATORS_FOR_FLAGS(SimpleCrypt::CryptoFlags) - -#endif // SimpleCrypt_H +/* +Copyright (c) 2011, Andre Somers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Rathenau Instituut, Andre Somers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SIMPLECRYPT_H +#define SIMPLECRYPT_H + +#include +#include +#include + + +/** + @short Simple encryption and decryption of strings and byte arrays + + This class provides a simple implementation of encryption and decryption + of strings and byte arrays. + + @warning The encryption provided by this class is NOT strong encryption. It may + help to shield things from curious eyes, but it will NOT stand up to someone + determined to break the encryption. Don't say you were not warned. + + The class uses a 64 bit key. Simply create an instance of the class, set the key, + and use the encryptToString() method to calculate an encrypted version of the input string. + To decrypt that string again, use an instance of SimpleCrypt initialized with + the same key, and call the decryptToString() method with the encrypted string. If the key + matches, the decrypted version of the string will be returned again. + + If you do not provide a key, or if something else is wrong, the encryption and + decryption function will return an empty string or will return a string containing nonsense. + lastError() will return a value indicating if the method was succesful, and if not, why not. + + SimpleCrypt is prepared for the case that the encryption and decryption + algorithm is changed in a later version, by prepending a version identifier to the cypertext. + */ +class SimpleCrypt { + public: + /** + CompressionMode describes if compression will be applied to the data to be + encrypted. + */ + enum CompressionMode { + CompressionAuto, /*!< Only apply compression if that results in a shorter plaintext. */ + CompressionAlways, /*!< Always apply compression. Note that for short inputs, a compression may result in longer data */ + CompressionNever /*!< Never apply compression. */ + }; + /** + IntegrityProtectionMode describes measures taken to make it possible to detect problems with the data + or wrong decryption keys. + + Measures involve adding a checksum or a cryptograhpic hash to the data to be encrypted. This + increases the length of the resulting cypertext, but makes it possible to check if the plaintext + appears to be valid after decryption. + */ + enum IntegrityProtectionMode { + ProtectionNone, /*!< The integerity of the encrypted data is not protected. It is not really possible to detect a wrong key, for instance. */ + ProtectionChecksum,/*!< A simple checksum is used to verify that the data is in order. If not, an empty string is returned. */ + ProtectionHash /*!< A cryptographic hash is used to verify the integrity of the data. This method produces a much stronger, but longer check */ + }; + /** + Error describes the type of error that occured. + */ + enum Error { + ErrorNoError, /*!< No error occurred. */ + ErrorNoKeySet, /*!< No key was set. You can not encrypt or decrypt without a valid key. */ + ErrorUnknownVersion, /*!< The version of this data is unknown, or the data is otherwise not valid. */ + ErrorIntegrityFailed /*!< The integrity check of the data failed. Perhaps the wrong key was used. */ + }; + + /** + Constructor. + + Constructs a SimpleCrypt instance without a valid key set on it. + */ + SimpleCrypt(); + /** + Constructor. + + Constructs a SimpleCrypt instance and initializes it with the given @arg key. + */ + explicit SimpleCrypt(quint64 key); + + /** + (Re-) initializes the key with the given @arg key. + */ + void setKey(quint64 key); + /** + Returns true if SimpleCrypt has been initialized with a key. + */ + bool hasKey() const { + return !m_keyParts.isEmpty(); + } + + /** + Sets the compression mode to use when encrypting data. The default mode is Auto. + + Note that decryption is not influenced by this mode, as the decryption recognizes + what mode was used when encrypting. + */ + void setCompressionMode(CompressionMode mode) { + m_compressionMode = mode; + } + /** + Returns the CompressionMode that is currently in use. + */ + CompressionMode compressionMode() const { + return m_compressionMode; + } + + /** + Sets the integrity mode to use when encrypting data. The default mode is Checksum. + + Note that decryption is not influenced by this mode, as the decryption recognizes + what mode was used when encrypting. + */ + void setIntegrityProtectionMode(IntegrityProtectionMode mode) { + m_protectionMode = mode; + } + /** + Returns the IntegrityProtectionMode that is currently in use. + */ + IntegrityProtectionMode integrityProtectionMode() const { + return m_protectionMode; + } + + /** + Returns the last error that occurred. + */ + Error lastError() const { + return m_lastError; + } + + /** + Encrypts the @arg plaintext string with the key the class was initialized with, and returns + a cyphertext the result. The result is a base64 encoded version of the binary array that is the + actual result of the string, so it can be stored easily in a text format. + */ + QString encryptToString(const QString& plaintext) ; + /** + Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns + a cyphertext the result. The result is a base64 encoded version of the binary array that is the + actual result of the encryption, so it can be stored easily in a text format. + */ + QString encryptToString(QByteArray plaintext) ; + /** + Encrypts the @arg plaintext string with the key the class was initialized with, and returns + a binary cyphertext in a QByteArray the result. + + This method returns a byte array, that is useable for storing a binary format. If you need + a string you can store in a text file, use encryptToString() instead. + */ + QByteArray encryptToByteArray(const QString& plaintext) ; + /** + Encrypts the @arg plaintext QByteArray with the key the class was initialized with, and returns + a binary cyphertext in a QByteArray the result. + + This method returns a byte array, that is useable for storing a binary format. If you need + a string you can store in a text file, use encryptToString() instead. + */ + QByteArray encryptToByteArray(QByteArray plaintext) ; + + /** + Decrypts a cyphertext string encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QString decryptToString(const QString& cyphertext) ; + /** + Decrypts a cyphertext string encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QByteArray decryptToByteArray(const QString& cyphertext) ; + /** + Decrypts a cyphertext binary encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QString decryptToString(QByteArray cypher) ; + /** + Decrypts a cyphertext binary encrypted with this class with the set key back to the + plain text version. + + If an error occured, such as non-matching keys between encryption and decryption, + an empty string or a string containing nonsense may be returned. + */ + QByteArray decryptToByteArray(QByteArray cypher) ; + + //enum to describe options that have been used for the encryption. Currently only one, but + //that only leaves room for future extensions like adding a cryptographic hash... + enum CryptoFlag {CryptoFlagNone = 0, + CryptoFlagCompression = 0x01, + CryptoFlagChecksum = 0x02, + CryptoFlagHash = 0x04 + }; + + Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag) + + private: + void splitKey(); + + quint64 m_key; + QVector m_keyParts; + CompressionMode m_compressionMode; + IntegrityProtectionMode m_protectionMode; + Error m_lastError; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(SimpleCrypt::CryptoFlags) + +#endif // SimpleCrypt_H diff --git a/src/miscellaneous/simpleregexp.cpp b/src/miscellaneous/simpleregexp.cpp index ad617844c..ae3944883 100755 --- a/src/miscellaneous/simpleregexp.cpp +++ b/src/miscellaneous/simpleregexp.cpp @@ -20,58 +20,61 @@ SimpleRegExp::SimpleRegExp() - : QRegularExpression(QString(), QRegularExpression::DotMatchesEverythingOption), m_matchedLength(-1) { + : QRegularExpression(QString(), QRegularExpression::DotMatchesEverythingOption), m_matchedLength(-1) { } -SimpleRegExp::SimpleRegExp(const QString &pattern, Qt::CaseSensitivity cs) - : QRegularExpression(pattern, QRegularExpression::DotMatchesEverythingOption), m_matchedLength(-1) { - if (cs == Qt::CaseInsensitive) { - setPatternOptions(patternOptions() | QRegularExpression::CaseInsensitiveOption); - } +SimpleRegExp::SimpleRegExp(const QString& pattern, Qt::CaseSensitivity cs) + : QRegularExpression(pattern, QRegularExpression::DotMatchesEverythingOption), m_matchedLength(-1) { + if (cs == Qt::CaseInsensitive) { + setPatternOptions(patternOptions() | QRegularExpression::CaseInsensitiveOption); + } } -SimpleRegExp::SimpleRegExp(const SimpleRegExp &re) - : QRegularExpression(re) , m_matchedLength(-1) { +SimpleRegExp::SimpleRegExp(const SimpleRegExp& re) + : QRegularExpression(re), m_matchedLength(-1) { } void SimpleRegExp::setMinimal(bool minimal) { - QRegularExpression::PatternOptions opt; + QRegularExpression::PatternOptions opt; - if (minimal) { - opt = patternOptions() | QRegularExpression::InvertedGreedinessOption; - } - else { - opt = patternOptions() & ~QRegularExpression::InvertedGreedinessOption; - } + if (minimal) { + opt = patternOptions() | QRegularExpression::InvertedGreedinessOption; + } - setPatternOptions(opt); + else { + opt = patternOptions() & ~QRegularExpression::InvertedGreedinessOption; + } + + setPatternOptions(opt); } -int SimpleRegExp::indexIn(const QString &str, int offset) const { - SimpleRegExp *that = const_cast(this); - QRegularExpressionMatch m = match(str, offset); +int SimpleRegExp::indexIn(const QString& str, int offset) const { + SimpleRegExp* that = const_cast(this); + QRegularExpressionMatch m = match(str, offset); - if (!m.hasMatch()) { - that->m_matchedLength = -1; - that->m_capturedTexts.clear(); - return -1; - } - else { - that->m_matchedLength = m.capturedLength(); - that->m_capturedTexts = m.capturedTexts(); - return m.capturedStart(); - } + if (!m.hasMatch()) { + that->m_matchedLength = -1; + that->m_capturedTexts.clear(); + return -1; + } + + else { + that->m_matchedLength = m.capturedLength(); + that->m_capturedTexts = m.capturedTexts(); + return m.capturedStart(); + } } int SimpleRegExp::matchedLength() const { - return m_matchedLength; + return m_matchedLength; } QString SimpleRegExp::cap(int nth) const { - if (nth >= 0 && m_capturedTexts.size() > nth) { - return m_capturedTexts.at(nth); - } - else { - return QString(); - } + if (nth >= 0 && m_capturedTexts.size() > nth) { + return m_capturedTexts.at(nth); + } + + else { + return QString(); + } } diff --git a/src/miscellaneous/simpleregexp.h b/src/miscellaneous/simpleregexp.h index 3ccbe8d59..a65db8245 100755 --- a/src/miscellaneous/simpleregexp.h +++ b/src/miscellaneous/simpleregexp.h @@ -24,19 +24,19 @@ class SimpleRegExp : public QRegularExpression { - public: - explicit SimpleRegExp(); - explicit SimpleRegExp(const QString &pattern, Qt::CaseSensitivity cs = Qt::CaseSensitive); - explicit SimpleRegExp(const SimpleRegExp &re); + public: + explicit SimpleRegExp(); + explicit SimpleRegExp(const QString& pattern, Qt::CaseSensitivity cs = Qt::CaseSensitive); + explicit SimpleRegExp(const SimpleRegExp& re); - void setMinimal(bool minimal); - int indexIn(const QString &str, int offset = 0) const; - int matchedLength() const; - QString cap(int nth = 0) const; + void setMinimal(bool minimal); + int indexIn(const QString& str, int offset = 0) const; + int matchedLength() const; + QString cap(int nth = 0) const; - private: - QStringList m_capturedTexts; - int m_matchedLength; + private: + QStringList m_capturedTexts; + int m_matchedLength; }; #endif // SIMPLEREGEXP_H diff --git a/src/miscellaneous/skinfactory.cpp b/src/miscellaneous/skinfactory.cpp index 960853209..e5c7945bf 100755 --- a/src/miscellaneous/skinfactory.cpp +++ b/src/miscellaneous/skinfactory.cpp @@ -1,188 +1,171 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "miscellaneous/skinfactory.h" - -#include "miscellaneous/application.h" - -#include -#include -#include -#include - - -SkinFactory::SkinFactory(QObject *parent) : QObject(parent) { -} - -SkinFactory::~SkinFactory() { -} - -void SkinFactory::loadCurrentSkin() { - QList skin_names_to_try; - - skin_names_to_try.append(selectedSkinName()); - skin_names_to_try.append(APP_SKIN_DEFAULT); - - bool skin_parsed; - Skin skin_data; - QString skin_name; - - while (!skin_names_to_try.isEmpty()) { - skin_name = skin_names_to_try.takeFirst(); - skin_data = skinInfo(skin_name, &skin_parsed); - - if (skin_parsed) { - loadSkinFromData(skin_data); - - // Set this 'Skin' object as active one. - m_currentSkin = skin_data; - - qDebug("Skin '%s' loaded.", qPrintable(skin_name)); - return; - } - else { - qWarning("Failed to load skin '%s'.", qPrintable(skin_name)); - } - } - - qCritical("Failed to load selected or default skin. Quitting!"); -} - -void SkinFactory::loadSkinFromData(const Skin &skin) { - if (!skin.m_rawData.isEmpty()) { - qApp->setStyleSheet(skin.m_rawData); - } - - qApp->setStyle(qApp->settings()->value(GROUP(GUI), SETTING(GUI::Style)).toString()); -} - -void SkinFactory::setCurrentSkinName(const QString &skin_name) { - qApp->settings()->setValue(GROUP(GUI), GUI::Skin, skin_name); -} - -QString SkinFactory::getUserSkinBaseFolder() const { - return qApp->getUserDataPath() + QDir::separator() + APP_SKIN_USER_FOLDER; -} - -QString SkinFactory::selectedSkinName() const { - return qApp->settings()->value(GROUP(GUI), SETTING(GUI::Skin)).toString(); -} - -Skin SkinFactory::skinInfo(const QString &skin_name, bool *ok) const { - Skin skin; - QStringList base_skin_folders; - - base_skin_folders.append(APP_SKIN_PATH); - base_skin_folders.append(getUserSkinBaseFolder()); - - while (!base_skin_folders.isEmpty()) { - const QString skin_folder = base_skin_folders.takeAt(0) + QDir::separator() + skin_name + QDir::separator(); - const QString metadata_file = skin_folder + APP_SKIN_METADATA_FILE; - - if (QFile::exists(metadata_file)) { - QFile skin_file(metadata_file); - - QDomDocument dokument; - - if (!skin_file.open(QIODevice::Text | QIODevice::ReadOnly) || !dokument.setContent(&skin_file, true)) { - if (ok) { - *ok = false; - } - - return skin; - } - - const QDomNode skin_node = dokument.namedItem(QSL("skin")); - - // Obtain visible skin name. - skin.m_visibleName = skin_name; - - // Obtain author. - skin.m_author = skin_node.namedItem(QSL("author")).namedItem(QSL("name")).toElement().text(); - - // Obtain email. - skin.m_email = skin_node.namedItem(QSL("author")).namedItem(QSL("email")).toElement().text(); - - // Obtain version. - skin.m_version = skin_node.attributes().namedItem(QSL("version")).toAttr().value(); - - // Obtain other information. - skin.m_baseName = skin_name; - - // Free resources. - skin_file.close(); - skin_file.deleteLater(); - - // Here we use "/" instead of QDir::separator() because CSS2.1 url field - // accepts '/' as path elements separator. - // - // "##" is placeholder for the actual path to skin file. This is needed for using - // images within the QSS file. - // So if one uses "##/images/border.png" in QSS then it is - // replaced by fully absolute path and target file can - // be safely loaded. - - skin.m_layoutMarkupWrapper = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_wrapper.html"))); - skin.m_layoutMarkupWrapper = skin.m_layoutMarkupWrapper.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); - - skin.m_enclosureImageMarkup = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_enclosure_image.html"))); - skin.m_enclosureImageMarkup = skin.m_enclosureImageMarkup.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); - - skin.m_layoutMarkup = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_single_message.html"))); - skin.m_layoutMarkup = skin.m_layoutMarkup.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); - - skin.m_enclosureMarkup = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_enclosure_every.html"))); - skin.m_enclosureMarkup = skin.m_enclosureMarkup.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); - - skin.m_rawData = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("theme.css"))); - skin.m_rawData = skin.m_rawData.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); - - if (ok != nullptr) { - *ok = !skin.m_author.isEmpty() && !skin.m_version.isEmpty() && - !skin.m_baseName.isEmpty() && !skin.m_email.isEmpty() && - !skin.m_layoutMarkup.isEmpty(); - } - - break; - } - } - - return skin; -} - -QList SkinFactory::installedSkins() const { - QList skins; - bool skin_load_ok; - QStringList skin_directories = QDir(APP_SKIN_PATH).entryList(QDir::Dirs | - QDir::NoDotAndDotDot | - QDir::NoSymLinks | - QDir::Readable); - skin_directories.append(QDir(getUserSkinBaseFolder()).entryList(QDir::Dirs | - QDir::NoDotAndDotDot | - QDir::NoSymLinks | - QDir::Readable)); - - foreach (const QString &base_directory, skin_directories) { - const Skin skin_info = skinInfo(base_directory, &skin_load_ok); - - if (skin_load_ok) { - skins.append(skin_info); - } - } - - return skins; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "miscellaneous/skinfactory.h" + +#include "miscellaneous/application.h" + +#include +#include +#include +#include + + +SkinFactory::SkinFactory(QObject* parent) : QObject(parent) { +} + +SkinFactory::~SkinFactory() { +} + +void SkinFactory::loadCurrentSkin() { + QList skin_names_to_try; + skin_names_to_try.append(selectedSkinName()); + skin_names_to_try.append(APP_SKIN_DEFAULT); + bool skin_parsed; + Skin skin_data; + QString skin_name; + + while (!skin_names_to_try.isEmpty()) { + skin_name = skin_names_to_try.takeFirst(); + skin_data = skinInfo(skin_name, &skin_parsed); + + if (skin_parsed) { + loadSkinFromData(skin_data); + // Set this 'Skin' object as active one. + m_currentSkin = skin_data; + qDebug("Skin '%s' loaded.", qPrintable(skin_name)); + return; + } + + else { + qWarning("Failed to load skin '%s'.", qPrintable(skin_name)); + } + } + + qCritical("Failed to load selected or default skin. Quitting!"); +} + +void SkinFactory::loadSkinFromData(const Skin& skin) { + if (!skin.m_rawData.isEmpty()) { + qApp->setStyleSheet(skin.m_rawData); + } + + qApp->setStyle(qApp->settings()->value(GROUP(GUI), SETTING(GUI::Style)).toString()); +} + +void SkinFactory::setCurrentSkinName(const QString& skin_name) { + qApp->settings()->setValue(GROUP(GUI), GUI::Skin, skin_name); +} + +QString SkinFactory::getUserSkinBaseFolder() const { + return qApp->getUserDataPath() + QDir::separator() + APP_SKIN_USER_FOLDER; +} + +QString SkinFactory::selectedSkinName() const { + return qApp->settings()->value(GROUP(GUI), SETTING(GUI::Skin)).toString(); +} + +Skin SkinFactory::skinInfo(const QString& skin_name, bool* ok) const { + Skin skin; + QStringList base_skin_folders; + base_skin_folders.append(APP_SKIN_PATH); + base_skin_folders.append(getUserSkinBaseFolder()); + + while (!base_skin_folders.isEmpty()) { + const QString skin_folder = base_skin_folders.takeAt(0) + QDir::separator() + skin_name + QDir::separator(); + const QString metadata_file = skin_folder + APP_SKIN_METADATA_FILE; + + if (QFile::exists(metadata_file)) { + QFile skin_file(metadata_file); + QDomDocument dokument; + + if (!skin_file.open(QIODevice::Text | QIODevice::ReadOnly) || !dokument.setContent(&skin_file, true)) { + if (ok) { + *ok = false; + } + + return skin; + } + + const QDomNode skin_node = dokument.namedItem(QSL("skin")); + // Obtain visible skin name. + skin.m_visibleName = skin_name; + // Obtain author. + skin.m_author = skin_node.namedItem(QSL("author")).namedItem(QSL("name")).toElement().text(); + // Obtain email. + skin.m_email = skin_node.namedItem(QSL("author")).namedItem(QSL("email")).toElement().text(); + // Obtain version. + skin.m_version = skin_node.attributes().namedItem(QSL("version")).toAttr().value(); + // Obtain other information. + skin.m_baseName = skin_name; + // Free resources. + skin_file.close(); + skin_file.deleteLater(); + // Here we use "/" instead of QDir::separator() because CSS2.1 url field + // accepts '/' as path elements separator. + // + // "##" is placeholder for the actual path to skin file. This is needed for using + // images within the QSS file. + // So if one uses "##/images/border.png" in QSS then it is + // replaced by fully absolute path and target file can + // be safely loaded. + skin.m_layoutMarkupWrapper = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_wrapper.html"))); + skin.m_layoutMarkupWrapper = skin.m_layoutMarkupWrapper.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); + skin.m_enclosureImageMarkup = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_enclosure_image.html"))); + skin.m_enclosureImageMarkup = skin.m_enclosureImageMarkup.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); + skin.m_layoutMarkup = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_single_message.html"))); + skin.m_layoutMarkup = skin.m_layoutMarkup.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); + skin.m_enclosureMarkup = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("html_enclosure_every.html"))); + skin.m_enclosureMarkup = skin.m_enclosureMarkup.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); + skin.m_rawData = QString::fromUtf8(IOFactory::readTextFile(skin_folder + QL1S("theme.css"))); + skin.m_rawData = skin.m_rawData.replace(QSL("##"), APP_SKIN_PATH + QL1S("/") + skin_name); + + if (ok != nullptr) { + *ok = !skin.m_author.isEmpty() && !skin.m_version.isEmpty() && + !skin.m_baseName.isEmpty() && !skin.m_email.isEmpty() && + !skin.m_layoutMarkup.isEmpty(); + } + + break; + } + } + + return skin; +} + +QList SkinFactory::installedSkins() const { + QList skins; + bool skin_load_ok; + QStringList skin_directories = QDir(APP_SKIN_PATH).entryList(QDir::Dirs | + QDir::NoDotAndDotDot | + QDir::NoSymLinks | + QDir::Readable); + skin_directories.append(QDir(getUserSkinBaseFolder()).entryList(QDir::Dirs | + QDir::NoDotAndDotDot | + QDir::NoSymLinks | + QDir::Readable)); + + foreach (const QString& base_directory, skin_directories) { + const Skin skin_info = skinInfo(base_directory, &skin_load_ok); + + if (skin_load_ok) { + skins.append(skin_info); + } + } + + return skins; +} diff --git a/src/miscellaneous/skinfactory.h b/src/miscellaneous/skinfactory.h index ba15d34f0..8bb5f5261 100755 --- a/src/miscellaneous/skinfactory.h +++ b/src/miscellaneous/skinfactory.h @@ -1,83 +1,83 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SKINFACTORY_H -#define SKINFACTORY_H - -#include - -#include -#include - - -struct Skin { - QString m_baseName; - QString m_visibleName; - QString m_author; - QString m_email; - QString m_version; - QString m_rawData; - QString m_layoutMarkupWrapper; - QString m_enclosureImageMarkup; - QString m_layoutMarkup; - QString m_enclosureMarkup; -}; - -Q_DECLARE_METATYPE(Skin) - -class SkinFactory : public QObject { - Q_OBJECT - - public: - // Constructor. - explicit SkinFactory(QObject *parent = 0); - - // Destructor. - virtual ~SkinFactory(); - - // Loads skin name from settings and sets it as active. - void loadCurrentSkin(); - - inline Skin currentSkin() const { - return m_currentSkin; - } - - // Returns the name of the skin, that should be activated - // after application restart. - QString selectedSkinName() const; - - // Gets skin about a particular skin. - Skin skinInfo(const QString &skin_name, bool *ok = nullptr) const; - - // Returns list of installed skins. - QList installedSkins() const; - - // Sets the desired skin as the active one if it exists. - void setCurrentSkinName(const QString &skin_name); - - QString getUserSkinBaseFolder() const; - - private: - - // Loads the skin from give skin_data. - void loadSkinFromData(const Skin &skin); - - // Holds name of the current skin. - Skin m_currentSkin; -}; - -#endif // SKINFACTORY_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SKINFACTORY_H +#define SKINFACTORY_H + +#include + +#include +#include + + +struct Skin { + QString m_baseName; + QString m_visibleName; + QString m_author; + QString m_email; + QString m_version; + QString m_rawData; + QString m_layoutMarkupWrapper; + QString m_enclosureImageMarkup; + QString m_layoutMarkup; + QString m_enclosureMarkup; +}; + +Q_DECLARE_METATYPE(Skin) + +class SkinFactory : public QObject { + Q_OBJECT + + public: + // Constructor. + explicit SkinFactory(QObject* parent = 0); + + // Destructor. + virtual ~SkinFactory(); + + // Loads skin name from settings and sets it as active. + void loadCurrentSkin(); + + inline Skin currentSkin() const { + return m_currentSkin; + } + + // Returns the name of the skin, that should be activated + // after application restart. + QString selectedSkinName() const; + + // Gets skin about a particular skin. + Skin skinInfo(const QString& skin_name, bool* ok = nullptr) const; + + // Returns list of installed skins. + QList installedSkins() const; + + // Sets the desired skin as the active one if it exists. + void setCurrentSkinName(const QString& skin_name); + + QString getUserSkinBaseFolder() const; + + private: + + // Loads the skin from give skin_data. + void loadSkinFromData(const Skin& skin); + + // Holds name of the current skin. + Skin m_currentSkin; +}; + +#endif // SKINFACTORY_H diff --git a/src/miscellaneous/systemfactory.cpp b/src/miscellaneous/systemfactory.cpp index 96f3f2cd4..6973355d9 100755 --- a/src/miscellaneous/systemfactory.cpp +++ b/src/miscellaneous/systemfactory.cpp @@ -40,274 +40,272 @@ typedef QPair UpdateCheck; -SystemFactory::SystemFactory(QObject *parent) : QObject(parent) { +SystemFactory::SystemFactory(QObject* parent) : QObject(parent) { } SystemFactory::~SystemFactory() { } SystemFactory::AutoStartStatus SystemFactory::getAutoStartStatus() const { - // User registry way to auto-start the application on Windows. + // User registry way to auto-start the application on Windows. #if defined(Q_OS_WIN) - QSettings registry_key(QSL("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"), - QSettings::NativeFormat); - const bool autostart_enabled = registry_key.value(QSL(APP_LOW_NAME), - QString()).toString().replace(QL1C('\\'), - QL1C('/')) == - Application::applicationFilePath(); + QSettings registry_key(QSL("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"), + QSettings::NativeFormat); + const bool autostart_enabled = registry_key.value(QSL(APP_LOW_NAME), + QString()).toString().replace(QL1C('\\'), + QL1C('/')) == + Application::applicationFilePath(); - if (autostart_enabled) { - return SystemFactory::Enabled; - } - else { - return SystemFactory::Disabled; - } + if (autostart_enabled) { + return SystemFactory::Enabled; + } + + else { + return SystemFactory::Disabled; + } #elif defined(Q_OS_LINUX) - // Use proper freedesktop.org way to auto-start the application on Linux. - // INFO: http://standards.freedesktop.org/autostart-spec/latest/ - const QString desktop_file_location = getAutostartDesktopFileLocation(); + // Use proper freedesktop.org way to auto-start the application on Linux. + // INFO: http://standards.freedesktop.org/autostart-spec/latest/ + const QString desktop_file_location = getAutostartDesktopFileLocation(); - // No correct path was found. - if (desktop_file_location.isEmpty()) { - qWarning("Searching for auto-start function status failed. HOME variable not found."); - return SystemFactory::Unavailable; - } + // No correct path was found. + if (desktop_file_location.isEmpty()) { + qWarning("Searching for auto-start function status failed. HOME variable not found."); + return SystemFactory::Unavailable; + } - // We found correct path, now check if file exists and return correct status. - if (QFile::exists(desktop_file_location)) { - // File exists, we must read it and check if "Hidden" attribute is defined and what is its value. - QSettings desktop_settings(desktop_file_location, QSettings::IniFormat); - bool hidden_value = desktop_settings.value(QSL("Desktop Entry/Hidden"), false).toBool(); + // We found correct path, now check if file exists and return correct status. + if (QFile::exists(desktop_file_location)) { + // File exists, we must read it and check if "Hidden" attribute is defined and what is its value. + QSettings desktop_settings(desktop_file_location, QSettings::IniFormat); + bool hidden_value = desktop_settings.value(QSL("Desktop Entry/Hidden"), false).toBool(); + return hidden_value ? SystemFactory::Disabled : SystemFactory::Enabled; + } - return hidden_value ? SystemFactory::Disabled : SystemFactory::Enabled; - } - else { - return SystemFactory::Disabled; - } + else { + return SystemFactory::Disabled; + } #else - // Disable auto-start functionality on unsupported platforms. - return SystemFactory::Unavailable; + // Disable auto-start functionality on unsupported platforms. + return SystemFactory::Unavailable; #endif } #if defined(Q_OS_LINUX) QString SystemFactory::getAutostartDesktopFileLocation() const { - const QString xdg_config_path(qgetenv("XDG_CONFIG_HOME")); - QString desktop_file_location; + const QString xdg_config_path(qgetenv("XDG_CONFIG_HOME")); + QString desktop_file_location; - if (!xdg_config_path.isEmpty()) { - // XDG_CONFIG_HOME variable is specified. Look for .desktop file - // in 'autostart' subdirectory. - desktop_file_location = xdg_config_path + QSL("/autostart/") + APP_DESKTOP_ENTRY_FILE; - } - else { - // Desired variable is not set, look for the default 'autostart' subdirectory. - const QString home_directory(qgetenv("HOME")); + if (!xdg_config_path.isEmpty()) { + // XDG_CONFIG_HOME variable is specified. Look for .desktop file + // in 'autostart' subdirectory. + desktop_file_location = xdg_config_path + QSL("/autostart/") + APP_DESKTOP_ENTRY_FILE; + } - if (!home_directory.isEmpty()) { - // Home directory exists. Check if target .desktop file exists and - // return according status. - desktop_file_location = home_directory + QSL("/.config/autostart/") + APP_DESKTOP_ENTRY_FILE; - } - } + else { + // Desired variable is not set, look for the default 'autostart' subdirectory. + const QString home_directory(qgetenv("HOME")); - return desktop_file_location; + if (!home_directory.isEmpty()) { + // Home directory exists. Check if target .desktop file exists and + // return according status. + desktop_file_location = home_directory + QSL("/.config/autostart/") + APP_DESKTOP_ENTRY_FILE; + } + } + + return desktop_file_location; } #endif -bool SystemFactory::setAutoStartStatus(const AutoStartStatus &new_status) { - const SystemFactory::AutoStartStatus current_status = SystemFactory::getAutoStartStatus(); +bool SystemFactory::setAutoStartStatus(const AutoStartStatus& new_status) { + const SystemFactory::AutoStartStatus current_status = SystemFactory::getAutoStartStatus(); - // Auto-start feature is not even available, exit. - if (current_status == SystemFactory::Unavailable) { - return false; - } + // Auto-start feature is not even available, exit. + if (current_status == SystemFactory::Unavailable) { + return false; + } #if defined(Q_OS_WIN) - QSettings registry_key(QSL("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"), QSettings::NativeFormat); + QSettings registry_key(QSL("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"), QSettings::NativeFormat); - switch (new_status) { - case SystemFactory::Enabled: - registry_key.setValue(APP_LOW_NAME, - Application::applicationFilePath().replace(QL1C('/'), QL1C('\\'))); - return true; + switch (new_status) { + case SystemFactory::Enabled: + registry_key.setValue(APP_LOW_NAME, + Application::applicationFilePath().replace(QL1C('/'), QL1C('\\'))); + return true; - case SystemFactory::Disabled: - registry_key.remove(APP_LOW_NAME); - return true; + case SystemFactory::Disabled: + registry_key.remove(APP_LOW_NAME); + return true; + + default: + return false; + } - default: - return false; - } #elif defined(Q_OS_LINUX) - // Note that we expect here that no other program uses - // "rssguard.desktop" desktop file. - const QString destination_file = getAutostartDesktopFileLocation(); - const QString destination_folder = QFileInfo(destination_file).absolutePath(); + // Note that we expect here that no other program uses + // "rssguard.desktop" desktop file. + const QString destination_file = getAutostartDesktopFileLocation(); + const QString destination_folder = QFileInfo(destination_file).absolutePath(); - switch (new_status) { - case SystemFactory::Enabled: { - if (QFile::exists(destination_file)) { - if (!QFile::remove(destination_file)) { - return false; - } - } + switch (new_status) { + case SystemFactory::Enabled: { + if (QFile::exists(destination_file)) { + if (!QFile::remove(destination_file)) { + return false; + } + } - if (!QDir().mkpath(destination_folder)) { - return false; - } + if (!QDir().mkpath(destination_folder)) { + return false; + } - const QString source_autostart_desktop_file = QString(APP_DESKTOP_ENTRY_PATH) + QDir::separator() + APP_DESKTOP_SOURCE_ENTRY_FILE; + const QString source_autostart_desktop_file = QString(APP_DESKTOP_ENTRY_PATH) + QDir::separator() + APP_DESKTOP_SOURCE_ENTRY_FILE; + return QFile::copy(source_autostart_desktop_file, destination_file); + } - return QFile::copy(source_autostart_desktop_file, destination_file); - } + case SystemFactory::Disabled: + return QFile::remove(destination_file); - case SystemFactory::Disabled: - return QFile::remove(destination_file); + default: + return false; + } - default: - return false; - } #else - return false; + return false; #endif } #if defined(Q_OS_WIN) bool SystemFactory::removeTrolltechJunkRegistryKeys() { - if (qApp->settings()->value(GROUP(General), SETTING(General::RemoveTrolltechJunk)).toBool()) { - QSettings registry_key(QSL("HKEY_CURRENT_USER\\Software\\TrollTech"), QSettings::NativeFormat); + if (qApp->settings()->value(GROUP(General), SETTING(General::RemoveTrolltechJunk)).toBool()) { + QSettings registry_key(QSL("HKEY_CURRENT_USER\\Software\\TrollTech"), QSettings::NativeFormat); + registry_key.remove(QSL("")); + registry_key.sync(); + return registry_key.status() == QSettings::NoError; + } - registry_key.remove(QSL("")); - registry_key.sync(); - - return registry_key.status() == QSettings::NoError; - } - else { - return false; - } + else { + return false; + } } #endif QString SystemFactory::getUsername() const { - QString name = qgetenv("USER"); + QString name = qgetenv("USER"); - if (name.isEmpty()) { - name = qgetenv("USERNAME"); - } + if (name.isEmpty()) { + name = qgetenv("USERNAME"); + } - if (name.isEmpty()) { - name = tr("anonymous"); - } + if (name.isEmpty()) { + name = tr("anonymous"); + } - return name; + return name; } QPair, QNetworkReply::NetworkError> SystemFactory::checkForUpdates() const { - QPair, QNetworkReply::NetworkError> result; - QByteArray releases_json; + QPair, QNetworkReply::NetworkError> result; + QByteArray releases_json; + result.second = NetworkFactory::performNetworkOperation(RELEASES_LIST, DOWNLOAD_TIMEOUT, QByteArray(), QString(), + releases_json, QNetworkAccessManager::GetOperation).first; - result.second = NetworkFactory::performNetworkOperation(RELEASES_LIST, DOWNLOAD_TIMEOUT, QByteArray(), QString(), - releases_json, QNetworkAccessManager::GetOperation).first; + if (result.second == QNetworkReply::NoError) { + result.first = parseUpdatesFile(releases_json); + } - if (result.second == QNetworkReply::NoError) { - result.first = parseUpdatesFile(releases_json); - } - - return result; + return result; } -bool SystemFactory::isVersionNewer(const QString &new_version, const QString &base_version) { - QStringList base_version_tkn = base_version.split(QL1C('.')); - QStringList new_version_tkn = new_version.split(QL1C('.')); +bool SystemFactory::isVersionNewer(const QString& new_version, const QString& base_version) { + QStringList base_version_tkn = base_version.split(QL1C('.')); + QStringList new_version_tkn = new_version.split(QL1C('.')); - while (!base_version_tkn.isEmpty() && !new_version_tkn.isEmpty()) { - const int base_number = base_version_tkn.takeFirst().toInt(); - const int new_number = new_version_tkn.takeFirst().toInt(); + while (!base_version_tkn.isEmpty() && !new_version_tkn.isEmpty()) { + const int base_number = base_version_tkn.takeFirst().toInt(); + const int new_number = new_version_tkn.takeFirst().toInt(); - if (new_number > base_number) { - // New version is indeed higher thatn current version. - return true; - } - else if (new_number < base_number) { - return false; - } - } + if (new_number > base_number) { + // New version is indeed higher thatn current version. + return true; + } - // Versions are either the same or they have unequal sizes. - if (base_version_tkn.isEmpty() && new_version_tkn.isEmpty()) { - // Versions are the same. - return false; - } - else { - if (new_version_tkn.isEmpty()) { - return false; - } - else { - return new_version_tkn.join(QString()).toInt() > 0; - } - } + else if (new_number < base_number) { + return false; + } + } + + // Versions are either the same or they have unequal sizes. + if (base_version_tkn.isEmpty() && new_version_tkn.isEmpty()) { + // Versions are the same. + return false; + } + + else { + if (new_version_tkn.isEmpty()) { + return false; + } + + else { + return new_version_tkn.join(QString()).toInt() > 0; + } + } } -bool SystemFactory::isVersionEqualOrNewer(const QString &new_version, const QString &base_version) { - return new_version == base_version || isVersionNewer(new_version, base_version); +bool SystemFactory::isVersionEqualOrNewer(const QString& new_version, const QString& base_version) { + return new_version == base_version || isVersionNewer(new_version, base_version); } -bool SystemFactory::openFolderFile(const QString &file_path) { +bool SystemFactory::openFolderFile(const QString& file_path) { #if defined(Q_OS_WIN) - return QProcess::startDetached(QString("explorer.exe /select, \"") + QDir::toNativeSeparators(file_path) + "\""); + return QProcess::startDetached(QString("explorer.exe /select, \"") + QDir::toNativeSeparators(file_path) + "\""); #else - const QString folder = QDir::toNativeSeparators(QFileInfo(file_path).absoluteDir().absolutePath()); - return QDesktopServices::openUrl(QUrl::fromLocalFile(folder)); + const QString folder = QDir::toNativeSeparators(QFileInfo(file_path).absoluteDir().absolutePath()); + return QDesktopServices::openUrl(QUrl::fromLocalFile(folder)); #endif } -QList SystemFactory::parseUpdatesFile(const QByteArray &updates_file) const { - QList updates; +QList SystemFactory::parseUpdatesFile(const QByteArray& updates_file) const { + QList updates; + QJsonArray document = QJsonDocument::fromJson(updates_file).array(); - QJsonArray document = QJsonDocument::fromJson(updates_file).array(); + for (int i = 0; i < document.size(); i++) { + QJsonObject release = document.at(i).toObject(); + UpdateInfo update; + update.m_date = QDateTime::fromString(release["published_at"].toString(), QSL("yyyy-MM-ddTHH:mm:ssZ")); + update.m_availableVersion = release["tag_name"].toString(); + update.m_changes = release["body"].toString(); + QJsonArray assets = release["assets"].toArray(); - for (int i = 0; i < document.size(); i++) { - QJsonObject release = document.at(i).toObject(); - UpdateInfo update; + for (int j = 0; j < assets.size(); j++) { + QJsonObject asset = assets.at(j).toObject(); + UpdateUrl url; + url.m_fileUrl = asset["browser_download_url"].toString(); + url.m_name = asset["name"].toString(); + url.m_size = asset["size"].toVariant().toString() + tr(" bytes"); + update.m_urls.append(url); + } - update.m_date = QDateTime::fromString(release["published_at"].toString(), QSL("yyyy-MM-ddTHH:mm:ssZ")); - update.m_availableVersion = release["tag_name"].toString(); - update.m_changes = release["body"].toString(); + updates.append(update); + } - QJsonArray assets = release["assets"].toArray(); - - for (int j = 0; j < assets.size(); j++) { - QJsonObject asset = assets.at(j).toObject(); - UpdateUrl url; - - url.m_fileUrl = asset["browser_download_url"].toString(); - url.m_name = asset["name"].toString(); - url.m_size = asset["size"].toVariant().toString() + tr(" bytes"); - - update.m_urls.append(url); - } - - updates.append(update); - } - - qSort(updates.begin(), updates.end(), [](const UpdateInfo &a, const UpdateInfo &b) -> bool { - return a.m_date > b.m_date; - }); - - return updates; + qSort(updates.begin(), updates.end(), [](const UpdateInfo & a, const UpdateInfo & b) -> bool { + return a.m_date > b.m_date; + }); + return updates; } void SystemFactory::checkForUpdatesOnStartup() { - const QPair, QNetworkReply::NetworkError> updates = checkForUpdates(); + const QPair, QNetworkReply::NetworkError> updates = checkForUpdates(); - if (!updates.first.isEmpty() && updates.second == QNetworkReply::NoError && isVersionNewer(updates.first.at(0).m_availableVersion, - APP_VERSION)) { - qApp->showGuiMessage(tr("New version available"), - tr("Click the bubble for more information."), - QSystemTrayIcon::Information, - nullptr, true, qApp->mainFormWidget(), SLOT(showUpdates())); - } + if (!updates.first.isEmpty() && updates.second == QNetworkReply::NoError && isVersionNewer(updates.first.at(0).m_availableVersion, + APP_VERSION)) { + qApp->showGuiMessage(tr("New version available"), + tr("Click the bubble for more information."), + QSystemTrayIcon::Information, + nullptr, true, qApp->mainFormWidget(), SLOT(showUpdates())); + } } diff --git a/src/miscellaneous/systemfactory.h b/src/miscellaneous/systemfactory.h index f34fb738e..cceb57360 100755 --- a/src/miscellaneous/systemfactory.h +++ b/src/miscellaneous/systemfactory.h @@ -27,78 +27,78 @@ class UpdateUrl { - public: - QString m_fileUrl; - QString m_name; - QString m_size; + public: + QString m_fileUrl; + QString m_name; + QString m_size; }; class UpdateInfo { - public: - explicit UpdateInfo() : m_availableVersion(QString()), m_changes(QString()), m_urls(QList()) { - } + public: + explicit UpdateInfo() : m_availableVersion(QString()), m_changes(QString()), m_urls(QList()) { + } - QString m_availableVersion; - QString m_changes; - QList m_urls; - QDateTime m_date; + QString m_availableVersion; + QString m_changes; + QList m_urls; + QDateTime m_date; }; Q_DECLARE_METATYPE(UpdateInfo) class SystemFactory : public QObject { - Q_OBJECT + Q_OBJECT - public: - // Specifies possible states of auto-start functionality. - enum AutoStartStatus { - Enabled, - Disabled, - Unavailable - }; + public: + // Specifies possible states of auto-start functionality. + enum AutoStartStatus { + Enabled, + Disabled, + Unavailable + }; - // Constructors and destructors. - explicit SystemFactory(QObject *parent = 0); + // Constructors and destructors. + explicit SystemFactory(QObject* parent = 0); - // Constructors and destructors. - virtual ~SystemFactory(); + // Constructors and destructors. + virtual ~SystemFactory(); - // Returns current status of auto-start function. - SystemFactory::AutoStartStatus getAutoStartStatus() const; + // Returns current status of auto-start function. + SystemFactory::AutoStartStatus getAutoStartStatus() const; - // Sets new status for auto-start function. - // Function returns false if setting of - // new status failed. - bool setAutoStartStatus(const SystemFactory::AutoStartStatus &new_status); + // Sets new status for auto-start function. + // Function returns false if setting of + // new status failed. + bool setAutoStartStatus(const SystemFactory::AutoStartStatus& new_status); #if defined(Q_OS_WIN) - bool removeTrolltechJunkRegistryKeys(); + bool removeTrolltechJunkRegistryKeys(); #endif #if defined(Q_OS_LINUX) - // Returns standard location where auto-start .desktop files - // should be placed. - QString getAutostartDesktopFileLocation() const; + // Returns standard location where auto-start .desktop files + // should be placed. + QString getAutostartDesktopFileLocation() const; #endif - // Retrieves username of currently logged-in user. - QString getUsername() const; + // Retrieves username of currently logged-in user. + QString getUsername() const; - // Tries to download list with new updates. - QPair, QNetworkReply::NetworkError> checkForUpdates() const; + // Tries to download list with new updates. + QPair, QNetworkReply::NetworkError> checkForUpdates() const; - // Checks if update is newer than current application version. - static bool isVersionNewer(const QString &new_version, const QString &base_version); - static bool isVersionEqualOrNewer(const QString &new_version, const QString &base_version); + // Checks if update is newer than current application version. + static bool isVersionNewer(const QString& new_version, const QString& base_version); + static bool isVersionEqualOrNewer(const QString& new_version, const QString& base_version); - static bool openFolderFile(const QString &file_path); + static bool openFolderFile(const QString& file_path); - public slots: - void checkForUpdatesOnStartup(); + public slots: + void checkForUpdatesOnStartup(); - private: - // Performs parsing of downloaded file with list of updates. - QList parseUpdatesFile(const QByteArray &updates_file) const; + private: + // Performs parsing of downloaded file with list of updates. + QList parseUpdatesFile(const QByteArray& updates_file) const; }; #endif // SYSTEMFACTORY_H diff --git a/src/miscellaneous/textfactory.cpp b/src/miscellaneous/textfactory.cpp index a0435a349..280b565db 100755 --- a/src/miscellaneous/textfactory.cpp +++ b/src/miscellaneous/textfactory.cpp @@ -34,122 +34,126 @@ quint64 TextFactory::s_encryptionKey = 0x0; TextFactory::TextFactory() { } -int TextFactory::stringHeight(const QString &string, const QFontMetrics &metrics) { - const int count_lines = string.split(QL1C('\n')).size(); - return metrics.height() * count_lines; +int TextFactory::stringHeight(const QString& string, const QFontMetrics& metrics) { + const int count_lines = string.split(QL1C('\n')).size(); + return metrics.height() * count_lines; } -int TextFactory::stringWidth(const QString &string, const QFontMetrics &metrics) { - const QStringList lines = string.split(QL1C('\n')); - int width = 0; +int TextFactory::stringWidth(const QString& string, const QFontMetrics& metrics) { + const QStringList lines = string.split(QL1C('\n')); + int width = 0; - foreach (const QString &line, lines) { - int line_width = metrics.width(line); + foreach (const QString& line, lines) { + int line_width = metrics.width(line); - if (line_width > width) { - width = line_width; - } - } + if (line_width > width) { + width = line_width; + } + } - return width; + return width; } -QDateTime TextFactory::parseDateTime(const QString &date_time) { - const QString input_date = date_time.simplified(); - QDateTime dt; - QTime time_zone_offset; - const QLocale locale(QLocale::C); - bool positive_time_zone_offset = false; +QDateTime TextFactory::parseDateTime(const QString& date_time) { + const QString input_date = date_time.simplified(); + QDateTime dt; + QTime time_zone_offset; + const QLocale locale(QLocale::C); + bool positive_time_zone_offset = false; + QStringList date_patterns; + date_patterns << QSL("yyyy-MM-ddTHH:mm:ss") << QSL("MMM dd yyyy hh:mm:ss") << + QSL("MMM d yyyy hh:mm:ss") << QSL("ddd, dd MMM yyyy HH:mm:ss") << + QSL("dd MMM yyyy") << QSL("yyyy-MM-dd HH:mm:ss.z") << QSL("yyyy-MM-dd") << + QSL("yyyy") << QSL("yyyy-MM") << QSL("yyyy-MM-dd") << QSL("yyyy-MM-ddThh:mm") << + QSL("yyyy-MM-ddThh:mm:ss"); + QStringList timezone_offset_patterns; + timezone_offset_patterns << QSL("+hh:mm") << QSL("-hh:mm") << QSL("+hhmm") + << QSL("-hhmm") << QSL("+hh") << QSL("-hh"); - QStringList date_patterns; date_patterns << QSL("yyyy-MM-ddTHH:mm:ss") << QSL("MMM dd yyyy hh:mm:ss") << - QSL("MMM d yyyy hh:mm:ss") << QSL("ddd, dd MMM yyyy HH:mm:ss") << - QSL("dd MMM yyyy") << QSL("yyyy-MM-dd HH:mm:ss.z") << QSL("yyyy-MM-dd") << - QSL("yyyy") << QSL("yyyy-MM") << QSL("yyyy-MM-dd") << QSL("yyyy-MM-ddThh:mm") << - QSL("yyyy-MM-ddThh:mm:ss"); + if (input_date.size() >= TIMEZONE_OFFSET_LIMIT) { + foreach (const QString& pattern, timezone_offset_patterns) { + time_zone_offset = QTime::fromString(input_date.right(pattern.size()), pattern); - QStringList timezone_offset_patterns; timezone_offset_patterns << QSL("+hh:mm") << QSL("-hh:mm") << QSL("+hhmm") - << QSL("-hhmm") << QSL("+hh") << QSL("-hh"); + if (time_zone_offset.isValid()) { + positive_time_zone_offset = pattern.at(0) == QL1C('+'); + break; + } + } + } - if (input_date.size() >= TIMEZONE_OFFSET_LIMIT) { - foreach (const QString &pattern, timezone_offset_patterns) { - time_zone_offset = QTime::fromString(input_date.right(pattern.size()), pattern); + // Iterate over patterns and check if input date/time matches the pattern. + foreach (const QString& pattern, date_patterns) { + dt = locale.toDateTime(input_date.left(pattern.size()), pattern); - if (time_zone_offset.isValid()) { - positive_time_zone_offset = pattern.at(0) == QL1C('+'); - break; - } - } - } + if (dt.isValid()) { + // Make sure that this date/time is considered UTC. + dt.setTimeSpec(Qt::UTC); - // Iterate over patterns and check if input date/time matches the pattern. - foreach (const QString &pattern, date_patterns) { - dt = locale.toDateTime(input_date.left(pattern.size()), pattern); + if (time_zone_offset.isValid()) { + // Time zone offset was detected. + if (positive_time_zone_offset) { + // Offset is positive, so we have to subtract it to get + // the original UTC. + return dt.addSecs(- QTime(0, 0, 0, 0).secsTo(time_zone_offset)); + } - if (dt.isValid()) { - // Make sure that this date/time is considered UTC. - dt.setTimeSpec(Qt::UTC); + else { + // Vice versa. + return dt.addSecs(QTime(0, 0, 0, 0).secsTo(time_zone_offset)); + } + } - if (time_zone_offset.isValid()) { - // Time zone offset was detected. - if (positive_time_zone_offset) { - // Offset is positive, so we have to subtract it to get - // the original UTC. - return dt.addSecs(- QTime(0, 0, 0, 0).secsTo(time_zone_offset)); - } - else { - // Vice versa. - return dt.addSecs(QTime(0, 0, 0, 0).secsTo(time_zone_offset)); - } - } - else { - return dt; - } - } - } + else { + return dt; + } + } + } - // Parsing failed, return invalid datetime. - return QDateTime(); + // Parsing failed, return invalid datetime. + return QDateTime(); } QDateTime TextFactory::parseDateTime(qint64 milis_from_epoch) { - return QDateTime::fromMSecsSinceEpoch(milis_from_epoch); + return QDateTime::fromMSecsSinceEpoch(milis_from_epoch); } -QString TextFactory::encrypt(const QString &text) { - return SimpleCrypt(initializeSecretEncryptionKey()).encryptToString(text); +QString TextFactory::encrypt(const QString& text) { + return SimpleCrypt(initializeSecretEncryptionKey()).encryptToString(text); } -QString TextFactory::decrypt(const QString &text) { - return SimpleCrypt(initializeSecretEncryptionKey()).decryptToString(text); +QString TextFactory::decrypt(const QString& text) { + return SimpleCrypt(initializeSecretEncryptionKey()).decryptToString(text); } -QString TextFactory::shorten(const QString &input, int text_length_limit) { - if (input.size() > text_length_limit) { - return input.left(text_length_limit - ELLIPSIS_LENGTH) + QString(ELLIPSIS_LENGTH, QL1C('.')); - } - else { - return input; - } +QString TextFactory::shorten(const QString& input, int text_length_limit) { + if (input.size() > text_length_limit) { + return input.left(text_length_limit - ELLIPSIS_LENGTH) + QString(ELLIPSIS_LENGTH, QL1C('.')); + } + + else { + return input; + } } quint64 TextFactory::initializeSecretEncryptionKey() { - if (s_encryptionKey == 0x0) { - // Check if file with encryption key exists. - QString encryption_file_path = qApp->settings()->pathName() + QDir::separator() + ENCRYPTION_FILE_NAME; + if (s_encryptionKey == 0x0) { + // Check if file with encryption key exists. + QString encryption_file_path = qApp->settings()->pathName() + QDir::separator() + ENCRYPTION_FILE_NAME; - try { - s_encryptionKey = (quint64) QString(IOFactory::readTextFile(encryption_file_path)).toLongLong(); - } - catch (ApplicationException) { - // Well, key does not exist or is invalid, generate and save one. - s_encryptionKey = generateSecretEncryptionKey(); - IOFactory::writeTextFile(encryption_file_path, QString::number(s_encryptionKey).toLocal8Bit()); - } - } + try { + s_encryptionKey = (quint64) QString(IOFactory::readTextFile(encryption_file_path)).toLongLong(); + } - return s_encryptionKey; + catch (ApplicationException) { + // Well, key does not exist or is invalid, generate and save one. + s_encryptionKey = generateSecretEncryptionKey(); + IOFactory::writeTextFile(encryption_file_path, QString::number(s_encryptionKey).toLocal8Bit()); + } + } + + return s_encryptionKey; } quint64 TextFactory::generateSecretEncryptionKey() { - return RAND_MAX * qrand() + qrand(); + return RAND_MAX * qrand() + qrand(); } diff --git a/src/miscellaneous/textfactory.h b/src/miscellaneous/textfactory.h index 06889eb81..83296b8eb 100755 --- a/src/miscellaneous/textfactory.h +++ b/src/miscellaneous/textfactory.h @@ -25,39 +25,39 @@ class TextFactory { - private: - // Constructors and destructors. - TextFactory(); + private: + // Constructors and destructors. + TextFactory(); - public: - // Returns true if lhs is smaller than rhs if case-insensitive string comparison is used. - static inline bool isCaseInsensitiveLessThan(const QString &lhs, const QString &rhs) { - return lhs.toLower() < rhs.toLower(); - } + public: + // Returns true if lhs is smaller than rhs if case-insensitive string comparison is used. + static inline bool isCaseInsensitiveLessThan(const QString& lhs, const QString& rhs) { + return lhs.toLower() < rhs.toLower(); + } - static int stringHeight(const QString &string, const QFontMetrics &metrics); - static int stringWidth(const QString &string, const QFontMetrics &metrics); + static int stringHeight(const QString& string, const QFontMetrics& metrics); + static int stringWidth(const QString& string, const QFontMetrics& metrics); - // Tries to parse input textual date/time representation. - // Returns invalid date/time if processing fails. - // NOTE: This method tries to always return time in UTC+00:00. - static QDateTime parseDateTime(const QString &date_time); + // Tries to parse input textual date/time representation. + // Returns invalid date/time if processing fails. + // NOTE: This method tries to always return time in UTC+00:00. + static QDateTime parseDateTime(const QString& date_time); - // Converts 1970-epoch miliseconds to date/time. - // NOTE: This apparently returns date/time in localtime. - static QDateTime parseDateTime(qint64 milis_from_epoch); + // Converts 1970-epoch miliseconds to date/time. + // NOTE: This apparently returns date/time in localtime. + static QDateTime parseDateTime(qint64 milis_from_epoch); - static QString encrypt(const QString &text); - static QString decrypt(const QString &text); + static QString encrypt(const QString& text); + static QString decrypt(const QString& text); - // Shortens input string according to given length limit. - static QString shorten(const QString &input, int text_length_limit = TEXT_TITLE_LIMIT); + // Shortens input string according to given length limit. + static QString shorten(const QString& input, int text_length_limit = TEXT_TITLE_LIMIT); - private: - static quint64 initializeSecretEncryptionKey(); - static quint64 generateSecretEncryptionKey(); + private: + static quint64 initializeSecretEncryptionKey(); + static quint64 generateSecretEncryptionKey(); - static quint64 s_encryptionKey; + static quint64 s_encryptionKey; }; #endif // TEXTFACTORY_H diff --git a/src/network-web/adblock/adblockaddsubscriptiondialog.cpp b/src/network-web/adblock/adblockaddsubscriptiondialog.cpp index 08896a62f..46d83e5f3 100755 --- a/src/network-web/adblock/adblockaddsubscriptiondialog.cpp +++ b/src/network-web/adblock/adblockaddsubscriptiondialog.cpp @@ -24,68 +24,67 @@ AdBlockAddSubscriptionDialog::AdBlockAddSubscriptionDialog(QWidget* parent) - : QDialog(parent), m_ui(new Ui::AdBlockAddSubscriptionDialog) { - m_ui->setupUi(this); + : QDialog(parent), m_ui(new Ui::AdBlockAddSubscriptionDialog) { + m_ui->setupUi(this); + m_knownSubscriptions << Subscription(QSL("EasyList (English)"), ADBLOCK_EASYLIST_URL) + << Subscription(QSL("BSI Lista Polska (Polish)"), QSL("http://www.bsi.info.pl/filtrABP.txt")) + << Subscription(QSL("EasyList Czech and Slovak (Czech)"), QSL("https://raw.githubusercontent.com/tomasko126/easylistczechandslovak/master/filters.txt")) + << Subscription(QSL("dutchblock (Dutch)"), QSL("http://groenewoudt.net/dutchblock/list.txt")) + << Subscription(QSL("Filtros Nauscopicos (Spanish)"), QSL("http://abp.mozilla-hispano.org/nauscopio/filtros.txt")) + << Subscription(QSL("IsraelList (Hebrew)"), QSL("http://secure.fanboy.co.nz/israelilist/IsraelList.txt")) + << Subscription(QSL("NLBlock (Dutch)"), QSL("http://www.verzijlbergh.com/adblock/nlblock.txt")) + << Subscription(QSL("Peter Lowe's list (English)"), QSL("http://pgl.yoyo.org/adservers/serverlist.php?hostformat=adblockplus&mimetype=plaintext")) + << Subscription(QSL("PLgeneral (Polish)"), QSL("http://www.niecko.pl/adblock/adblock.txt")) + << Subscription(QSL("Schacks Adblock Plus liste (Danish)"), QSL("http://adblock.schack.dk/block.txt")) + << Subscription(QSL("Xfiles (Italian)"), QSL("http://mozilla.gfsolone.com/filtri.txt")) + << Subscription(QSL("EasyPrivacy (English)"), QSL("http://easylist-downloads.adblockplus.org/easyprivacy.txt")) + << Subscription(QSL("RU Adlist (Russian)"), QSL("https://easylist-downloads.adblockplus.org/advblock.txt")) + << Subscription(QSL("ABPindo (Indonesian)"), QSL("https://raw.githubusercontent.com/heradhis/indonesianadblockrules/master/subscriptions/abpindo.txt")) + << Subscription(QSL("Easylist China (Chinese)"), QSL("https://easylist-downloads.adblockplus.org/easylistchina.txt")) + << Subscription(QSL("Anti-Adblock Killer"), QSL("https://raw.githubusercontent.com/reek/anti-adblock-killer/master/anti-adblock-killer-filters.txt")) + << Subscription(tr("Other..."), QString()); - m_knownSubscriptions << Subscription(QSL("EasyList (English)"), ADBLOCK_EASYLIST_URL) - << Subscription(QSL("BSI Lista Polska (Polish)"), QSL("http://www.bsi.info.pl/filtrABP.txt")) - << Subscription(QSL("EasyList Czech and Slovak (Czech)"), QSL("https://raw.githubusercontent.com/tomasko126/easylistczechandslovak/master/filters.txt")) - << Subscription(QSL("dutchblock (Dutch)"), QSL("http://groenewoudt.net/dutchblock/list.txt")) - << Subscription(QSL("Filtros Nauscopicos (Spanish)"), QSL("http://abp.mozilla-hispano.org/nauscopio/filtros.txt")) - << Subscription(QSL("IsraelList (Hebrew)"), QSL("http://secure.fanboy.co.nz/israelilist/IsraelList.txt")) - << Subscription(QSL("NLBlock (Dutch)"), QSL("http://www.verzijlbergh.com/adblock/nlblock.txt")) - << Subscription(QSL("Peter Lowe's list (English)"), QSL("http://pgl.yoyo.org/adservers/serverlist.php?hostformat=adblockplus&mimetype=plaintext")) - << Subscription(QSL("PLgeneral (Polish)"), QSL("http://www.niecko.pl/adblock/adblock.txt")) - << Subscription(QSL("Schacks Adblock Plus liste (Danish)"), QSL("http://adblock.schack.dk/block.txt")) - << Subscription(QSL("Xfiles (Italian)"), QSL("http://mozilla.gfsolone.com/filtri.txt")) - << Subscription(QSL("EasyPrivacy (English)"), QSL("http://easylist-downloads.adblockplus.org/easyprivacy.txt")) - << Subscription(QSL("RU Adlist (Russian)"), QSL("https://easylist-downloads.adblockplus.org/advblock.txt")) - << Subscription(QSL("ABPindo (Indonesian)"), QSL("https://raw.githubusercontent.com/heradhis/indonesianadblockrules/master/subscriptions/abpindo.txt")) - << Subscription(QSL("Easylist China (Chinese)"), QSL("https://easylist-downloads.adblockplus.org/easylistchina.txt")) - << Subscription(QSL("Anti-Adblock Killer"), QSL("https://raw.githubusercontent.com/reek/anti-adblock-killer/master/anti-adblock-killer-filters.txt")) - << Subscription(tr("Other..."), QString()); + foreach (const Subscription& subscription, m_knownSubscriptions) { + m_ui->comboBox->addItem(subscription.m_title); + } - foreach (const Subscription &subscription, m_knownSubscriptions) { - m_ui->comboBox->addItem(subscription.m_title); - } - - connect(m_ui->comboBox, static_cast(&QComboBox::currentIndexChanged), - this, &AdBlockAddSubscriptionDialog::indexChanged); - indexChanged(0); + connect(m_ui->comboBox, static_cast(&QComboBox::currentIndexChanged), + this, &AdBlockAddSubscriptionDialog::indexChanged); + indexChanged(0); } QString AdBlockAddSubscriptionDialog::title() const { - return m_ui->title->text(); + return m_ui->title->text(); } QString AdBlockAddSubscriptionDialog::url() const { - return m_ui->url->text(); + return m_ui->url->text(); } void AdBlockAddSubscriptionDialog::indexChanged(int index) { - const Subscription subscription = m_knownSubscriptions.at(index); + const Subscription subscription = m_knownSubscriptions.at(index); - // "Other..." entry. - if (subscription.m_url.isEmpty()) { - m_ui->title->clear(); - m_ui->url->clear(); - } - else { - int pos = subscription.m_title.indexOf(QLatin1Char('(')); - QString title = subscription.m_title; + // "Other..." entry. + if (subscription.m_url.isEmpty()) { + m_ui->title->clear(); + m_ui->url->clear(); + } - if (pos > 0) { - title = title.left(pos).trimmed(); - } + else { + int pos = subscription.m_title.indexOf(QLatin1Char('(')); + QString title = subscription.m_title; - m_ui->title->setText(title); - m_ui->title->setCursorPosition(0); + if (pos > 0) { + title = title.left(pos).trimmed(); + } - m_ui->url->setText(subscription.m_url); - m_ui->url->setCursorPosition(0); - } + m_ui->title->setText(title); + m_ui->title->setCursorPosition(0); + m_ui->url->setText(subscription.m_url); + m_ui->url->setCursorPosition(0); + } } AdBlockAddSubscriptionDialog::~AdBlockAddSubscriptionDialog() { - delete m_ui; + delete m_ui; } diff --git a/src/network-web/adblock/adblockaddsubscriptiondialog.h b/src/network-web/adblock/adblockaddsubscriptiondialog.h index eec53bfbe..af9fea063 100755 --- a/src/network-web/adblock/adblockaddsubscriptiondialog.h +++ b/src/network-web/adblock/adblockaddsubscriptiondialog.h @@ -26,38 +26,38 @@ namespace Ui { - class AdBlockAddSubscriptionDialog; + class AdBlockAddSubscriptionDialog; } class AdBlockAddSubscriptionDialog : public QDialog { - Q_OBJECT + Q_OBJECT - public: - explicit AdBlockAddSubscriptionDialog(QWidget* parent = 0); - virtual ~AdBlockAddSubscriptionDialog(); + public: + explicit AdBlockAddSubscriptionDialog(QWidget* parent = 0); + virtual ~AdBlockAddSubscriptionDialog(); - QString title() const; - QString url() const; + QString title() const; + QString url() const; - private slots: - void indexChanged(int index); + private slots: + void indexChanged(int index); - private: - Ui::AdBlockAddSubscriptionDialog *m_ui; + private: + Ui::AdBlockAddSubscriptionDialog* m_ui; - struct Subscription { - QString m_title; - QString m_url; + struct Subscription { + QString m_title; + QString m_url; - Subscription() {} + Subscription() {} - Subscription(const QString &t, const QString &u) { - m_title = t; - m_url = u; - } - }; + Subscription(const QString& t, const QString& u) { + m_title = t; + m_url = u; + } + }; - QVector m_knownSubscriptions; + QVector m_knownSubscriptions; }; #endif // ADBLOCKADDSUBSCRIPTIONDIALOG_H diff --git a/src/network-web/adblock/adblockdialog.cpp b/src/network-web/adblock/adblockdialog.cpp index 7f08bd574..31bddec46 100755 --- a/src/network-web/adblock/adblockdialog.cpp +++ b/src/network-web/adblock/adblockdialog.cpp @@ -31,134 +31,124 @@ AdBlockDialog::AdBlockDialog(QWidget* parent) - : QWidget(parent), m_ui(new Ui::AdBlockDialog), m_manager(AdBlockManager::instance()), m_currentTreeWidget(0), m_currentSubscription(0), m_loaded(false) { - setAttribute(Qt::WA_DeleteOnClose); - m_ui->setupUi(this); - + : QWidget(parent), m_ui(new Ui::AdBlockDialog), m_manager(AdBlockManager::instance()), m_currentTreeWidget(0), m_currentSubscription(0), m_loaded(false) { + setAttribute(Qt::WA_DeleteOnClose); + m_ui->setupUi(this); #ifdef Q_OS_MACOS - m_ui->tabWidget->setDocumentMode(false); + m_ui->tabWidget->setDocumentMode(false); #endif - - m_ui->adblockCheckBox->setChecked(m_manager->isEnabled()); - - QMenu* menu = new QMenu(m_ui->buttonOptions); - m_actionAddRule = menu->addAction(tr("Add rule"), this, SLOT(addRule())); - m_actionRemoveRule = menu->addAction(tr("Remove rule"), this, SLOT(removeRule())); - menu->addSeparator(); - m_actionAddSubscription = menu->addAction(tr("Add subscription"), this, SLOT(addSubscription())); - m_actionRemoveSubscription = menu->addAction(tr("Remove subscription"), this, SLOT(removeSubscription())); - menu->addAction(tr("Update subscriptions"), m_manager, SLOT(updateAllSubscriptions())); - menu->addSeparator(); - menu->addAction(tr("Learn about writing rules..."), this, SLOT(learnAboutRules())); - - m_ui->buttonOptions->setMenu(menu); - connect(menu, SIGNAL(aboutToShow()), this, SLOT(aboutToShowMenu())); - - connect(m_ui->adblockCheckBox, SIGNAL(toggled(bool)), this, SLOT(enableAdBlock(bool))); - connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(currentChanged(int))); - connect(m_ui->buttonBox, &QDialogButtonBox::clicked, this, &AdBlockDialog::close); - - load(); - - m_ui->buttonBox->setFocus(); + m_ui->adblockCheckBox->setChecked(m_manager->isEnabled()); + QMenu* menu = new QMenu(m_ui->buttonOptions); + m_actionAddRule = menu->addAction(tr("Add rule"), this, SLOT(addRule())); + m_actionRemoveRule = menu->addAction(tr("Remove rule"), this, SLOT(removeRule())); + menu->addSeparator(); + m_actionAddSubscription = menu->addAction(tr("Add subscription"), this, SLOT(addSubscription())); + m_actionRemoveSubscription = menu->addAction(tr("Remove subscription"), this, SLOT(removeSubscription())); + menu->addAction(tr("Update subscriptions"), m_manager, SLOT(updateAllSubscriptions())); + menu->addSeparator(); + menu->addAction(tr("Learn about writing rules..."), this, SLOT(learnAboutRules())); + m_ui->buttonOptions->setMenu(menu); + connect(menu, SIGNAL(aboutToShow()), this, SLOT(aboutToShowMenu())); + connect(m_ui->adblockCheckBox, SIGNAL(toggled(bool)), this, SLOT(enableAdBlock(bool))); + connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(currentChanged(int))); + connect(m_ui->buttonBox, &QDialogButtonBox::clicked, this, &AdBlockDialog::close); + load(); + m_ui->buttonBox->setFocus(); } void AdBlockDialog::showRule(const AdBlockRule* rule) const { - AdBlockSubscription *subscription = rule->subscription(); + AdBlockSubscription* subscription = rule->subscription(); - if (!subscription) { - return; - } + if (!subscription) { + return; + } - for (int i = 0; i < m_ui->tabWidget->count(); ++i) { - AdBlockTreeWidget *treeWidget = qobject_cast(m_ui->tabWidget->widget(i)); + for (int i = 0; i < m_ui->tabWidget->count(); ++i) { + AdBlockTreeWidget* treeWidget = qobject_cast(m_ui->tabWidget->widget(i)); - if (subscription == treeWidget->subscription()) { - treeWidget->showRule(rule); - m_ui->tabWidget->setCurrentIndex(i); - break; - } - } + if (subscription == treeWidget->subscription()) { + treeWidget->showRule(rule); + m_ui->tabWidget->setCurrentIndex(i); + break; + } + } } void AdBlockDialog::addRule() { - m_currentTreeWidget->addRule(); + m_currentTreeWidget->addRule(); } void AdBlockDialog::removeRule() { - m_currentTreeWidget->removeRule(); + m_currentTreeWidget->removeRule(); } void AdBlockDialog::addSubscription() { - AdBlockAddSubscriptionDialog dialog(this); + AdBlockAddSubscriptionDialog dialog(this); - if (dialog.exec() != QDialog::Accepted) { - return; - } + if (dialog.exec() != QDialog::Accepted) { + return; + } - QString title = dialog.title(); - QString url = dialog.url(); + QString title = dialog.title(); + QString url = dialog.url(); - if (AdBlockSubscription *subscription = m_manager->addSubscription(title, url)) { - AdBlockTreeWidget *tree = new AdBlockTreeWidget(subscription, m_ui->tabWidget); - int index = m_ui->tabWidget->insertTab(m_ui->tabWidget->count() - 1, tree, subscription->title()); - - m_ui->tabWidget->setCurrentIndex(index); - } + if (AdBlockSubscription* subscription = m_manager->addSubscription(title, url)) { + AdBlockTreeWidget* tree = new AdBlockTreeWidget(subscription, m_ui->tabWidget); + int index = m_ui->tabWidget->insertTab(m_ui->tabWidget->count() - 1, tree, subscription->title()); + m_ui->tabWidget->setCurrentIndex(index); + } } void AdBlockDialog::removeSubscription() { - if (m_manager->removeSubscription(m_currentSubscription)) { - delete m_currentTreeWidget; - } + if (m_manager->removeSubscription(m_currentSubscription)) { + delete m_currentTreeWidget; + } } void AdBlockDialog::currentChanged(int index) { - if (index != -1) { - m_currentTreeWidget = qobject_cast(m_ui->tabWidget->widget(index)); - m_currentSubscription = m_currentTreeWidget->subscription(); - } + if (index != -1) { + m_currentTreeWidget = qobject_cast(m_ui->tabWidget->widget(index)); + m_currentSubscription = m_currentTreeWidget->subscription(); + } } void AdBlockDialog::enableAdBlock(bool state) { - m_manager->setEnabled(state); + m_manager->setEnabled(state); - if (state) { - load(); - } + if (state) { + load(); + } } void AdBlockDialog::aboutToShowMenu() { - bool subscriptionEditable = m_currentSubscription && m_currentSubscription->canEditRules(); - bool subscriptionRemovable = m_currentSubscription && m_currentSubscription->canBeRemoved(); - - m_actionAddRule->setEnabled(subscriptionEditable); - m_actionRemoveRule->setEnabled(subscriptionEditable); - m_actionRemoveSubscription->setEnabled(subscriptionRemovable); + bool subscriptionEditable = m_currentSubscription && m_currentSubscription->canEditRules(); + bool subscriptionRemovable = m_currentSubscription && m_currentSubscription->canBeRemoved(); + m_actionAddRule->setEnabled(subscriptionEditable); + m_actionRemoveRule->setEnabled(subscriptionEditable); + m_actionRemoveSubscription->setEnabled(subscriptionRemovable); } void AdBlockDialog::learnAboutRules() { - WebFactory::instance()->openUrlInExternalBrowser(QSL("http://adblockplus.org/en/filters")); + WebFactory::instance()->openUrlInExternalBrowser(QSL("http://adblockplus.org/en/filters")); } void AdBlockDialog::loadSubscriptions() { - for (int i = 0; i < m_ui->tabWidget->count(); ++i) { - AdBlockTreeWidget *treeWidget = qobject_cast(m_ui->tabWidget->widget(i)); - treeWidget->refresh(); - } + for (int i = 0; i < m_ui->tabWidget->count(); ++i) { + AdBlockTreeWidget* treeWidget = qobject_cast(m_ui->tabWidget->widget(i)); + treeWidget->refresh(); + } } void AdBlockDialog::load() { - if (m_loaded || !m_ui->adblockCheckBox->isChecked()) { - return; - } + if (m_loaded || !m_ui->adblockCheckBox->isChecked()) { + return; + } - foreach (AdBlockSubscription* subscription, m_manager->subscriptions()) { - AdBlockTreeWidget *tree = new AdBlockTreeWidget(subscription, m_ui->tabWidget); - m_ui->tabWidget->addTab(tree, subscription->title()); - } + foreach (AdBlockSubscription* subscription, m_manager->subscriptions()) { + AdBlockTreeWidget* tree = new AdBlockTreeWidget(subscription, m_ui->tabWidget); + m_ui->tabWidget->addTab(tree, subscription->title()); + } - m_loaded = true; - - QTimer::singleShot(50, this, SLOT(loadSubscriptions())); + m_loaded = true; + QTimer::singleShot(50, this, SLOT(loadSubscriptions())); } diff --git a/src/network-web/adblock/adblockdialog.h b/src/network-web/adblock/adblockdialog.h index 914fe3386..f6f9f7118 100755 --- a/src/network-web/adblock/adblockdialog.h +++ b/src/network-web/adblock/adblockdialog.h @@ -30,42 +30,42 @@ class AdBlockManager; class AdBlockRule; class AdBlockDialog : public QWidget { - Q_OBJECT + Q_OBJECT - public: - explicit AdBlockDialog(QWidget* parent = 0); + public: + explicit AdBlockDialog(QWidget* parent = 0); - void showRule(const AdBlockRule* rule) const; + void showRule(const AdBlockRule* rule) const; - private slots: - void addRule(); - void removeRule(); + private slots: + void addRule(); + void removeRule(); - void addSubscription(); - void removeSubscription(); + void addSubscription(); + void removeSubscription(); - void currentChanged(int index); - void enableAdBlock(bool state); + void currentChanged(int index); + void enableAdBlock(bool state); - void aboutToShowMenu(); - void learnAboutRules(); + void aboutToShowMenu(); + void learnAboutRules(); - void loadSubscriptions(); - void load(); + void loadSubscriptions(); + void load(); - private: - AdBlockManager *m_manager; - AdBlockTreeWidget *m_currentTreeWidget; - AdBlockSubscription *m_currentSubscription; + private: + AdBlockManager* m_manager; + AdBlockTreeWidget* m_currentTreeWidget; + AdBlockSubscription* m_currentSubscription; - QAction *m_actionAddRule; - QAction *m_actionRemoveRule; - QAction *m_actionAddSubscription; - QAction *m_actionRemoveSubscription; + QAction* m_actionAddRule; + QAction* m_actionRemoveRule; + QAction* m_actionAddSubscription; + QAction* m_actionRemoveSubscription; - bool m_loaded; + bool m_loaded; - Ui::AdBlockDialog *m_ui; + Ui::AdBlockDialog* m_ui; }; #endif // ADBLOCKDIALOG_H diff --git a/src/network-web/adblock/adblockicon.cpp b/src/network-web/adblock/adblockicon.cpp index fca011a6b..7dbf08637 100755 --- a/src/network-web/adblock/adblockicon.cpp +++ b/src/network-web/adblock/adblockicon.cpp @@ -33,183 +33,172 @@ AdBlockIcon::AdBlockIcon(QWidget* parent) - : ClickableLabel(parent), m_menuAction(0), m_flashTimer(0), m_timerTicks(0), m_enabled(false) { - setCursor(Qt::PointingHandCursor); - setToolTip(tr("AdBlock lets you block unwanted content on web pages")); - setFixedSize(16, 16); - - connect(this, SIGNAL(clicked(QPoint)), this, SLOT(showMenu(QPoint))); - connect(AdBlockManager::instance(), SIGNAL(enabledChanged(bool)), this, SLOT(setEnabled(bool))); - - m_enabled = AdBlockManager::instance()->isEnabled(); + : ClickableLabel(parent), m_menuAction(0), m_flashTimer(0), m_timerTicks(0), m_enabled(false) { + setCursor(Qt::PointingHandCursor); + setToolTip(tr("AdBlock lets you block unwanted content on web pages")); + setFixedSize(16, 16); + connect(this, SIGNAL(clicked(QPoint)), this, SLOT(showMenu(QPoint))); + connect(AdBlockManager::instance(), SIGNAL(enabledChanged(bool)), this, SLOT(setEnabled(bool))); + m_enabled = AdBlockManager::instance()->isEnabled(); } AdBlockIcon::~AdBlockIcon() { - for (int i = 0; i < m_blockedPopups.count(); ++i) { - delete m_blockedPopups.at(i).first; - } + for (int i = 0; i < m_blockedPopups.count(); ++i) { + delete m_blockedPopups.at(i).first; + } } -void AdBlockIcon::popupBlocked(const QString &ruleString, const QUrl &url) { - int index = ruleString.lastIndexOf(QLatin1String(" (")); +void AdBlockIcon::popupBlocked(const QString& ruleString, const QUrl& url) { + int index = ruleString.lastIndexOf(QLatin1String(" (")); + const QString subscriptionName = ruleString.left(index); + const QString filter = ruleString.mid(index + 2, ruleString.size() - index - 3); + AdBlockSubscription* subscription = AdBlockManager::instance()->subscriptionByName(subscriptionName); - const QString subscriptionName = ruleString.left(index); - const QString filter = ruleString.mid(index + 2, ruleString.size() - index - 3); - AdBlockSubscription *subscription = AdBlockManager::instance()->subscriptionByName(subscriptionName); - if (filter.isEmpty() || !subscription) { - return; - } + if (filter.isEmpty() || !subscription) { + return; + } - QPair pair; - pair.first = new AdBlockRule(filter, subscription); - pair.second = url; - m_blockedPopups.append(pair); + QPair pair; + pair.first = new AdBlockRule(filter, subscription); + pair.second = url; + m_blockedPopups.append(pair); + qApp->showGuiMessage(tr("Blocked popup window"), tr("AdBlock blocked unwanted popup window."), QSystemTrayIcon::Information); - qApp->showGuiMessage(tr("Blocked popup window"), tr("AdBlock blocked unwanted popup window."), QSystemTrayIcon::Information); + if (!m_flashTimer) { + m_flashTimer = new QTimer(this); + } - if (!m_flashTimer) { - m_flashTimer = new QTimer(this); - } + if (m_flashTimer->isActive()) { + stopAnimation(); + } - if (m_flashTimer->isActive()) { - stopAnimation(); - } - - m_flashTimer->setInterval(500); - m_flashTimer->start(); - - connect(m_flashTimer, &QTimer::timeout, this, &AdBlockIcon::animateIcon); + m_flashTimer->setInterval(500); + m_flashTimer->start(); + connect(m_flashTimer, &QTimer::timeout, this, &AdBlockIcon::animateIcon); } -QAction *AdBlockIcon::menuAction() { - if (!m_menuAction) { - m_menuAction = new QAction(tr("AdBlock"), this); - m_menuAction->setMenu(new QMenu(this)); - connect(m_menuAction->menu(), SIGNAL(aboutToShow()), this, SLOT(createMenu())); - connect(m_menuAction, &QAction::triggered, AdBlockManager::instance(), &AdBlockManager::showDialog); - } +QAction* AdBlockIcon::menuAction() { + if (!m_menuAction) { + m_menuAction = new QAction(tr("AdBlock"), this); + m_menuAction->setMenu(new QMenu(this)); + connect(m_menuAction->menu(), SIGNAL(aboutToShow()), this, SLOT(createMenu())); + connect(m_menuAction, &QAction::triggered, AdBlockManager::instance(), &AdBlockManager::showDialog); + } - m_menuAction->setIcon(m_enabled ? qApp->icons()->miscIcon(ADBLOCK_ICON_ACTIVE) : qApp->icons()->miscIcon(ADBLOCK_ICON_DISABLED)); - return m_menuAction; + m_menuAction->setIcon(m_enabled ? qApp->icons()->miscIcon(ADBLOCK_ICON_ACTIVE) : qApp->icons()->miscIcon(ADBLOCK_ICON_DISABLED)); + return m_menuAction; } -void AdBlockIcon::createMenu(QMenu *menu) { - if (!menu) { - menu = qobject_cast(sender()); +void AdBlockIcon::createMenu(QMenu* menu) { + if (!menu) { + menu = qobject_cast(sender()); - if (!menu) { - return; - } - } + if (!menu) { + return; + } + } - menu->clear(); + menu->clear(); + AdBlockManager* manager = AdBlockManager::instance(); + AdBlockCustomList* customList = manager->customList(); + WebPage* page = qApp->mainForm()->tabWidget()->currentWidget()->webBrowser()->viewer()->page(); + const QUrl pageUrl = page->url(); + menu->addAction(tr("Show AdBlock &settings"), manager, SLOT(showDialog())); + menu->addSeparator(); - AdBlockManager* manager = AdBlockManager::instance(); - AdBlockCustomList* customList = manager->customList(); + if (!pageUrl.host().isEmpty() && m_enabled && manager->canRunOnScheme(pageUrl.scheme())) { + const QString host = page->url().host().contains(QLatin1String("www.")) ? pageUrl.host().mid(4) : pageUrl.host(); + const QString hostFilter = QString("@@||%1^$document").arg(host); + const QString pageFilter = QString("@@|%1|$document").arg(pageUrl.toString()); + QAction* act = menu->addAction(tr("Disable on %1").arg(host)); + act->setCheckable(true); + act->setChecked(customList->containsFilter(hostFilter)); + act->setData(hostFilter); + connect(act, SIGNAL(triggered()), this, SLOT(toggleCustomFilter())); + act = menu->addAction(tr("Disable only on this page")); + act->setCheckable(true); + act->setChecked(customList->containsFilter(pageFilter)); + act->setData(pageFilter); + connect(act, SIGNAL(triggered()), this, SLOT(toggleCustomFilter())); + menu->addSeparator(); + } - WebPage *page = qApp->mainForm()->tabWidget()->currentWidget()->webBrowser()->viewer()->page(); - const QUrl pageUrl = page->url(); + if (!m_blockedPopups.isEmpty()) { + menu->addAction(tr("Blocked popup windows"))->setEnabled(false); - menu->addAction(tr("Show AdBlock &settings"), manager, SLOT(showDialog())); - menu->addSeparator(); - - if (!pageUrl.host().isEmpty() && m_enabled && manager->canRunOnScheme(pageUrl.scheme())) { - const QString host = page->url().host().contains(QLatin1String("www.")) ? pageUrl.host().mid(4) : pageUrl.host(); - const QString hostFilter = QString("@@||%1^$document").arg(host); - const QString pageFilter = QString("@@|%1|$document").arg(pageUrl.toString()); - - QAction *act = menu->addAction(tr("Disable on %1").arg(host)); - act->setCheckable(true); - act->setChecked(customList->containsFilter(hostFilter)); - act->setData(hostFilter); - connect(act, SIGNAL(triggered()), this, SLOT(toggleCustomFilter())); - - act = menu->addAction(tr("Disable only on this page")); - act->setCheckable(true); - act->setChecked(customList->containsFilter(pageFilter)); - act->setData(pageFilter); - connect(act, SIGNAL(triggered()), this, SLOT(toggleCustomFilter())); - - menu->addSeparator(); - } - - if (!m_blockedPopups.isEmpty()) { - menu->addAction(tr("Blocked popup windows"))->setEnabled(false); - - for (int i = 0; i < m_blockedPopups.count(); i++) { - const QPair &pair = m_blockedPopups.at(i); - - QString address = pair.second.toString().right(55); - QString actionText = tr("%1 with (%2)").arg(address, pair.first->filter()).replace(QL1C('&'), QL1S("&&")); - - QAction *action = menu->addAction(actionText, manager, SLOT(showRule())); - action->setData(QVariant::fromValue((void*)pair.first)); - } - } + for (int i = 0; i < m_blockedPopups.count(); i++) { + const QPair& pair = m_blockedPopups.at(i); + QString address = pair.second.toString().right(55); + QString actionText = tr("%1 with (%2)").arg(address, pair.first->filter()).replace(QL1C('&'), QL1S("&&")); + QAction* action = menu->addAction(actionText, manager, SLOT(showRule())); + action->setData(QVariant::fromValue((void*)pair.first)); + } + } } -void AdBlockIcon::showMenu(const QPoint &pos) { - QMenu menu; - createMenu(&menu); - - menu.exec(pos); +void AdBlockIcon::showMenu(const QPoint& pos) { + QMenu menu; + createMenu(&menu); + menu.exec(pos); } void AdBlockIcon::toggleCustomFilter() { - QAction *action = qobject_cast(sender()); + QAction* action = qobject_cast(sender()); - if (!action) { - return; - } + if (!action) { + return; + } - const QString filter = action->data().toString(); - AdBlockManager *manager = AdBlockManager::instance(); - AdBlockCustomList *customList = manager->customList(); + const QString filter = action->data().toString(); + AdBlockManager* manager = AdBlockManager::instance(); + AdBlockCustomList* customList = manager->customList(); - if (customList->containsFilter(filter)) { - customList->removeFilter(filter); - } - else { - AdBlockRule *rule = new AdBlockRule(filter, customList); - customList->addRule(rule); - } + if (customList->containsFilter(filter)) { + customList->removeFilter(filter); + } + + else { + AdBlockRule* rule = new AdBlockRule(filter, customList); + customList->addRule(rule); + } } void AdBlockIcon::animateIcon() { - ++m_timerTicks; + ++m_timerTicks; - if (m_timerTicks > 10) { - stopAnimation(); - return; - } + if (m_timerTicks > 10) { + stopAnimation(); + return; + } - if (pixmap()->isNull()) { - setPixmap(qApp->icons()->miscIcon(ADBLOCK_ICON_ACTIVE).pixmap(16)); - } - else { - setPixmap(QPixmap()); - } + if (pixmap()->isNull()) { + setPixmap(qApp->icons()->miscIcon(ADBLOCK_ICON_ACTIVE).pixmap(16)); + } + + else { + setPixmap(QPixmap()); + } } void AdBlockIcon::stopAnimation() { - m_timerTicks = 0; - m_flashTimer->stop(); - disconnect(m_flashTimer, SIGNAL(timeout()), this, SLOT(animateIcon())); - - setEnabled(m_enabled); + m_timerTicks = 0; + m_flashTimer->stop(); + disconnect(m_flashTimer, SIGNAL(timeout()), this, SLOT(animateIcon())); + setEnabled(m_enabled); } void AdBlockIcon::setEnabled(bool enabled) { - if (enabled) { - setPixmap(qApp->icons()->miscIcon(ADBLOCK_ICON_ACTIVE).pixmap(16)); - } - else { - setPixmap(qApp->icons()->miscIcon(ADBLOCK_ICON_DISABLED).pixmap(16)); - } + if (enabled) { + setPixmap(qApp->icons()->miscIcon(ADBLOCK_ICON_ACTIVE).pixmap(16)); + } - if (m_menuAction != nullptr) { - m_menuAction->setIcon(enabled ? qApp->icons()->miscIcon(ADBLOCK_ICON_ACTIVE) : qApp->icons()->miscIcon(ADBLOCK_ICON_DISABLED)); - } + else { + setPixmap(qApp->icons()->miscIcon(ADBLOCK_ICON_DISABLED).pixmap(16)); + } - m_enabled = enabled; + if (m_menuAction != nullptr) { + m_menuAction->setIcon(enabled ? qApp->icons()->miscIcon(ADBLOCK_ICON_ACTIVE) : qApp->icons()->miscIcon(ADBLOCK_ICON_DISABLED)); + } + + m_enabled = enabled; } diff --git a/src/network-web/adblock/adblockicon.h b/src/network-web/adblock/adblockicon.h index 8a4905cfc..4e2ba886b 100755 --- a/src/network-web/adblock/adblockicon.h +++ b/src/network-web/adblock/adblockicon.h @@ -32,34 +32,34 @@ class QTimer; class BrowserWindow; class AdBlockIcon : public ClickableLabel { - Q_OBJECT + Q_OBJECT - public: - explicit AdBlockIcon(QWidget* parent = 0); - virtual ~AdBlockIcon(); + public: + explicit AdBlockIcon(QWidget* parent = 0); + virtual ~AdBlockIcon(); - void popupBlocked(const QString &ruleString, const QUrl &url); - QAction *menuAction(); + void popupBlocked(const QString& ruleString, const QUrl& url); + QAction* menuAction(); - public slots: - void setEnabled(bool enabled); - void createMenu(QMenu *menu = 0); + public slots: + void setEnabled(bool enabled); + void createMenu(QMenu* menu = 0); - private slots: - void showMenu(const QPoint &pos); - void toggleCustomFilter(); + private slots: + void showMenu(const QPoint& pos); + void toggleCustomFilter(); - void animateIcon(); - void stopAnimation(); + void animateIcon(); + void stopAnimation(); - private: - QAction *m_menuAction; + private: + QAction* m_menuAction; - QVector > m_blockedPopups; - QTimer *m_flashTimer; + QVector> m_blockedPopups; + QTimer* m_flashTimer; - int m_timerTicks; - bool m_enabled; + int m_timerTicks; + bool m_enabled; }; #endif // ADBLOCKICON_H diff --git a/src/network-web/adblock/adblockmanager.cpp b/src/network-web/adblock/adblockmanager.cpp index c54261fd5..89f813a3e 100755 --- a/src/network-web/adblock/adblockmanager.cpp +++ b/src/network-web/adblock/adblockmanager.cpp @@ -42,353 +42,349 @@ Q_GLOBAL_STATIC(AdBlockManager, qz_adblock_manager) AdBlockManager::AdBlockManager(QObject* parent) - : QObject(parent),m_loaded(false),m_enabled(true),m_matcher(new AdBlockMatcher(this)), m_interceptor(new AdBlockUrlInterceptor(this)) { - load(); + : QObject(parent), m_loaded(false), m_enabled(true), m_matcher(new AdBlockMatcher(this)), m_interceptor(new AdBlockUrlInterceptor(this)) { + load(); } -AdBlockManager::~AdBlockManager() { qDeleteAll(m_subscriptions); } +AdBlockManager::~AdBlockManager() { + qDeleteAll(m_subscriptions); +} -AdBlockManager* AdBlockManager::instance() { return qz_adblock_manager(); } +AdBlockManager* AdBlockManager::instance() { + return qz_adblock_manager(); +} void AdBlockManager::setEnabled(bool enabled) { - if (m_enabled == enabled) { - return; - } + if (m_enabled == enabled) { + return; + } - m_enabled = enabled; - emit enabledChanged(enabled); + m_enabled = enabled; + emit enabledChanged(enabled); + qApp->settings()->setValue(GROUP(AdBlock), AdBlock::AdBlockEnabled, m_enabled); + load(); + // TODO: Reload user stylesheet. + // mApp->reloadUserStyleSheet(); + QMutexLocker locker(&m_mutex); - qApp->settings()->setValue(GROUP(AdBlock), AdBlock::AdBlockEnabled, m_enabled); - load(); - // TODO: Reload user stylesheet. - // mApp->reloadUserStyleSheet(); + if (m_enabled) { + m_matcher->update(); + } - QMutexLocker locker(&m_mutex); - - if (m_enabled) { - m_matcher->update(); - } - else { - m_matcher->clear(); - } + else { + m_matcher->clear(); + } } QList AdBlockManager::subscriptions() const { - return m_subscriptions; + return m_subscriptions; } bool AdBlockManager::block(QWebEngineUrlRequestInfo& request) { - QMutexLocker locker(&m_mutex); + QMutexLocker locker(&m_mutex); - if (!isEnabled()) { - return false; - } + if (!isEnabled()) { + return false; + } - const QString urlString = request.requestUrl().toEncoded().toLower(); - const QString urlDomain = request.requestUrl().host().toLower(); - const QString urlScheme = request.requestUrl().scheme().toLower(); + const QString urlString = request.requestUrl().toEncoded().toLower(); + const QString urlDomain = request.requestUrl().host().toLower(); + const QString urlScheme = request.requestUrl().scheme().toLower(); - if (!canRunOnScheme(urlScheme) || !canBeBlocked(request.firstPartyUrl())) { - return false; - } + if (!canRunOnScheme(urlScheme) || !canBeBlocked(request.firstPartyUrl())) { + return false; + } - bool res = false; - const AdBlockRule* blockedRule = m_matcher->match(request, urlDomain, urlString); + bool res = false; + const AdBlockRule* blockedRule = m_matcher->match(request, urlDomain, urlString); - if (blockedRule) { - res = true; + if (blockedRule) { + res = true; - if (request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeMainFrame) { - // We are blocking main URL frame, we can display "AdBlock error page" or - // redirect to somewhere. - // TODO: dodělat lepší + if (request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeMainFrame) { + // We are blocking main URL frame, we can display "AdBlock error page" or + // redirect to somewhere. + // TODO: dodělat lepší + // TODO request.redirect() přesměrovat na "chybovou stranku"; + // QUrl url(QSL("rssguard:adblock")); + // QUrlQuery query; + // query.addQueryItem(QSL("rule"), blockedRule->filter()); + // query.addQueryItem(QSL("subscription"), + // blockedRule->subscription()->title()); + // url.setQuery(query); + // request.redirect(url); + request.block(true); + } - // TODO request.redirect() přesměrovat na "chybovou stranku"; - // QUrl url(QSL("rssguard:adblock")); - // QUrlQuery query; - // query.addQueryItem(QSL("rule"), blockedRule->filter()); - // query.addQueryItem(QSL("subscription"), - // blockedRule->subscription()->title()); - // url.setQuery(query); - // request.redirect(url); + else { + request.block(true); + } + } - request.block(true); - } - else { - request.block(true); - } - } - - return res; + return res; } -QStringList AdBlockManager::disabledRules() const { return m_disabledRules; } +QStringList AdBlockManager::disabledRules() const { + return m_disabledRules; +} void AdBlockManager::addDisabledRule(const QString& filter) { - m_disabledRules.append(filter); + m_disabledRules.append(filter); } void AdBlockManager::removeDisabledRule(const QString& filter) { - m_disabledRules.removeOne(filter); + m_disabledRules.removeOne(filter); } bool AdBlockManager::addSubscriptionFromUrl(const QUrl& url) { - const QList> queryItems = QUrlQuery(url).queryItems(QUrl::FullyDecoded); + const QList> queryItems = QUrlQuery(url).queryItems(QUrl::FullyDecoded); + QString subscriptionTitle; + QString subscriptionUrl; - QString subscriptionTitle; - QString subscriptionUrl; + for (int i = 0; i < queryItems.count(); ++i) { + QPair pair = queryItems.at(i); - for (int i = 0; i < queryItems.count(); ++i) { - QPair pair = queryItems.at(i); + if (pair.first == QL1S("location")) { + subscriptionUrl = pair.second; + } - if (pair.first == QL1S("location")) { - subscriptionUrl = pair.second; - } - else if (pair.first == QL1S("title")) { - subscriptionTitle = pair.second; - } - } + else if (pair.first == QL1S("title")) { + subscriptionTitle = pair.second; + } + } - if (subscriptionTitle.isEmpty() || subscriptionUrl.isEmpty()) { - return false; - } + if (subscriptionTitle.isEmpty() || subscriptionUrl.isEmpty()) { + return false; + } - const QString message = tr("Do you want to add %1 subscription?").arg(subscriptionTitle); + const QString message = tr("Do you want to add %1 subscription?").arg(subscriptionTitle); + QMessageBox::StandardButton result = QMessageBox::question(nullptr, tr("Add AdBlock subscription"), message, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - QMessageBox::StandardButton result = QMessageBox::question(nullptr, tr("Add AdBlock subscription"), message, - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (result == QMessageBox::Yes) { + AdBlockManager::instance()->addSubscription(subscriptionTitle, subscriptionUrl); + AdBlockManager::instance()->showDialog(); + } - if (result == QMessageBox::Yes) { - AdBlockManager::instance()->addSubscription(subscriptionTitle, subscriptionUrl); - AdBlockManager::instance()->showDialog(); - } - - return true; + return true; } AdBlockSubscription* AdBlockManager::addSubscription(const QString& title, const QString& url) { - if (title.isEmpty() || url.isEmpty()) { - return 0; - } + if (title.isEmpty() || url.isEmpty()) { + return 0; + } - QString fileName = title + QSL(".txt"); - QString filePath = storedListsPath() + QDir::separator() + fileName; - QByteArray data = QString("Title: %1\nUrl: %2\n[Adblock Plus 1.1.1]").arg(title, url).toLatin1(); + QString fileName = title + QSL(".txt"); + QString filePath = storedListsPath() + QDir::separator() + fileName; + QByteArray data = QString("Title: %1\nUrl: %2\n[Adblock Plus 1.1.1]").arg(title, url).toLatin1(); + QSaveFile file(filePath); - QSaveFile file(filePath); - if (!file.open(QFile::WriteOnly)) { - qWarning("Cannot save AdBlock subscription to file '%s'.", qPrintable(filePath)); - return 0; - } + if (!file.open(QFile::WriteOnly)) { + qWarning("Cannot save AdBlock subscription to file '%s'.", qPrintable(filePath)); + return 0; + } - file.write(data); - file.commit(); - - AdBlockSubscription *subscription = new AdBlockSubscription(title, this); - subscription->setUrl(QUrl(url)); - subscription->setFilePath(filePath); - subscription->loadSubscription(m_disabledRules); - - m_subscriptions.insert(m_subscriptions.count() - 1, subscription); - - // TODO: po změně subskripce přehrat user css? - // connect(subscription, SIGNAL(subscriptionUpdated()), mApp, - // SLOT(reloadUserStyleSheet())); - connect(subscription, SIGNAL(subscriptionChanged()), this, SLOT(updateMatcher())); - - return subscription; + file.write(data); + file.commit(); + AdBlockSubscription* subscription = new AdBlockSubscription(title, this); + subscription->setUrl(QUrl(url)); + subscription->setFilePath(filePath); + subscription->loadSubscription(m_disabledRules); + m_subscriptions.insert(m_subscriptions.count() - 1, subscription); + // TODO: po změně subskripce přehrat user css? + // connect(subscription, SIGNAL(subscriptionUpdated()), mApp, + // SLOT(reloadUserStyleSheet())); + connect(subscription, SIGNAL(subscriptionChanged()), this, SLOT(updateMatcher())); + return subscription; } -bool AdBlockManager::removeSubscription(AdBlockSubscription *subscription) { - QMutexLocker locker(&m_mutex); +bool AdBlockManager::removeSubscription(AdBlockSubscription* subscription) { + QMutexLocker locker(&m_mutex); - if (!m_subscriptions.contains(subscription) || !subscription->canBeRemoved()) { - return false; - } + if (!m_subscriptions.contains(subscription) || !subscription->canBeRemoved()) { + return false; + } - QFile(subscription->filePath()).remove(); - m_subscriptions.removeOne(subscription); - - m_matcher->update(); - delete subscription; - - return true; + QFile(subscription->filePath()).remove(); + m_subscriptions.removeOne(subscription); + m_matcher->update(); + delete subscription; + return true; } -AdBlockCustomList *AdBlockManager::customList() const { - foreach (AdBlockSubscription *subscription, m_subscriptions) { - AdBlockCustomList *list = qobject_cast(subscription); +AdBlockCustomList* AdBlockManager::customList() const { + foreach (AdBlockSubscription* subscription, m_subscriptions) { + AdBlockCustomList* list = qobject_cast(subscription); - if (list) { - return list; - } - } + if (list) { + return list; + } + } - return 0; + return 0; } QString AdBlockManager::storedListsPath() { - return qApp->getUserDataPath() + QDir::separator() + ADBLOCK_LISTS_SUBDIRECTORY; + return qApp->getUserDataPath() + QDir::separator() + ADBLOCK_LISTS_SUBDIRECTORY; } void AdBlockManager::load() { - QMutexLocker locker(&m_mutex); + QMutexLocker locker(&m_mutex); - if (m_loaded) { - return; - } + if (m_loaded) { + return; + } - m_enabled = qApp->settings()->value(GROUP(AdBlock), SETTING(AdBlock::AdBlockEnabled)).toBool(); - m_disabledRules = qApp->settings()->value(GROUP(AdBlock), SETTING(AdBlock::DisabledRules)).toStringList(); - QDateTime lastUpdate =qApp->settings()->value(GROUP(AdBlock), SETTING(AdBlock::LastUpdatedOn)).toDateTime(); + m_enabled = qApp->settings()->value(GROUP(AdBlock), SETTING(AdBlock::AdBlockEnabled)).toBool(); + m_disabledRules = qApp->settings()->value(GROUP(AdBlock), SETTING(AdBlock::DisabledRules)).toStringList(); + QDateTime lastUpdate = qApp->settings()->value(GROUP(AdBlock), SETTING(AdBlock::LastUpdatedOn)).toDateTime(); - if (!m_enabled) { - return; - } + if (!m_enabled) { + return; + } - QDir adblockDir(storedListsPath()); + QDir adblockDir(storedListsPath()); - // Create if neccessary - if (!adblockDir.exists()) { - QDir().mkpath(storedListsPath()); - } + // Create if neccessary + if (!adblockDir.exists()) { + QDir().mkpath(storedListsPath()); + } - foreach (const QString& fileName, adblockDir.entryList(QStringList("*.txt"), QDir::Files)) { - if (fileName == ADBLOCK_CUSTOMLIST_NAME) { - continue; - } + foreach (const QString& fileName, adblockDir.entryList(QStringList("*.txt"), QDir::Files)) { + if (fileName == ADBLOCK_CUSTOMLIST_NAME) { + continue; + } - const QString absolutePath = adblockDir.absoluteFilePath(fileName); - QFile file(absolutePath); + const QString absolutePath = adblockDir.absoluteFilePath(fileName); + QFile file(absolutePath); - if (!file.open(QFile::ReadOnly)) { - continue; - } + if (!file.open(QFile::ReadOnly)) { + continue; + } - QTextStream textStream(&file); - textStream.setCodec("UTF-8"); - QString title = textStream.readLine(1024).remove(QLatin1String("Title: ")); - QUrl url = QUrl(textStream.readLine(1024).remove(QLatin1String("Url: "))); + QTextStream textStream(&file); + textStream.setCodec("UTF-8"); + QString title = textStream.readLine(1024).remove(QLatin1String("Title: ")); + QUrl url = QUrl(textStream.readLine(1024).remove(QLatin1String("Url: "))); - if (title.isEmpty() || !url.isValid()) { - qWarning("Invalid AdBlock subscription file '%s'.", qPrintable(absolutePath)); - continue; - } + if (title.isEmpty() || !url.isValid()) { + qWarning("Invalid AdBlock subscription file '%s'.", qPrintable(absolutePath)); + continue; + } - AdBlockSubscription *subscription = new AdBlockSubscription(title, this); - subscription->setUrl(url); - subscription->setFilePath(absolutePath); + AdBlockSubscription* subscription = new AdBlockSubscription(title, this); + subscription->setUrl(url); + subscription->setFilePath(absolutePath); + m_subscriptions.append(subscription); + } - m_subscriptions.append(subscription); - } + // Append CustomList. + AdBlockCustomList* customList = new AdBlockCustomList(this); + m_subscriptions.append(customList); - // Append CustomList. - AdBlockCustomList *customList = new AdBlockCustomList(this); - m_subscriptions.append(customList); + // Load all subscriptions + foreach (AdBlockSubscription* subscription, m_subscriptions) { + subscription->loadSubscription(m_disabledRules); + // TODO: po zmene subskripce prehrat user css? + // connect(subscription, SIGNAL(subscriptionUpdated()), mApp, + // SLOT(reloadUserStyleSheet())); + connect(subscription, SIGNAL(subscriptionChanged()), this, SLOT(updateMatcher())); + } - // Load all subscriptions - foreach (AdBlockSubscription *subscription, m_subscriptions) { - subscription->loadSubscription(m_disabledRules); + if (lastUpdate.addDays(ADBLOCK_UPDATE_DAYS_INTERVAL) < QDateTime::currentDateTime()) { + QTimer::singleShot(1000 * 60, this, SLOT(updateAllSubscriptions())); + } - // TODO: po zmene subskripce prehrat user css? - // connect(subscription, SIGNAL(subscriptionUpdated()), mApp, - // SLOT(reloadUserStyleSheet())); - connect(subscription, SIGNAL(subscriptionChanged()), this, SLOT(updateMatcher())); - } - - if (lastUpdate.addDays(ADBLOCK_UPDATE_DAYS_INTERVAL) < QDateTime::currentDateTime()) { - QTimer::singleShot(1000 * 60, this, SLOT(updateAllSubscriptions())); - } - - m_matcher->update(); - m_loaded = true; - - qApp->urlIinterceptor()->installUrlInterceptor(m_interceptor); + m_matcher->update(); + m_loaded = true; + qApp->urlIinterceptor()->installUrlInterceptor(m_interceptor); } void AdBlockManager::updateMatcher() { - QMutexLocker locker(&m_mutex); - - m_matcher->update(); + QMutexLocker locker(&m_mutex); + m_matcher->update(); } void AdBlockManager::updateAllSubscriptions() { - foreach (AdBlockSubscription* subscription, m_subscriptions) { - subscription->updateSubscription(); - } + foreach (AdBlockSubscription* subscription, m_subscriptions) { + subscription->updateSubscription(); + } - qApp->settings()->setValue(GROUP(AdBlock), AdBlock::LastUpdatedOn, QDateTime::currentDateTime()); + qApp->settings()->setValue(GROUP(AdBlock), AdBlock::LastUpdatedOn, QDateTime::currentDateTime()); } void AdBlockManager::save() { - if (!m_loaded) { - return; - } + if (!m_loaded) { + return; + } - foreach (AdBlockSubscription* subscription, m_subscriptions) { - subscription->saveSubscription(); - } + foreach (AdBlockSubscription* subscription, m_subscriptions) { + subscription->saveSubscription(); + } - qApp->settings()->setValue(GROUP(AdBlock), AdBlock::AdBlockEnabled, m_enabled); - qApp->settings()->setValue(GROUP(AdBlock), AdBlock::DisabledRules, m_disabledRules); + qApp->settings()->setValue(GROUP(AdBlock), AdBlock::AdBlockEnabled, m_enabled); + qApp->settings()->setValue(GROUP(AdBlock), AdBlock::DisabledRules, m_disabledRules); } -bool AdBlockManager::isEnabled() const { return m_enabled; } +bool AdBlockManager::isEnabled() const { + return m_enabled; +} bool AdBlockManager::canRunOnScheme(const QString& scheme) const { - return !(scheme == QSL("file") || scheme == QSL("qrc") || scheme == QSL("data") || scheme == QSL("abp")); + return !(scheme == QSL("file") || scheme == QSL("qrc") || scheme == QSL("data") || scheme == QSL("abp")); } bool AdBlockManager::canBeBlocked(const QUrl& url) const { - return !m_matcher->adBlockDisabledForUrl(url); + return !m_matcher->adBlockDisabledForUrl(url); } QString AdBlockManager::elementHidingRules(const QUrl& url) const { - if (!isEnabled() || !canRunOnScheme(url.scheme()) || !canBeBlocked(url)) { - return QString(); - } - else { - return m_matcher->elementHidingRules(); - } + if (!isEnabled() || !canRunOnScheme(url.scheme()) || !canBeBlocked(url)) { + return QString(); + } + + else { + return m_matcher->elementHidingRules(); + } } QString AdBlockManager::elementHidingRulesForDomain(const QUrl& url) const { - if (!isEnabled() || !canRunOnScheme(url.scheme()) || !canBeBlocked(url)) { - return QString(); - } - else { - return m_matcher->elementHidingRulesForDomain(url.host()); - } + if (!isEnabled() || !canRunOnScheme(url.scheme()) || !canBeBlocked(url)) { + return QString(); + } + + else { + return m_matcher->elementHidingRulesForDomain(url.host()); + } } AdBlockSubscription* AdBlockManager::subscriptionByName(const QString& name) const { - foreach (AdBlockSubscription *subscription, m_subscriptions) { - if (subscription->title() == name) { - return subscription; - } - } + foreach (AdBlockSubscription* subscription, m_subscriptions) { + if (subscription->title() == name) { + return subscription; + } + } - return 0; + return 0; } -AdBlockDialog *AdBlockManager::showDialog() { - if (!m_adBlockDialog) { - m_adBlockDialog = new AdBlockDialog; - } +AdBlockDialog* AdBlockManager::showDialog() { + if (!m_adBlockDialog) { + m_adBlockDialog = new AdBlockDialog; + } - m_adBlockDialog.data()->show(); - m_adBlockDialog.data()->raise(); - m_adBlockDialog.data()->activateWindow(); - - return m_adBlockDialog.data(); + m_adBlockDialog.data()->show(); + m_adBlockDialog.data()->raise(); + m_adBlockDialog.data()->activateWindow(); + return m_adBlockDialog.data(); } void AdBlockManager::showRule() { - if (QAction *action = qobject_cast(sender())) { - const AdBlockRule* rule = static_cast(action->data().value()); + if (QAction* action = qobject_cast(sender())) { + const AdBlockRule* rule = static_cast(action->data().value()); - if (rule) { - showDialog()->showRule(rule); - } - } + if (rule) { + showDialog()->showRule(rule); + } + } } diff --git a/src/network-web/adblock/adblockmanager.h b/src/network-web/adblock/adblockmanager.h index c56640eb6..309d57bed 100755 --- a/src/network-web/adblock/adblockmanager.h +++ b/src/network-web/adblock/adblockmanager.h @@ -35,67 +35,66 @@ class AdBlockDialog; class AdBlockUrlInterceptor; class AdBlockManager : public QObject { - Q_OBJECT + Q_OBJECT - public: - explicit AdBlockManager(QObject* parent = 0); - virtual ~AdBlockManager(); + public: + explicit AdBlockManager(QObject* parent = 0); + virtual ~AdBlockManager(); - void load(); - void save(); + void load(); + void save(); - bool isEnabled() const; - bool canRunOnScheme(const QString& scheme) const; + bool isEnabled() const; + bool canRunOnScheme(const QString& scheme) const; - QString elementHidingRules(const QUrl& url) const; - QString elementHidingRulesForDomain(const QUrl& url) const; + QString elementHidingRules(const QUrl& url) const; + QString elementHidingRulesForDomain(const QUrl& url) const; - AdBlockSubscription* subscriptionByName(const QString& name) const; - QList subscriptions() const; + AdBlockSubscription* subscriptionByName(const QString& name) const; + QList subscriptions() const; - bool block(QWebEngineUrlRequestInfo& request); + bool block(QWebEngineUrlRequestInfo& request); - QStringList disabledRules() const; - void addDisabledRule(const QString& filter); - void removeDisabledRule(const QString& filter); + QStringList disabledRules() const; + void addDisabledRule(const QString& filter); + void removeDisabledRule(const QString& filter); - bool addSubscriptionFromUrl(const QUrl& url); + bool addSubscriptionFromUrl(const QUrl& url); - AdBlockSubscription *addSubscription(const QString& title, - const QString &url); - bool removeSubscription(AdBlockSubscription* subscription); + AdBlockSubscription* addSubscription(const QString& title, const QString& url); + bool removeSubscription(AdBlockSubscription* subscription); - AdBlockCustomList *customList() const; + AdBlockCustomList* customList() const; - static QString storedListsPath(); + static QString storedListsPath(); - static AdBlockManager *instance(); + static AdBlockManager* instance(); - signals: - void enabledChanged(bool enabled); + signals: + void enabledChanged(bool enabled); - public slots: - void setEnabled(bool enabled); - void showRule(); + public slots: + void setEnabled(bool enabled); + void showRule(); - void updateMatcher(); - void updateAllSubscriptions(); + void updateMatcher(); + void updateAllSubscriptions(); - AdBlockDialog *showDialog(); + AdBlockDialog* showDialog(); - private: - inline bool canBeBlocked(const QUrl &url) const; + private: + inline bool canBeBlocked(const QUrl& url) const; - bool m_loaded; - bool m_enabled; + bool m_loaded; + bool m_enabled; - QList m_subscriptions; - AdBlockMatcher* m_matcher; - QStringList m_disabledRules; + QList m_subscriptions; + AdBlockMatcher* m_matcher; + QStringList m_disabledRules; - AdBlockUrlInterceptor* m_interceptor; - QPointer m_adBlockDialog; - QMutex m_mutex; + AdBlockUrlInterceptor* m_interceptor; + QPointer m_adBlockDialog; + QMutex m_mutex; }; #endif // ADBLOCKMANAGER_H diff --git a/src/network-web/adblock/adblockmatcher.cpp b/src/network-web/adblock/adblockmatcher.cpp index c2b0b7e20..b2ef565f0 100755 --- a/src/network-web/adblock/adblockmatcher.cpp +++ b/src/network-web/adblock/adblockmatcher.cpp @@ -24,212 +24,218 @@ #include "definitions/definitions.h" -AdBlockMatcher::AdBlockMatcher(AdBlockManager *manager) - : QObject(manager), m_manager(manager) { +AdBlockMatcher::AdBlockMatcher(AdBlockManager* manager) + : QObject(manager), m_manager(manager) { } AdBlockMatcher::~AdBlockMatcher() { - clear(); + clear(); } -const AdBlockRule *AdBlockMatcher::match(const QWebEngineUrlRequestInfo &request, const QString &urlDomain, const QString &urlString) const { - // Exception rules. - if (m_networkExceptionTree.find(request, urlDomain, urlString)) { - return 0; - } +const AdBlockRule* AdBlockMatcher::match(const QWebEngineUrlRequestInfo& request, const QString& urlDomain, const QString& urlString) const { + // Exception rules. + if (m_networkExceptionTree.find(request, urlDomain, urlString)) { + return 0; + } - int count = m_networkExceptionRules.count(); + int count = m_networkExceptionRules.count(); - for (int i = 0; i < count; ++i) { - const AdBlockRule *rule = m_networkExceptionRules.at(i); + for (int i = 0; i < count; ++i) { + const AdBlockRule* rule = m_networkExceptionRules.at(i); - if (rule->networkMatch(request, urlDomain, urlString)) { - return 0; - } - } + if (rule->networkMatch(request, urlDomain, urlString)) { + return 0; + } + } - // Block rules. - if (const AdBlockRule* rule = m_networkBlockTree.find(request, urlDomain, urlString)) { - return rule; - } + // Block rules. + if (const AdBlockRule* rule = m_networkBlockTree.find(request, urlDomain, urlString)) { + return rule; + } - count = m_networkBlockRules.count(); + count = m_networkBlockRules.count(); - for (int i = 0; i < count; ++i) { - const AdBlockRule *rule = m_networkBlockRules.at(i); + for (int i = 0; i < count; ++i) { + const AdBlockRule* rule = m_networkBlockRules.at(i); - if (rule->networkMatch(request, urlDomain, urlString)) { - return rule; - } - } + if (rule->networkMatch(request, urlDomain, urlString)) { + return rule; + } + } - return 0; + return 0; } -bool AdBlockMatcher::adBlockDisabledForUrl(const QUrl &url) const { - int count = m_documentRules.count(); +bool AdBlockMatcher::adBlockDisabledForUrl(const QUrl& url) const { + int count = m_documentRules.count(); - for (int i = 0; i < count; ++i) { - if (m_documentRules.at(i)->urlMatch(url)) { - return true; - } - } + for (int i = 0; i < count; ++i) { + if (m_documentRules.at(i)->urlMatch(url)) { + return true; + } + } - return false; + return false; } -bool AdBlockMatcher::elemHideDisabledForUrl(const QUrl &url) const { - if (adBlockDisabledForUrl(url)) { - return true; - } +bool AdBlockMatcher::elemHideDisabledForUrl(const QUrl& url) const { + if (adBlockDisabledForUrl(url)) { + return true; + } - int count = m_elemhideRules.count(); + int count = m_elemhideRules.count(); - for (int i = 0; i < count; ++i) { - if (m_elemhideRules.at(i)->urlMatch(url)) { - return true; - } - } + for (int i = 0; i < count; ++i) { + if (m_elemhideRules.at(i)->urlMatch(url)) { + return true; + } + } - return false; + return false; } QString AdBlockMatcher::elementHidingRules() const { - return m_elementHidingRules; + return m_elementHidingRules; } -QString AdBlockMatcher::elementHidingRulesForDomain(const QString &domain) const { - QString rules; - int addedRulesCount = 0; - int count = m_domainRestrictedCssRules.count(); +QString AdBlockMatcher::elementHidingRulesForDomain(const QString& domain) const { + QString rules; + int addedRulesCount = 0; + int count = m_domainRestrictedCssRules.count(); - for (int i = 0; i < count; ++i) { - const AdBlockRule *rule = m_domainRestrictedCssRules.at(i); + for (int i = 0; i < count; ++i) { + const AdBlockRule* rule = m_domainRestrictedCssRules.at(i); - if (!rule->matchDomain(domain)) { - continue; - } + if (!rule->matchDomain(domain)) { + continue; + } - if (Q_UNLIKELY(addedRulesCount == 1000)) { - rules.append(rule->cssSelector()); - rules.append(QSL("{display:none !important;}\n")); - addedRulesCount = 0; - } - else { - rules.append(rule->cssSelector() + QLatin1Char(',')); - addedRulesCount++; - } - } + if (Q_UNLIKELY(addedRulesCount == 1000)) { + rules.append(rule->cssSelector()); + rules.append(QSL("{display:none !important;}\n")); + addedRulesCount = 0; + } - if (addedRulesCount != 0) { - rules = rules.left(rules.size() - 1); - rules.append(QSL("{display:none !important;}\n")); - } + else { + rules.append(rule->cssSelector() + QLatin1Char(',')); + addedRulesCount++; + } + } - return rules; + if (addedRulesCount != 0) { + rules = rules.left(rules.size() - 1); + rules.append(QSL("{display:none !important;}\n")); + } + + return rules; } void AdBlockMatcher::update() { - clear(); + clear(); + QHash cssRulesHash; + QVector exceptionCssRules; - QHash cssRulesHash; - QVector exceptionCssRules; + foreach (AdBlockSubscription* subscription, m_manager->subscriptions()) { + foreach (const AdBlockRule* rule, subscription->allRules()) { + // Don't add internally disabled rules to cache. + if (rule->isInternalDisabled()) { + continue; + } - foreach (AdBlockSubscription *subscription, m_manager->subscriptions()) { - foreach (const AdBlockRule *rule, subscription->allRules()) { - // Don't add internally disabled rules to cache. - if (rule->isInternalDisabled()) { - continue; - } + if (rule->isCssRule()) { + // We will add only enabled css rules to cache, because there is no enabled/disabled + // check on match. They are directly embedded to pages. + if (!rule->isEnabled()) { + continue; + } - if (rule->isCssRule()) { - // We will add only enabled css rules to cache, because there is no enabled/disabled - // check on match. They are directly embedded to pages. - if (!rule->isEnabled()) { - continue; - } + if (rule->isException()) { + exceptionCssRules.append(rule); + } - if (rule->isException()) { - exceptionCssRules.append(rule); - } - else { - cssRulesHash.insert(rule->cssSelector(), rule); - } - } - else if (rule->isDocument()) { - m_documentRules.append(rule); - } - else if (rule->isElemhide()) { - m_elemhideRules.append(rule); - } - else if (rule->isException()) { - if (!m_networkExceptionTree.add(rule)) { - m_networkExceptionRules.append(rule); - } - } - else { - if (!m_networkBlockTree.add(rule)) { - m_networkBlockRules.append(rule); - } - } - } - } + else { + cssRulesHash.insert(rule->cssSelector(), rule); + } + } - foreach (const AdBlockRule *rule, exceptionCssRules) { - const AdBlockRule *originalRule = cssRulesHash.value(rule->cssSelector()); + else if (rule->isDocument()) { + m_documentRules.append(rule); + } - // If we don't have this selector, the exception does nothing. - if (!originalRule) { - continue; - } + else if (rule->isElemhide()) { + m_elemhideRules.append(rule); + } - AdBlockRule *copiedRule = originalRule->copy(); - copiedRule->m_options |= AdBlockRule::DomainRestrictedOption; - copiedRule->m_blockedDomains.append(rule->m_allowedDomains); + else if (rule->isException()) { + if (!m_networkExceptionTree.add(rule)) { + m_networkExceptionRules.append(rule); + } + } - cssRulesHash[rule->cssSelector()] = copiedRule; - m_createdRules.append(copiedRule); - } + else { + if (!m_networkBlockTree.add(rule)) { + m_networkBlockRules.append(rule); + } + } + } + } - // Apparently, excessive amount of selectors for one CSS rule is not what WebKit likes. - // (In my testings, 4931 is the number that makes it crash). - // So let's split it by 1000 selectors. - int hidingRulesCount = 0; - QHashIterator it(cssRulesHash); + foreach (const AdBlockRule* rule, exceptionCssRules) { + const AdBlockRule* originalRule = cssRulesHash.value(rule->cssSelector()); - while (it.hasNext()) { - it.next(); - const AdBlockRule *rule = it.value(); + // If we don't have this selector, the exception does nothing. + if (!originalRule) { + continue; + } - if (rule->isDomainRestricted()) { - m_domainRestrictedCssRules.append(rule); - } - else if (Q_UNLIKELY(hidingRulesCount == 1000)) { - m_elementHidingRules.append(rule->cssSelector()); - m_elementHidingRules.append(QL1S("{display:none !important;} ")); - hidingRulesCount = 0; - } - else { - m_elementHidingRules.append(rule->cssSelector() + QLatin1Char(',')); - hidingRulesCount++; - } - } + AdBlockRule* copiedRule = originalRule->copy(); + copiedRule->m_options |= AdBlockRule::DomainRestrictedOption; + copiedRule->m_blockedDomains.append(rule->m_allowedDomains); + cssRulesHash[rule->cssSelector()] = copiedRule; + m_createdRules.append(copiedRule); + } - if (hidingRulesCount != 0) { - m_elementHidingRules = m_elementHidingRules.left(m_elementHidingRules.size() - 1); - m_elementHidingRules.append(QL1S("{display:none !important;} ")); - } + // Apparently, excessive amount of selectors for one CSS rule is not what WebKit likes. + // (In my testings, 4931 is the number that makes it crash). + // So let's split it by 1000 selectors. + int hidingRulesCount = 0; + QHashIterator it(cssRulesHash); + + while (it.hasNext()) { + it.next(); + const AdBlockRule* rule = it.value(); + + if (rule->isDomainRestricted()) { + m_domainRestrictedCssRules.append(rule); + } + + else if (Q_UNLIKELY(hidingRulesCount == 1000)) { + m_elementHidingRules.append(rule->cssSelector()); + m_elementHidingRules.append(QL1S("{display:none !important;} ")); + hidingRulesCount = 0; + } + + else { + m_elementHidingRules.append(rule->cssSelector() + QLatin1Char(',')); + hidingRulesCount++; + } + } + + if (hidingRulesCount != 0) { + m_elementHidingRules = m_elementHidingRules.left(m_elementHidingRules.size() - 1); + m_elementHidingRules.append(QL1S("{display:none !important;} ")); + } } void AdBlockMatcher::clear() { - m_networkExceptionTree.clear(); - m_networkExceptionRules.clear(); - m_networkBlockTree.clear(); - m_networkBlockRules.clear(); - m_domainRestrictedCssRules.clear(); - m_elementHidingRules.clear(); - m_documentRules.clear(); - m_elemhideRules.clear(); - qDeleteAll(m_createdRules); - m_createdRules.clear(); + m_networkExceptionTree.clear(); + m_networkExceptionRules.clear(); + m_networkBlockTree.clear(); + m_networkBlockRules.clear(); + m_domainRestrictedCssRules.clear(); + m_elementHidingRules.clear(); + m_documentRules.clear(); + m_elemhideRules.clear(); + qDeleteAll(m_createdRules); + m_createdRules.clear(); } diff --git a/src/network-web/adblock/adblockmatcher.h b/src/network-web/adblock/adblockmatcher.h index 3945edf54..7f22a0a1f 100755 --- a/src/network-web/adblock/adblockmatcher.h +++ b/src/network-web/adblock/adblockmatcher.h @@ -31,37 +31,37 @@ class QWebEngineUrlRequestInfo; class AdBlockManager; class AdBlockMatcher : public QObject { - Q_OBJECT + Q_OBJECT - public: - explicit AdBlockMatcher(AdBlockManager *manager); - virtual ~AdBlockMatcher(); + public: + explicit AdBlockMatcher(AdBlockManager* manager); + virtual ~AdBlockMatcher(); - const AdBlockRule* match(const QWebEngineUrlRequestInfo &request, const QString &urlDomain, const QString &urlString) const; + const AdBlockRule* match(const QWebEngineUrlRequestInfo& request, const QString& urlDomain, const QString& urlString) const; - bool adBlockDisabledForUrl(const QUrl &url) const; - bool elemHideDisabledForUrl(const QUrl &url) const; + bool adBlockDisabledForUrl(const QUrl& url) const; + bool elemHideDisabledForUrl(const QUrl& url) const; - QString elementHidingRules() const; - QString elementHidingRulesForDomain(const QString &domain) const; + QString elementHidingRules() const; + QString elementHidingRulesForDomain(const QString& domain) const; - public slots: - void update(); - void clear(); + public slots: + void update(); + void clear(); - private: - AdBlockManager *m_manager; + private: + AdBlockManager* m_manager; - QVector m_createdRules; - QVector m_networkExceptionRules; - QVector m_networkBlockRules; - QVector m_domainRestrictedCssRules; - QVector m_documentRules; - QVector m_elemhideRules; + QVector m_createdRules; + QVector m_networkExceptionRules; + QVector m_networkBlockRules; + QVector m_domainRestrictedCssRules; + QVector m_documentRules; + QVector m_elemhideRules; - QString m_elementHidingRules; - AdBlockSearchTree m_networkBlockTree; - AdBlockSearchTree m_networkExceptionTree; + QString m_elementHidingRules; + AdBlockSearchTree m_networkBlockTree; + AdBlockSearchTree m_networkExceptionTree; }; #endif // ADBLOCKMATCHER_H diff --git a/src/network-web/adblock/adblockrule.cpp b/src/network-web/adblock/adblockrule.cpp index 3b2f0e4f5..da68b7da7 100755 --- a/src/network-web/adblock/adblockrule.cpp +++ b/src/network-web/adblock/adblockrule.cpp @@ -58,683 +58,693 @@ #include -static QString toSecondLevelDomain(const QUrl &url) { - const QString topLevelDomain = url.topLevelDomain(); - const QString urlHost = url.host(); +static QString toSecondLevelDomain(const QUrl& url) { + const QString topLevelDomain = url.topLevelDomain(); + const QString urlHost = url.host(); - if (topLevelDomain.isEmpty() || urlHost.isEmpty()) { - return QString(); - } + if (topLevelDomain.isEmpty() || urlHost.isEmpty()) { + return QString(); + } - QString domain = urlHost.left(urlHost.size() - topLevelDomain.size()); + QString domain = urlHost.left(urlHost.size() - topLevelDomain.size()); - if (domain.count(QL1C('.')) == 0) { - return urlHost; - } + if (domain.count(QL1C('.')) == 0) { + return urlHost; + } - while (domain.count(QL1C('.')) != 0) { - domain = domain.mid(domain.indexOf(QL1C('.')) + 1); - } + while (domain.count(QL1C('.')) != 0) { + domain = domain.mid(domain.indexOf(QL1C('.')) + 1); + } - return domain + topLevelDomain; + return domain + topLevelDomain; } -AdBlockRule::AdBlockRule(const QString &filter, AdBlockSubscription* subscription) - : m_subscription(subscription), m_type(StringContainsMatchRule), m_caseSensitivity(Qt::CaseInsensitive), - m_isEnabled(true), m_isException(false), m_isInternalDisabled(false), m_regExp(0) { - setFilter(filter); +AdBlockRule::AdBlockRule(const QString& filter, AdBlockSubscription* subscription) + : m_subscription(subscription), m_type(StringContainsMatchRule), m_caseSensitivity(Qt::CaseInsensitive), + m_isEnabled(true), m_isException(false), m_isInternalDisabled(false), m_regExp(0) { + setFilter(filter); } AdBlockRule::~AdBlockRule() { - delete m_regExp; + delete m_regExp; } -AdBlockRule *AdBlockRule::copy() const { - AdBlockRule *rule = new AdBlockRule(); +AdBlockRule* AdBlockRule::copy() const { + AdBlockRule* rule = new AdBlockRule(); + rule->m_subscription = m_subscription; + rule->m_type = m_type; + rule->m_options = m_options; + rule->m_exceptions = m_exceptions; + rule->m_filter = m_filter; + rule->m_matchString = m_matchString; + rule->m_caseSensitivity = m_caseSensitivity; + rule->m_isEnabled = m_isEnabled; + rule->m_isException = m_isException; + rule->m_isInternalDisabled = m_isInternalDisabled; + rule->m_allowedDomains = m_allowedDomains; + rule->m_blockedDomains = m_blockedDomains; - rule->m_subscription = m_subscription; - rule->m_type = m_type; - rule->m_options = m_options; - rule->m_exceptions = m_exceptions; - rule->m_filter = m_filter; - rule->m_matchString = m_matchString; - rule->m_caseSensitivity = m_caseSensitivity; - rule->m_isEnabled = m_isEnabled; - rule->m_isException = m_isException; - rule->m_isInternalDisabled = m_isInternalDisabled; - rule->m_allowedDomains = m_allowedDomains; - rule->m_blockedDomains = m_blockedDomains; + if (m_regExp) { + rule->m_regExp = new RegExp; + rule->m_regExp->regExp = m_regExp->regExp; + rule->m_regExp->matchers = m_regExp->matchers; + } - if (m_regExp) { - rule->m_regExp = new RegExp; - rule->m_regExp->regExp = m_regExp->regExp; - rule->m_regExp->matchers = m_regExp->matchers; - } - - return rule; + return rule; } -AdBlockSubscription *AdBlockRule::subscription() const { - return m_subscription; +AdBlockSubscription* AdBlockRule::subscription() const { + return m_subscription; } -void AdBlockRule::setSubscription(AdBlockSubscription *subscription) { - m_subscription = subscription; +void AdBlockRule::setSubscription(AdBlockSubscription* subscription) { + m_subscription = subscription; } QString AdBlockRule::filter() const { - return m_filter; + return m_filter; } -void AdBlockRule::setFilter(const QString &filter) { - m_filter = filter; - parseFilter(); +void AdBlockRule::setFilter(const QString& filter) { + m_filter = filter; + parseFilter(); } bool AdBlockRule::isCssRule() const { - return m_type == CssRule; + return m_type == CssRule; } QString AdBlockRule::cssSelector() const { - return m_matchString; + return m_matchString; } bool AdBlockRule::isDocument() const { - return hasOption(DocumentOption); + return hasOption(DocumentOption); } bool AdBlockRule::isElemhide() const { - return hasOption(ElementHideOption); + return hasOption(ElementHideOption); } bool AdBlockRule::isDomainRestricted() const { - return hasOption(DomainRestrictedOption); + return hasOption(DomainRestrictedOption); } bool AdBlockRule::isException() const { - return m_isException; + return m_isException; } bool AdBlockRule::isComment() const { - return m_filter.startsWith(QL1C('!')); + return m_filter.startsWith(QL1C('!')); } bool AdBlockRule::isEnabled() const { - return m_isEnabled; + return m_isEnabled; } void AdBlockRule::setEnabled(bool enabled) { - m_isEnabled = enabled; + m_isEnabled = enabled; } bool AdBlockRule::isSlow() const { - return m_regExp != 0; + return m_regExp != 0; } bool AdBlockRule::isInternalDisabled() const { - return m_isInternalDisabled; + return m_isInternalDisabled; } -bool AdBlockRule::urlMatch(const QUrl &url) const { - if (!hasOption(DocumentOption) && !hasOption(ElementHideOption)) { - return false; - } - else { - const QString encodedUrl = url.toEncoded(); - const QString domain = url.host(); +bool AdBlockRule::urlMatch(const QUrl& url) const { + if (!hasOption(DocumentOption) && !hasOption(ElementHideOption)) { + return false; + } - return stringMatch(domain, encodedUrl); - } + else { + const QString encodedUrl = url.toEncoded(); + const QString domain = url.host(); + return stringMatch(domain, encodedUrl); + } } -bool AdBlockRule::networkMatch(const QWebEngineUrlRequestInfo &request, const QString &domain, const QString &encodedUrl) const { - if (m_type == CssRule || !m_isEnabled || m_isInternalDisabled) { - return false; - } +bool AdBlockRule::networkMatch(const QWebEngineUrlRequestInfo& request, const QString& domain, const QString& encodedUrl) const { + if (m_type == CssRule || !m_isEnabled || m_isInternalDisabled) { + return false; + } - bool matched = stringMatch(domain, encodedUrl); + bool matched = stringMatch(domain, encodedUrl); - if (matched) { - // Check domain restrictions. - if (hasOption(DomainRestrictedOption) && !matchDomain(request.firstPartyUrl().host())) { - return false; - } + if (matched) { + // Check domain restrictions. + if (hasOption(DomainRestrictedOption) && !matchDomain(request.firstPartyUrl().host())) { + return false; + } - // Check third-party restriction. - if (hasOption(ThirdPartyOption) && !matchThirdParty(request)) { - return false; - } + // Check third-party restriction. + if (hasOption(ThirdPartyOption) && !matchThirdParty(request)) { + return false; + } - // Check object restrictions. - if (hasOption(ObjectOption) && !matchObject(request)) { - return false; - } + // Check object restrictions. + if (hasOption(ObjectOption) && !matchObject(request)) { + return false; + } - // Check subdocument restriction. - if (hasOption(SubdocumentOption) && !matchSubdocument(request)) { - return false; - } + // Check subdocument restriction. + if (hasOption(SubdocumentOption) && !matchSubdocument(request)) { + return false; + } - // Check xmlhttprequest restriction. - if (hasOption(XMLHttpRequestOption) && !matchXmlHttpRequest(request)) { - return false; - } + // Check xmlhttprequest restriction. + if (hasOption(XMLHttpRequestOption) && !matchXmlHttpRequest(request)) { + return false; + } - // Check image restriction. - if (hasOption(ImageOption) && !matchImage(request)) { - return false; - } + // Check image restriction. + if (hasOption(ImageOption) && !matchImage(request)) { + return false; + } - // Check script restriction. - if (hasOption(ScriptOption) && !matchScript(request)) { - return false; - } + // Check script restriction. + if (hasOption(ScriptOption) && !matchScript(request)) { + return false; + } - // Check stylesheet restriction. - if (hasOption(StyleSheetOption) && !matchStyleSheet(request)) { - return false; - } + // Check stylesheet restriction. + if (hasOption(StyleSheetOption) && !matchStyleSheet(request)) { + return false; + } - // Check object-subrequest restriction. - if (hasOption(ObjectSubrequestOption) && !matchObjectSubrequest(request)) { - return false; - } - } + // Check object-subrequest restriction. + if (hasOption(ObjectSubrequestOption) && !matchObjectSubrequest(request)) { + return false; + } + } - return matched; + return matched; } -bool AdBlockRule::matchDomain(const QString &domain) const { - if (!m_isEnabled) { - return false; - } +bool AdBlockRule::matchDomain(const QString& domain) const { + if (!m_isEnabled) { + return false; + } - if (!hasOption(DomainRestrictedOption)) { - return true; - } + if (!hasOption(DomainRestrictedOption)) { + return true; + } - if (m_blockedDomains.isEmpty()) { - foreach (const QString &d, m_allowedDomains) { - if (isMatchingDomain(domain, d)) { - return true; - } - } - } - else if (m_allowedDomains.isEmpty()) { - foreach (const QString &d, m_blockedDomains) { - if (isMatchingDomain(domain, d)) { - return false; - } - } + if (m_blockedDomains.isEmpty()) { + foreach (const QString& d, m_allowedDomains) { + if (isMatchingDomain(domain, d)) { + return true; + } + } + } - return true; - } - else { - foreach (const QString &d, m_blockedDomains) { - if (isMatchingDomain(domain, d)) { - return false; - } - } + else if (m_allowedDomains.isEmpty()) { + foreach (const QString& d, m_blockedDomains) { + if (isMatchingDomain(domain, d)) { + return false; + } + } - foreach (const QString &d, m_allowedDomains) { - if (isMatchingDomain(domain, d)) { - return true; - } - } - } + return true; + } - return false; + else { + foreach (const QString& d, m_blockedDomains) { + if (isMatchingDomain(domain, d)) { + return false; + } + } + + foreach (const QString& d, m_allowedDomains) { + if (isMatchingDomain(domain, d)) { + return true; + } + } + } + + return false; } -bool AdBlockRule::matchThirdParty(const QWebEngineUrlRequestInfo &request) const { - // Third-party matching should be performed on second-level domains. - const QString firstPartyHost = toSecondLevelDomain(request.firstPartyUrl()); - const QString host = toSecondLevelDomain(request.requestUrl()); - - bool match = firstPartyHost != host; - - return hasException(ThirdPartyOption) ? !match : match; +bool AdBlockRule::matchThirdParty(const QWebEngineUrlRequestInfo& request) const { + // Third-party matching should be performed on second-level domains. + const QString firstPartyHost = toSecondLevelDomain(request.firstPartyUrl()); + const QString host = toSecondLevelDomain(request.requestUrl()); + bool match = firstPartyHost != host; + return hasException(ThirdPartyOption) ? !match : match; } -bool AdBlockRule::matchObject(const QWebEngineUrlRequestInfo &request) const { - bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeObject; - - return hasException(ObjectOption) ? !match : match; +bool AdBlockRule::matchObject(const QWebEngineUrlRequestInfo& request) const { + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeObject; + return hasException(ObjectOption) ? !match : match; } -bool AdBlockRule::matchSubdocument(const QWebEngineUrlRequestInfo &request) const { - bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeSubFrame; - - return hasException(SubdocumentOption) ? !match : match; +bool AdBlockRule::matchSubdocument(const QWebEngineUrlRequestInfo& request) const { + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeSubFrame; + return hasException(SubdocumentOption) ? !match : match; } -bool AdBlockRule::matchXmlHttpRequest(const QWebEngineUrlRequestInfo &request) const { - bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeXhr; - - return hasException(XMLHttpRequestOption) ? !match : match; +bool AdBlockRule::matchXmlHttpRequest(const QWebEngineUrlRequestInfo& request) const { + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeXhr; + return hasException(XMLHttpRequestOption) ? !match : match; } -bool AdBlockRule::matchImage(const QWebEngineUrlRequestInfo &request) const { - bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeImage; - - return hasException(ImageOption) ? !match : match; +bool AdBlockRule::matchImage(const QWebEngineUrlRequestInfo& request) const { + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeImage; + return hasException(ImageOption) ? !match : match; } -bool AdBlockRule::matchScript(const QWebEngineUrlRequestInfo &request) const { - bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeScript; - - return hasException(ScriptOption) ? !match : match; +bool AdBlockRule::matchScript(const QWebEngineUrlRequestInfo& request) const { + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeScript; + return hasException(ScriptOption) ? !match : match; } -bool AdBlockRule::matchStyleSheet(const QWebEngineUrlRequestInfo &request) const { - bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeStylesheet; - - return hasException(StyleSheetOption) ? !match : match; +bool AdBlockRule::matchStyleSheet(const QWebEngineUrlRequestInfo& request) const { + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeStylesheet; + return hasException(StyleSheetOption) ? !match : match; } -bool AdBlockRule::matchObjectSubrequest(const QWebEngineUrlRequestInfo &request) const { - bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeSubResource; - - return hasException(ObjectSubrequestOption) ? !match : match; +bool AdBlockRule::matchObjectSubrequest(const QWebEngineUrlRequestInfo& request) const { + bool match = request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeSubResource; + return hasException(ObjectSubrequestOption) ? !match : match; } void AdBlockRule::parseFilter() { - QString parsedLine = m_filter; + QString parsedLine = m_filter; - // Empty rule or just comment. - if (m_filter.trimmed().isEmpty() || m_filter.startsWith(QL1C('!'))) { - // We want to differentiate rule disabled by user and rule disabled in subscription file - // m_isInternalDisabled is also used when rule is disabled due to all options not being supported. - m_isEnabled = false; - m_isInternalDisabled = true; - m_type = Invalid; - return; - } + // Empty rule or just comment. + if (m_filter.trimmed().isEmpty() || m_filter.startsWith(QL1C('!'))) { + // We want to differentiate rule disabled by user and rule disabled in subscription file + // m_isInternalDisabled is also used when rule is disabled due to all options not being supported. + m_isEnabled = false; + m_isInternalDisabled = true; + m_type = Invalid; + return; + } - // CSS Element hiding rule. - if (parsedLine.contains(QL1S("##")) || parsedLine.contains(QL1S("#@#"))) { - m_type = CssRule; - int pos = parsedLine.indexOf(QL1C('#')); + // CSS Element hiding rule. + if (parsedLine.contains(QL1S("##")) || parsedLine.contains(QL1S("#@#"))) { + m_type = CssRule; + int pos = parsedLine.indexOf(QL1C('#')); - // Domain restricted rule. - if (!parsedLine.startsWith(QL1S("##"))) { - QString domains = parsedLine.left(pos); - parseDomains(domains, QL1C(',')); - } + // Domain restricted rule. + if (!parsedLine.startsWith(QL1S("##"))) { + QString domains = parsedLine.left(pos); + parseDomains(domains, QL1C(',')); + } - m_isException = parsedLine.at(pos + 1) == QL1C('@'); - m_matchString = parsedLine.mid(m_isException ? pos + 3 : pos + 2); + m_isException = parsedLine.at(pos + 1) == QL1C('@'); + m_matchString = parsedLine.mid(m_isException ? pos + 3 : pos + 2); + // CSS rule cannot have more options -> stop parsing. + return; + } - // CSS rule cannot have more options -> stop parsing. - return; - } + // Exception always starts with @@. + if (parsedLine.startsWith(QL1S("@@"))) { + m_isException = true; + parsedLine = parsedLine.mid(2); + } - // Exception always starts with @@. - if (parsedLine.startsWith(QL1S("@@"))) { - m_isException = true; - parsedLine = parsedLine.mid(2); - } + // Parse all options following $ char + int optionsIndex = parsedLine.indexOf(QL1C('$')); - // Parse all options following $ char - int optionsIndex = parsedLine.indexOf(QL1C('$')); + if (optionsIndex >= 0) { + const QStringList options = parsedLine.mid(optionsIndex + 1).split(QL1C(','), QString::SkipEmptyParts); + int handledOptions = 0; - if (optionsIndex >= 0) { - const QStringList options = parsedLine.mid(optionsIndex + 1).split(QL1C(','), QString::SkipEmptyParts); - int handledOptions = 0; + foreach (const QString& option, options) { + if (option.startsWith(QL1S("domain="))) { + parseDomains(option.mid(7), QL1C('|')); + ++handledOptions; + } - foreach (const QString &option, options) { - if (option.startsWith(QL1S("domain="))) { - parseDomains(option.mid(7), QL1C('|')); - ++handledOptions; - } - else if (option == QL1S("match-case")) { - m_caseSensitivity = Qt::CaseSensitive; - ++handledOptions; - } - else if (option.endsWith(QL1S("third-party"))) { - setOption(ThirdPartyOption); - setException(ThirdPartyOption, option.startsWith(QL1C('~'))); - ++handledOptions; - } - else if (option.endsWith(QL1S("object"))) { - setOption(ObjectOption); - setException(ObjectOption, option.startsWith(QL1C('~'))); - ++handledOptions; - } - else if (option.endsWith(QL1S("subdocument"))) { - setOption(SubdocumentOption); - setException(SubdocumentOption, option.startsWith(QL1C('~'))); - ++handledOptions; - } - else if (option.endsWith(QL1S("xmlhttprequest"))) { - setOption(XMLHttpRequestOption); - setException(XMLHttpRequestOption, option.startsWith(QL1C('~'))); - ++handledOptions; - } - else if (option.endsWith(QL1S("image"))) { - setOption(ImageOption); - setException(ImageOption, option.startsWith(QL1C('~'))); - ++handledOptions; - } - else if (option.endsWith(QL1S("script"))) { - setOption(ScriptOption); - setException(ScriptOption, option.startsWith(QL1C('~'))); - ++handledOptions; - } - else if (option.endsWith(QL1S("stylesheet"))) { - setOption(StyleSheetOption); - setException(StyleSheetOption, option.startsWith(QL1C('~'))); - ++handledOptions; - } - else if (option.endsWith(QL1S("object-subrequest"))) { - setOption(ObjectSubrequestOption); - setException(ObjectSubrequestOption, option.startsWith(QL1C('~'))); - ++handledOptions; - } - else if (option == QL1S("document") && m_isException) { - setOption(DocumentOption); - ++handledOptions; - } - else if (option == QL1S("elemhide") && m_isException) { - setOption(ElementHideOption); - ++handledOptions; - } - else if (option == QL1S("collapse")) { - // Hiding placeholders of blocked elements is enabled by default. - ++handledOptions; - } - } + else if (option == QL1S("match-case")) { + m_caseSensitivity = Qt::CaseSensitive; + ++handledOptions; + } - // If we don't handle all options, it's safer to just disable this rule. - if (handledOptions != options.count()) { - m_isInternalDisabled = true; - m_type = Invalid; - return; - } + else if (option.endsWith(QL1S("third-party"))) { + setOption(ThirdPartyOption); + setException(ThirdPartyOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } - parsedLine = parsedLine.left(optionsIndex); - } + else if (option.endsWith(QL1S("object"))) { + setOption(ObjectOption); + setException(ObjectOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } - // Rule is classic regexp. - if (parsedLine.startsWith(QL1C('/')) && parsedLine.endsWith(QL1C('/'))) { - parsedLine = parsedLine.mid(1); - parsedLine = parsedLine.left(parsedLine.size() - 1); + else if (option.endsWith(QL1S("subdocument"))) { + setOption(SubdocumentOption); + setException(SubdocumentOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } - m_type = RegExpMatchRule; - m_regExp = new RegExp; - m_regExp->regExp = SimpleRegExp(parsedLine, m_caseSensitivity); - m_regExp->matchers = createStringMatchers(parseRegExpFilter(parsedLine)); - return; - } + else if (option.endsWith(QL1S("xmlhttprequest"))) { + setOption(XMLHttpRequestOption); + setException(XMLHttpRequestOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } - // Remove starting and ending wildcards (*). - if (parsedLine.startsWith(QL1C('*'))) { - parsedLine = parsedLine.mid(1); - } + else if (option.endsWith(QL1S("image"))) { + setOption(ImageOption); + setException(ImageOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } - if (parsedLine.endsWith(QL1C('*'))) { - parsedLine = parsedLine.left(parsedLine.size() - 1); - } + else if (option.endsWith(QL1S("script"))) { + setOption(ScriptOption); + setException(ScriptOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } - // We can use fast string matching for domain here. - if (filterIsOnlyDomain(parsedLine)) { - parsedLine = parsedLine.mid(2); - parsedLine = parsedLine.left(parsedLine.size() - 1); + else if (option.endsWith(QL1S("stylesheet"))) { + setOption(StyleSheetOption); + setException(StyleSheetOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } - m_type = DomainMatchRule; - m_matchString = parsedLine; - return; - } + else if (option.endsWith(QL1S("object-subrequest"))) { + setOption(ObjectSubrequestOption); + setException(ObjectSubrequestOption, option.startsWith(QL1C('~'))); + ++handledOptions; + } - // If rule contains only | at end, we can also use string matching. - if (filterIsOnlyEndsMatch(parsedLine)) { - parsedLine = parsedLine.left(parsedLine.size() - 1); + else if (option == QL1S("document") && m_isException) { + setOption(DocumentOption); + ++handledOptions; + } - m_type = StringEndsMatchRule; - m_matchString = parsedLine; - return; - } + else if (option == QL1S("elemhide") && m_isException) { + setOption(ElementHideOption); + ++handledOptions; + } - // If we still find a wildcard (*) or separator (^) or (|) - // we must modify parsedLine to comply with SimpleRegExp. - if (parsedLine.contains(QL1C('*')) || parsedLine.contains(QL1C('^')) ||parsedLine.contains(QL1C('|'))) { - m_type = RegExpMatchRule; - m_regExp = new RegExp; - m_regExp->regExp = SimpleRegExp(createRegExpFromFilter(parsedLine), m_caseSensitivity); - m_regExp->matchers = createStringMatchers(parseRegExpFilter(parsedLine)); - return; - } + else if (option == QL1S("collapse")) { + // Hiding placeholders of blocked elements is enabled by default. + ++handledOptions; + } + } - // We haven't found anything that needs use of regexp, yay! - m_type = StringContainsMatchRule; - m_matchString = parsedLine; + // If we don't handle all options, it's safer to just disable this rule. + if (handledOptions != options.count()) { + m_isInternalDisabled = true; + m_type = Invalid; + return; + } + + parsedLine = parsedLine.left(optionsIndex); + } + + // Rule is classic regexp. + if (parsedLine.startsWith(QL1C('/')) && parsedLine.endsWith(QL1C('/'))) { + parsedLine = parsedLine.mid(1); + parsedLine = parsedLine.left(parsedLine.size() - 1); + m_type = RegExpMatchRule; + m_regExp = new RegExp; + m_regExp->regExp = SimpleRegExp(parsedLine, m_caseSensitivity); + m_regExp->matchers = createStringMatchers(parseRegExpFilter(parsedLine)); + return; + } + + // Remove starting and ending wildcards (*). + if (parsedLine.startsWith(QL1C('*'))) { + parsedLine = parsedLine.mid(1); + } + + if (parsedLine.endsWith(QL1C('*'))) { + parsedLine = parsedLine.left(parsedLine.size() - 1); + } + + // We can use fast string matching for domain here. + if (filterIsOnlyDomain(parsedLine)) { + parsedLine = parsedLine.mid(2); + parsedLine = parsedLine.left(parsedLine.size() - 1); + m_type = DomainMatchRule; + m_matchString = parsedLine; + return; + } + + // If rule contains only | at end, we can also use string matching. + if (filterIsOnlyEndsMatch(parsedLine)) { + parsedLine = parsedLine.left(parsedLine.size() - 1); + m_type = StringEndsMatchRule; + m_matchString = parsedLine; + return; + } + + // If we still find a wildcard (*) or separator (^) or (|) + // we must modify parsedLine to comply with SimpleRegExp. + if (parsedLine.contains(QL1C('*')) || parsedLine.contains(QL1C('^')) || parsedLine.contains(QL1C('|'))) { + m_type = RegExpMatchRule; + m_regExp = new RegExp; + m_regExp->regExp = SimpleRegExp(createRegExpFromFilter(parsedLine), m_caseSensitivity); + m_regExp->matchers = createStringMatchers(parseRegExpFilter(parsedLine)); + return; + } + + // We haven't found anything that needs use of regexp, yay! + m_type = StringContainsMatchRule; + m_matchString = parsedLine; } -void AdBlockRule::parseDomains(const QString &domains, const QChar &separator) { - QStringList domainsList = domains.split(separator, QString::SkipEmptyParts); +void AdBlockRule::parseDomains(const QString& domains, const QChar& separator) { + QStringList domainsList = domains.split(separator, QString::SkipEmptyParts); - foreach (const QString domain, domainsList) { - if (domain.isEmpty()) { - continue; - } - if (domain.startsWith(QL1C('~'))) { - m_blockedDomains.append(domain.mid(1)); - } - else { - m_allowedDomains.append(domain); - } - } + foreach (const QString domain, domainsList) { + if (domain.isEmpty()) { + continue; + } - if (!m_blockedDomains.isEmpty() || !m_allowedDomains.isEmpty()) { - setOption(DomainRestrictedOption); - } + if (domain.startsWith(QL1C('~'))) { + m_blockedDomains.append(domain.mid(1)); + } + + else { + m_allowedDomains.append(domain); + } + } + + if (!m_blockedDomains.isEmpty() || !m_allowedDomains.isEmpty()) { + setOption(DomainRestrictedOption); + } } -bool AdBlockRule::filterIsOnlyDomain(const QString &filter) const { - if (!filter.endsWith(QL1C('^')) || !filter.startsWith(QL1S("||"))) { - return false; - } +bool AdBlockRule::filterIsOnlyDomain(const QString& filter) const { + if (!filter.endsWith(QL1C('^')) || !filter.startsWith(QL1S("||"))) { + return false; + } - for (int i = 0; i < filter.size(); ++i) { - switch (filter.at(i).toLatin1()) { - case '/': - case ':': - case '?': - case '=': - case '&': - case '*': - return false; + for (int i = 0; i < filter.size(); ++i) { + switch (filter.at(i).toLatin1()) { + case '/': + case ':': + case '?': + case '=': + case '&': + case '*': + return false; - default: - break; - } - } + default: + break; + } + } - return true; + return true; } -bool AdBlockRule::filterIsOnlyEndsMatch(const QString &filter) const { - for (int i = 0; i < filter.size(); ++i) { - switch (filter.at(i).toLatin1()) { - case '^': - case '*': - return false; +bool AdBlockRule::filterIsOnlyEndsMatch(const QString& filter) const { + for (int i = 0; i < filter.size(); ++i) { + switch (filter.at(i).toLatin1()) { + case '^': + case '*': + return false; - case '|': - return i == filter.size() - 1; + case '|': + return i == filter.size() - 1; - default: - break; - } - } + default: + break; + } + } - return false; + return false; } -static bool wordCharacter(const QChar &c) { - return c.isLetterOrNumber() || c.isMark() || c == QL1C('_'); +static bool wordCharacter(const QChar& c) { + return c.isLetterOrNumber() || c.isMark() || c == QL1C('_'); } -QString AdBlockRule::createRegExpFromFilter(const QString &filter) const { - QString parsed; - parsed.reserve(filter.size()); +QString AdBlockRule::createRegExpFromFilter(const QString& filter) const { + QString parsed; + parsed.reserve(filter.size()); + bool hadWildcard = false; // Filter multiple wildcards. - bool hadWildcard = false; // Filter multiple wildcards. + for (int i = 0; i < filter.size(); ++i) { + const QChar c = filter.at(i); - for (int i = 0; i < filter.size(); ++i) { - const QChar c = filter.at(i); - switch (c.toLatin1()) { - case '^': - parsed.append(QL1S("(?:[^\\w\\d\\-.%]|$)")); - break; + switch (c.toLatin1()) { + case '^': + parsed.append(QL1S("(?:[^\\w\\d\\-.%]|$)")); + break; - case '*': - if (!hadWildcard) { - parsed.append(QL1S(".*")); - } - break; + case '*': + if (!hadWildcard) { + parsed.append(QL1S(".*")); + } - case '|': - if (i == 0) { - if (filter.size() > 1 && filter.at(1) == QL1C('|')) { - parsed.append(QL1S("^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?")); - i++; - } - else { - parsed.append('^'); - } - break; - } - else if (i == filter.size() - 1) { - parsed.append(QL1C('$')); - break; - } - // fallthrough + break; - default: - if (!wordCharacter(c)) { - parsed.append(QL1C('\\') + c); - } - else { - parsed.append(c); - } - } + case '|': + if (i == 0) { + if (filter.size() > 1 && filter.at(1) == QL1C('|')) { + parsed.append(QL1S("^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?")); + i++; + } - hadWildcard = c == QL1C('*'); - } + else { + parsed.append('^'); + } - return parsed; + break; + } + + else if (i == filter.size() - 1) { + parsed.append(QL1C('$')); + break; + } + + // fallthrough + + default: + if (!wordCharacter(c)) { + parsed.append(QL1C('\\') + c); + } + + else { + parsed.append(c); + } + } + + hadWildcard = c == QL1C('*'); + } + + return parsed; } -QList AdBlockRule::createStringMatchers(const QStringList &filters) const { - QList matchers; - matchers.reserve(filters.size()); +QList AdBlockRule::createStringMatchers(const QStringList& filters) const { + QList matchers; + matchers.reserve(filters.size()); - foreach (const QString &filter, filters) { - matchers.append(QStringMatcher(filter, m_caseSensitivity)); - } + foreach (const QString& filter, filters) { + matchers.append(QStringMatcher(filter, m_caseSensitivity)); + } - return matchers; + return matchers; } -bool AdBlockRule::stringMatch(const QString &domain, const QString &encodedUrl) const { - if (m_type == StringContainsMatchRule) { - return encodedUrl.contains(m_matchString, m_caseSensitivity); - } - else if (m_type == DomainMatchRule) { - return isMatchingDomain(domain, m_matchString); - } - else if (m_type == StringEndsMatchRule) { - return encodedUrl.endsWith(m_matchString, m_caseSensitivity); - } - else if (m_type == RegExpMatchRule) { - if (!isMatchingRegExpStrings(encodedUrl)) { - return false; - } - else { - return (m_regExp->regExp.indexIn(encodedUrl) != -1); - } - } +bool AdBlockRule::stringMatch(const QString& domain, const QString& encodedUrl) const { + if (m_type == StringContainsMatchRule) { + return encodedUrl.contains(m_matchString, m_caseSensitivity); + } - return false; + else if (m_type == DomainMatchRule) { + return isMatchingDomain(domain, m_matchString); + } + + else if (m_type == StringEndsMatchRule) { + return encodedUrl.endsWith(m_matchString, m_caseSensitivity); + } + + else if (m_type == RegExpMatchRule) { + if (!isMatchingRegExpStrings(encodedUrl)) { + return false; + } + + else { + return (m_regExp->regExp.indexIn(encodedUrl) != -1); + } + } + + return false; } -bool AdBlockRule::matchDomain(const QString &pattern, const QString &domain) const { - if (pattern == domain) { - return true; - } +bool AdBlockRule::matchDomain(const QString& pattern, const QString& domain) const { + if (pattern == domain) { + return true; + } - if (!domain.endsWith(pattern)) { - return false; - } + if (!domain.endsWith(pattern)) { + return false; + } - int index = domain.indexOf(pattern); - - return index > 0 && domain[index - 1] == QLatin1Char('.'); + int index = domain.indexOf(pattern); + return index > 0 && domain[index - 1] == QLatin1Char('.'); } -bool AdBlockRule::isMatchingDomain(const QString &domain, const QString &filter) const { - return matchDomain(filter, domain); +bool AdBlockRule::isMatchingDomain(const QString& domain, const QString& filter) const { + return matchDomain(filter, domain); } -bool AdBlockRule::isMatchingRegExpStrings(const QString &url) const { - Q_ASSERT(m_regExp); +bool AdBlockRule::isMatchingRegExpStrings(const QString& url) const { + Q_ASSERT(m_regExp); - foreach (const QStringMatcher &matcher, m_regExp->matchers) { - if (matcher.indexIn(url) == -1) { - return false; - } - } + foreach (const QStringMatcher& matcher, m_regExp->matchers) { + if (matcher.indexIn(url) == -1) { + return false; + } + } - return true; + return true; } // Split regexp filter into strings that can be used with QString::contains // Don't use parts that contains only 1 char and duplicated parts. -QStringList AdBlockRule::parseRegExpFilter(const QString &filter) const { - QStringList list; - int startPos = -1; +QStringList AdBlockRule::parseRegExpFilter(const QString& filter) const { + QStringList list; + int startPos = -1; - for (int i = 0; i < filter.size(); ++i) { - const QChar c = filter.at(i); + for (int i = 0; i < filter.size(); ++i) { + const QChar c = filter.at(i); - // Meta characters in AdBlock rules are | * ^ - if (c == QL1C('|') || c == QL1C('*') || c == QL1C('^')) { - const QString sub = filter.mid(startPos, i - startPos); + // Meta characters in AdBlock rules are | * ^ + if (c == QL1C('|') || c == QL1C('*') || c == QL1C('^')) { + const QString sub = filter.mid(startPos, i - startPos); - if (sub.size() > 1) { - list.append(sub); - } + if (sub.size() > 1) { + list.append(sub); + } - startPos = i + 1; - } - } + startPos = i + 1; + } + } - const QString sub = filter.mid(startPos); + const QString sub = filter.mid(startPos); - if (sub.size() > 1) { - list.append(sub); - } + if (sub.size() > 1) { + list.append(sub); + } - list.removeDuplicates(); - - return list; + list.removeDuplicates(); + return list; } -bool AdBlockRule::hasOption(const AdBlockRule::RuleOption &opt) const { - return (m_options & opt); +bool AdBlockRule::hasOption(const AdBlockRule::RuleOption& opt) const { + return (m_options & opt); } -bool AdBlockRule::hasException(const AdBlockRule::RuleOption &opt) const { - return (m_exceptions & opt); +bool AdBlockRule::hasException(const AdBlockRule::RuleOption& opt) const { + return (m_exceptions & opt); } -void AdBlockRule::setOption(const AdBlockRule::RuleOption &opt) { - m_options |= opt; +void AdBlockRule::setOption(const AdBlockRule::RuleOption& opt) { + m_options |= opt; } -void AdBlockRule::setException(const AdBlockRule::RuleOption &opt, bool on) { - if (on) { - m_exceptions |= opt; - } +void AdBlockRule::setException(const AdBlockRule::RuleOption& opt, bool on) { + if (on) { + m_exceptions |= opt; + } } diff --git a/src/network-web/adblock/adblockrule.h b/src/network-web/adblock/adblockrule.h index d0670559e..989d0cb37 100755 --- a/src/network-web/adblock/adblockrule.h +++ b/src/network-web/adblock/adblockrule.h @@ -59,128 +59,128 @@ class QWebEngineUrlRequestInfo; class AdBlockSubscription; class AdBlockRule { - Q_DISABLE_COPY(AdBlockRule) + Q_DISABLE_COPY(AdBlockRule) - public: - explicit AdBlockRule(const QString &filter = QString(), AdBlockSubscription *subscription = 0); - virtual ~AdBlockRule(); + public: + explicit AdBlockRule(const QString& filter = QString(), AdBlockSubscription* subscription = 0); + virtual ~AdBlockRule(); - AdBlockRule *copy() const; + AdBlockRule* copy() const; - AdBlockSubscription *subscription() const; - void setSubscription(AdBlockSubscription *subscription); + AdBlockSubscription* subscription() const; + void setSubscription(AdBlockSubscription* subscription); - QString filter() const; - void setFilter(const QString &filter); + QString filter() const; + void setFilter(const QString& filter); - bool isCssRule() const; - QString cssSelector() const; + bool isCssRule() const; + QString cssSelector() const; - bool isDocument() const; - bool isElemhide() const; + bool isDocument() const; + bool isElemhide() const; - bool isDomainRestricted() const; - bool isException() const; + bool isDomainRestricted() const; + bool isException() const; - bool isComment() const; - bool isEnabled() const; - void setEnabled(bool enabled); + bool isComment() const; + bool isEnabled() const; + void setEnabled(bool enabled); - bool isSlow() const; - bool isInternalDisabled() const; + bool isSlow() const; + bool isInternalDisabled() const; - bool urlMatch(const QUrl &url) const; - bool networkMatch(const QWebEngineUrlRequestInfo &request, const QString &domain, const QString &encodedUrl) const; + bool urlMatch(const QUrl& url) const; + bool networkMatch(const QWebEngineUrlRequestInfo& request, const QString& domain, const QString& encodedUrl) const; - bool matchDomain(const QString &domain) const; - bool matchThirdParty(const QWebEngineUrlRequestInfo &request) const; - bool matchObject(const QWebEngineUrlRequestInfo &request) const; - bool matchSubdocument(const QWebEngineUrlRequestInfo &request) const; - bool matchXmlHttpRequest(const QWebEngineUrlRequestInfo &request) const; - bool matchImage(const QWebEngineUrlRequestInfo &request) const; - bool matchScript(const QWebEngineUrlRequestInfo &request) const; - bool matchStyleSheet(const QWebEngineUrlRequestInfo &request) const; - bool matchObjectSubrequest(const QWebEngineUrlRequestInfo &request) const; + bool matchDomain(const QString& domain) const; + bool matchThirdParty(const QWebEngineUrlRequestInfo& request) const; + bool matchObject(const QWebEngineUrlRequestInfo& request) const; + bool matchSubdocument(const QWebEngineUrlRequestInfo& request) const; + bool matchXmlHttpRequest(const QWebEngineUrlRequestInfo& request) const; + bool matchImage(const QWebEngineUrlRequestInfo& request) const; + bool matchScript(const QWebEngineUrlRequestInfo& request) const; + bool matchStyleSheet(const QWebEngineUrlRequestInfo& request) const; + bool matchObjectSubrequest(const QWebEngineUrlRequestInfo& request) const; - protected: - bool matchDomain(const QString &pattern, const QString &domain) const; - bool stringMatch(const QString &domain, const QString &encodedUrl) const; - bool isMatchingDomain(const QString &domain, const QString &filter) const; - bool isMatchingRegExpStrings(const QString &url) const; - QStringList parseRegExpFilter(const QString &filter) const; + protected: + bool matchDomain(const QString& pattern, const QString& domain) const; + bool stringMatch(const QString& domain, const QString& encodedUrl) const; + bool isMatchingDomain(const QString& domain, const QString& filter) const; + bool isMatchingRegExpStrings(const QString& url) const; + QStringList parseRegExpFilter(const QString& filter) const; - private: - enum RuleType { - CssRule = 0, - DomainMatchRule = 1, - RegExpMatchRule = 2, - StringEndsMatchRule = 3, - StringContainsMatchRule = 4, - Invalid = 5 - }; + private: + enum RuleType { + CssRule = 0, + DomainMatchRule = 1, + RegExpMatchRule = 2, + StringEndsMatchRule = 3, + StringContainsMatchRule = 4, + Invalid = 5 + }; - enum RuleOption { - DomainRestrictedOption = 1, - ThirdPartyOption = 2, - ObjectOption = 4, - SubdocumentOption = 8, - XMLHttpRequestOption = 16, - ImageOption = 32, - ScriptOption = 64, - StyleSheetOption = 128, - ObjectSubrequestOption = 256, + enum RuleOption { + DomainRestrictedOption = 1, + ThirdPartyOption = 2, + ObjectOption = 4, + SubdocumentOption = 8, + XMLHttpRequestOption = 16, + ImageOption = 32, + ScriptOption = 64, + StyleSheetOption = 128, + ObjectSubrequestOption = 256, - // Exception only options. - DocumentOption = 1024, - ElementHideOption = 2048 - }; + // Exception only options. + DocumentOption = 1024, + ElementHideOption = 2048 + }; - Q_DECLARE_FLAGS(RuleOptions, RuleOption) + Q_DECLARE_FLAGS(RuleOptions, RuleOption) - inline bool hasOption(const RuleOption &opt) const; - inline bool hasException(const RuleOption &opt) const; + inline bool hasOption(const RuleOption& opt) const; + inline bool hasException(const RuleOption& opt) const; - inline void setOption(const RuleOption &opt); - inline void setException(const RuleOption &opt, bool on); + inline void setOption(const RuleOption& opt); + inline void setException(const RuleOption& opt, bool on); - void parseFilter(); - void parseDomains(const QString &domains, const QChar &separator); - bool filterIsOnlyDomain(const QString &filter) const; - bool filterIsOnlyEndsMatch(const QString &filter) const; - QString createRegExpFromFilter(const QString &filter) const; - QList createStringMatchers(const QStringList &filters) const; + void parseFilter(); + void parseDomains(const QString& domains, const QChar& separator); + bool filterIsOnlyDomain(const QString& filter) const; + bool filterIsOnlyEndsMatch(const QString& filter) const; + QString createRegExpFromFilter(const QString& filter) const; + QList createStringMatchers(const QStringList& filters) const; - AdBlockSubscription *m_subscription; + AdBlockSubscription* m_subscription; - RuleType m_type; - RuleOptions m_options; - RuleOptions m_exceptions; + RuleType m_type; + RuleOptions m_options; + RuleOptions m_exceptions; - // Original rule filter - QString m_filter; - // Parsed rule for string matching (CSS Selector for CSS rules) - QString m_matchString; - // Case sensitivity for string matching - Qt::CaseSensitivity m_caseSensitivity; + // Original rule filter + QString m_filter; + // Parsed rule for string matching (CSS Selector for CSS rules) + QString m_matchString; + // Case sensitivity for string matching + Qt::CaseSensitivity m_caseSensitivity; - bool m_isEnabled; - bool m_isException; - bool m_isInternalDisabled; + bool m_isEnabled; + bool m_isException; + bool m_isInternalDisabled; - QStringList m_allowedDomains; - QStringList m_blockedDomains; + QStringList m_allowedDomains; + QStringList m_blockedDomains; - struct RegExp { - SimpleRegExp regExp; - QList matchers; - }; + struct RegExp { + SimpleRegExp regExp; + QList matchers; + }; - // Use dynamic allocation to save memory - RegExp *m_regExp; + // Use dynamic allocation to save memory + RegExp* m_regExp; - friend class AdBlockMatcher; - friend class AdBlockSearchTree; - friend class AdBlockSubscription; + friend class AdBlockMatcher; + friend class AdBlockSearchTree; + friend class AdBlockSubscription; }; #endif // ADBLOCKRULE_H diff --git a/src/network-web/adblock/adblocksearchtree.cpp b/src/network-web/adblock/adblocksearchtree.cpp index 9c10ab806..2ca8b17ed 100755 --- a/src/network-web/adblock/adblocksearchtree.cpp +++ b/src/network-web/adblock/adblocksearchtree.cpp @@ -26,112 +26,110 @@ AdBlockSearchTree::AdBlockSearchTree() : m_root(new Node) { } AdBlockSearchTree::~AdBlockSearchTree() { - deleteNode(m_root); + deleteNode(m_root); } void AdBlockSearchTree::clear() { - deleteNode(m_root); - m_root = new Node; + deleteNode(m_root); + m_root = new Node; } -bool AdBlockSearchTree::add(const AdBlockRule *rule) { - if (rule->m_type != AdBlockRule::StringContainsMatchRule) { - return false; - } +bool AdBlockSearchTree::add(const AdBlockRule* rule) { + if (rule->m_type != AdBlockRule::StringContainsMatchRule) { + return false; + } - const QString filter = rule->m_matchString; - int len = filter.size(); + const QString filter = rule->m_matchString; + int len = filter.size(); - if (len <= 0) { - qDebug("AdBlockSearchTree: Inserting rule with filter len <= 0!"); - return false; - } + if (len <= 0) { + qDebug("AdBlockSearchTree: Inserting rule with filter len <= 0!"); + return false; + } - Node* node = m_root; + Node* node = m_root; - for (int i = 0; i < len; ++i) { - const QChar c = filter.at(i); - Node *next = node->children.value(c); + for (int i = 0; i < len; ++i) { + const QChar c = filter.at(i); + Node* next = node->children.value(c); - if (!next) { - next = new Node; - next->c = c; - node->children[c] = next; - } + if (!next) { + next = new Node; + next->c = c; + node->children[c] = next; + } - node = next; - } + node = next; + } - node->rule = rule; - - return true; + node->rule = rule; + return true; } -const AdBlockRule *AdBlockSearchTree::find(const QWebEngineUrlRequestInfo &request, const QString &domain, const QString &urlString) const { - int len = urlString.size(); +const AdBlockRule* AdBlockSearchTree::find(const QWebEngineUrlRequestInfo& request, const QString& domain, const QString& urlString) const { + int len = urlString.size(); - if (len <= 0) { - return 0; - } + if (len <= 0) { + return 0; + } - const QChar *string = urlString.constData(); + const QChar* string = urlString.constData(); - for (int i = 0; i < len; ++i) { - const AdBlockRule *rule = prefixSearch(request, domain, urlString, string++, len - i); + for (int i = 0; i < len; ++i) { + const AdBlockRule* rule = prefixSearch(request, domain, urlString, string++, len - i); - if (rule) { - return rule; - } - } + if (rule) { + return rule; + } + } - return 0; + return 0; } -const AdBlockRule *AdBlockSearchTree::prefixSearch(const QWebEngineUrlRequestInfo &request, const QString &domain, const QString &urlString, const QChar *string, int len) const { - if (len <= 0) { - return 0; - } +const AdBlockRule* AdBlockSearchTree::prefixSearch(const QWebEngineUrlRequestInfo& request, const QString& domain, const QString& urlString, const QChar* string, int len) const { + if (len <= 0) { + return 0; + } - QChar c = string[0]; + QChar c = string[0]; + Node* node = m_root->children.value(c); - Node *node = m_root->children.value(c); + if (!node) { + return nullptr; + } - if (!node) { - return nullptr; - } + for (int i = 1; i < len; ++i) { + const QChar c = (++string)[0]; - for (int i = 1; i < len; ++i) { - const QChar c = (++string)[0]; + if (node->rule && node->rule->networkMatch(request, domain, urlString)) { + return node->rule; + } - if (node->rule && node->rule->networkMatch(request, domain, urlString)) { - return node->rule; - } + node = node->children.value(c); - node = node->children.value(c); + if (!node) { + return nullptr; + } + } - if (!node) { - return nullptr; - } - } + if (node->rule && node->rule->networkMatch(request, domain, urlString)) { + return node->rule; + } - if (node->rule && node->rule->networkMatch(request, domain, urlString)) { - return node->rule; - } - - return nullptr; + return nullptr; } void AdBlockSearchTree::deleteNode(AdBlockSearchTree::Node* node) { - if (!node) { - return; - } + if (!node) { + return; + } - QHashIterator i(node->children); + QHashIterator i(node->children); - while (i.hasNext()) { - i.next(); - deleteNode(i.value()); - } + while (i.hasNext()) { + i.next(); + deleteNode(i.value()); + } - delete node; + delete node; } diff --git a/src/network-web/adblock/adblocksearchtree.h b/src/network-web/adblock/adblocksearchtree.h index 3be09c289..cb26a1356 100755 --- a/src/network-web/adblock/adblocksearchtree.h +++ b/src/network-web/adblock/adblocksearchtree.h @@ -27,30 +27,30 @@ class QWebEngineUrlRequestInfo; class AdBlockRule; class AdBlockSearchTree { - public: - explicit AdBlockSearchTree(); - virtual ~AdBlockSearchTree(); + public: + explicit AdBlockSearchTree(); + virtual ~AdBlockSearchTree(); - void clear(); + void clear(); - bool add(const AdBlockRule *rule); - const AdBlockRule *find(const QWebEngineUrlRequestInfo &request, const QString &domain, const QString &urlString) const; + bool add(const AdBlockRule* rule); + const AdBlockRule* find(const QWebEngineUrlRequestInfo& request, const QString& domain, const QString& urlString) const; - private: - struct Node { - QChar c; - const AdBlockRule* rule; - QHash children; + private: + struct Node { + QChar c; + const AdBlockRule* rule; + QHash children; - Node() : c(0) , rule(0) { } - }; + Node() : c(0), rule(0) { } + }; - const AdBlockRule *prefixSearch(const QWebEngineUrlRequestInfo &request, const QString &domain, - const QString &urlString, const QChar *string, int len) const; + const AdBlockRule* prefixSearch(const QWebEngineUrlRequestInfo& request, const QString& domain, + const QString& urlString, const QChar* string, int len) const; - void deleteNode(Node *node); + void deleteNode(Node* node); - Node* m_root; + Node* m_root; }; #endif // ADBLOCKSEARCHTREE_H diff --git a/src/network-web/adblock/adblocksubscription.cpp b/src/network-web/adblock/adblocksubscription.cpp index 90d04ae6a..6d31b1184 100755 --- a/src/network-web/adblock/adblocksubscription.cpp +++ b/src/network-web/adblock/adblocksubscription.cpp @@ -61,361 +61,353 @@ #include -AdBlockSubscription::AdBlockSubscription(const QString &title, QObject* parent) - : QObject(parent), m_reply(0), m_title(title), m_updated(false) { +AdBlockSubscription::AdBlockSubscription(const QString& title, QObject* parent) + : QObject(parent), m_reply(0), m_title(title), m_updated(false) { } QString AdBlockSubscription::title() const { - return m_title; + return m_title; } QString AdBlockSubscription::filePath() const { - return m_filePath; + return m_filePath; } -void AdBlockSubscription::setFilePath(const QString &path) { - m_filePath = path; +void AdBlockSubscription::setFilePath(const QString& path) { + m_filePath = path; } QUrl AdBlockSubscription::url() const { - return m_url; + return m_url; } -void AdBlockSubscription::setUrl(const QUrl &url) { - m_url = url; +void AdBlockSubscription::setUrl(const QUrl& url) { + m_url = url; } -void AdBlockSubscription::loadSubscription(const QStringList &disabledRules) { - QFile file(m_filePath); +void AdBlockSubscription::loadSubscription(const QStringList& disabledRules) { + QFile file(m_filePath); - if (!file.exists()) { - QTimer::singleShot(0, this, SLOT(updateSubscription())); - return; - } + if (!file.exists()) { + QTimer::singleShot(0, this, SLOT(updateSubscription())); + return; + } - if (!file.open(QFile::ReadOnly)) { - qWarning("Unable to open adblock file '%s' for reading.", qPrintable(m_filePath)); - QTimer::singleShot(0, this, SLOT(updateSubscription())); - return; - } + if (!file.open(QFile::ReadOnly)) { + qWarning("Unable to open adblock file '%s' for reading.", qPrintable(m_filePath)); + QTimer::singleShot(0, this, SLOT(updateSubscription())); + return; + } - QTextStream textStream(&file); - textStream.setCodec("UTF-8"); + QTextStream textStream(&file); + textStream.setCodec("UTF-8"); + // Header is on 3rd line. + textStream.readLine(1024); + textStream.readLine(1024); + QString header = textStream.readLine(1024); - // Header is on 3rd line. - textStream.readLine(1024); - textStream.readLine(1024); - QString header = textStream.readLine(1024); + if (!header.startsWith(QL1S("[Adblock")) || m_title.isEmpty()) { + qWarning("Invalid format of AdBlock file '%s'.", qPrintable(m_filePath)); + QTimer::singleShot(0, this, SLOT(updateSubscription())); + return; + } - if (!header.startsWith(QL1S("[Adblock")) || m_title.isEmpty()) { - qWarning("Invalid format of AdBlock file '%s'.", qPrintable(m_filePath)); - QTimer::singleShot(0, this, SLOT(updateSubscription())); - return; - } + m_rules.clear(); - m_rules.clear(); + while (!textStream.atEnd()) { + AdBlockRule* rule = new AdBlockRule(textStream.readLine(), this); - while (!textStream.atEnd()) { - AdBlockRule *rule = new AdBlockRule(textStream.readLine(), this); + if (disabledRules.contains(rule->filter())) { + rule->setEnabled(false); + } - if (disabledRules.contains(rule->filter())) { - rule->setEnabled(false); - } + m_rules.append(rule); + } - m_rules.append(rule); - } - - // Initial update. - if (m_rules.isEmpty() && !m_updated) { - QTimer::singleShot(0, this, SLOT(updateSubscription())); - } + // Initial update. + if (m_rules.isEmpty() && !m_updated) { + QTimer::singleShot(0, this, SLOT(updateSubscription())); + } } void AdBlockSubscription::saveSubscription() { } void AdBlockSubscription::updateSubscription() { - if (m_reply || !m_url.isValid()) { - return; - } + if (m_reply || !m_url.isValid()) { + return; + } - SilentNetworkAccessManager *mgs = new SilentNetworkAccessManager(this); - - m_reply = mgs->get(QNetworkRequest(m_url)); - connect(m_reply, &QNetworkReply::finished, this, &AdBlockSubscription::subscriptionDownloaded); + SilentNetworkAccessManager* mgs = new SilentNetworkAccessManager(this); + m_reply = mgs->get(QNetworkRequest(m_url)); + connect(m_reply, &QNetworkReply::finished, this, &AdBlockSubscription::subscriptionDownloaded); } void AdBlockSubscription::subscriptionDownloaded() { - if (m_reply != qobject_cast(sender())) { - return; - } + if (m_reply != qobject_cast(sender())) { + return; + } - bool error = false; - const QByteArray response = QString::fromUtf8(m_reply->readAll()).toUtf8(); + bool error = false; + const QByteArray response = QString::fromUtf8(m_reply->readAll()).toUtf8(); - if (m_reply->error() != QNetworkReply::NoError || !response.startsWith(QByteArray("[Adblock")) || !saveDownloadedData(response)) { - error = true; - } + if (m_reply->error() != QNetworkReply::NoError || !response.startsWith(QByteArray("[Adblock")) || !saveDownloadedData(response)) { + error = true; + } - m_reply->manager()->deleteLater(); - m_reply->deleteLater(); - m_reply = 0; + m_reply->manager()->deleteLater(); + m_reply->deleteLater(); + m_reply = 0; - if (error) { - emit subscriptionError(tr("Cannot load subscription!")); - return; - } + if (error) { + emit subscriptionError(tr("Cannot load subscription!")); + return; + } - loadSubscription(AdBlockManager::instance()->disabledRules()); - - emit subscriptionUpdated(); - emit subscriptionChanged(); + loadSubscription(AdBlockManager::instance()->disabledRules()); + emit subscriptionUpdated(); + emit subscriptionChanged(); } -bool AdBlockSubscription::saveDownloadedData(const QByteArray &data) { - QSaveFile file(m_filePath); +bool AdBlockSubscription::saveDownloadedData(const QByteArray& data) { + QSaveFile file(m_filePath); - if (!file.open(QFile::WriteOnly)) { - qWarning("Unable to open AdBlock file '%s' for writing.", qPrintable(m_filePath)); - return false; - } - else { - // Write subscription header - file.write(QString("Title: %1\nUrl: %2\n").arg(title(), url().toString()).toUtf8()); - file.write(data); - file.commit(); - return true; - } + if (!file.open(QFile::WriteOnly)) { + qWarning("Unable to open AdBlock file '%s' for writing.", qPrintable(m_filePath)); + return false; + } + + else { + // Write subscription header + file.write(QString("Title: %1\nUrl: %2\n").arg(title(), url().toString()).toUtf8()); + file.write(data); + file.commit(); + return true; + } } -const AdBlockRule *AdBlockSubscription::rule(int offset) const { - if (IS_IN_ARRAY(offset, m_rules)) { - return m_rules[offset]; - } - else { - return 0; - } +const AdBlockRule* AdBlockSubscription::rule(int offset) const { + if (IS_IN_ARRAY(offset, m_rules)) { + return m_rules[offset]; + } + + else { + return 0; + } } QVector AdBlockSubscription::allRules() const { - return m_rules; + return m_rules; } -const AdBlockRule *AdBlockSubscription::enableRule(int offset) { - if (IS_IN_ARRAY(offset, m_rules)) { - AdBlockRule *rule = m_rules[offset]; - rule->setEnabled(true); - AdBlockManager::instance()->removeDisabledRule(rule->filter()); +const AdBlockRule* AdBlockSubscription::enableRule(int offset) { + if (IS_IN_ARRAY(offset, m_rules)) { + AdBlockRule* rule = m_rules[offset]; + rule->setEnabled(true); + AdBlockManager::instance()->removeDisabledRule(rule->filter()); + emit subscriptionChanged(); - emit subscriptionChanged(); + if (rule->isCssRule()) { + // TODO: opravdu? + //mApp->reloadUserStyleSheet(); + } - if (rule->isCssRule()) { - // TODO: opravdu? - //mApp->reloadUserStyleSheet(); - } + return rule; + } - return rule; - } - else { - return 0; - } + else { + return 0; + } } -const AdBlockRule *AdBlockSubscription::disableRule(int offset) { - if (!IS_IN_ARRAY(offset, m_rules)) { - return 0; - } +const AdBlockRule* AdBlockSubscription::disableRule(int offset) { + if (!IS_IN_ARRAY(offset, m_rules)) { + return 0; + } - AdBlockRule* rule = m_rules[offset]; - rule->setEnabled(false); - AdBlockManager::instance()->addDisabledRule(rule->filter()); + AdBlockRule* rule = m_rules[offset]; + rule->setEnabled(false); + AdBlockManager::instance()->addDisabledRule(rule->filter()); + emit subscriptionChanged(); - emit subscriptionChanged(); + if (rule->isCssRule()) { + // TODO: opravdu? + //mApp->reloadUserStyleSheet(); + } - if (rule->isCssRule()) { - // TODO: opravdu? - //mApp->reloadUserStyleSheet(); - } - - return rule; + return rule; } bool AdBlockSubscription::canEditRules() const { - return false; + return false; } bool AdBlockSubscription::canBeRemoved() const { - return true; + return true; } int AdBlockSubscription::addRule(AdBlockRule* rule) { - Q_UNUSED(rule) - return -1; + Q_UNUSED(rule) + return -1; } bool AdBlockSubscription::removeRule(int offset) { - Q_UNUSED(offset) - return false; + Q_UNUSED(offset) + return false; } -const AdBlockRule *AdBlockSubscription::replaceRule(AdBlockRule *rule, int offset) { - Q_UNUSED(rule) - Q_UNUSED(offset) - return 0; +const AdBlockRule* AdBlockSubscription::replaceRule(AdBlockRule* rule, int offset) { + Q_UNUSED(rule) + Q_UNUSED(offset) + return 0; } AdBlockSubscription::~AdBlockSubscription() { - qDeleteAll(m_rules); + qDeleteAll(m_rules); } // AdBlockCustomList -AdBlockCustomList::AdBlockCustomList(QObject *parent) - : AdBlockSubscription(tr("Custom rules"), parent) { - setFilePath(AdBlockManager::storedListsPath() + QDir::separator() + ADBLOCK_CUSTOMLIST_NAME); +AdBlockCustomList::AdBlockCustomList(QObject* parent) + : AdBlockSubscription(tr("Custom rules"), parent) { + setFilePath(AdBlockManager::storedListsPath() + QDir::separator() + ADBLOCK_CUSTOMLIST_NAME); } -void AdBlockCustomList::loadSubscription(const QStringList &disabledRules) { - // DuckDuckGo ad whitelist rules - // They cannot be removed, but can be disabled. - // Please consider not disabling them. Thanks! +void AdBlockCustomList::loadSubscription(const QStringList& disabledRules) { + // DuckDuckGo ad whitelist rules + // They cannot be removed, but can be disabled. + // Please consider not disabling them. Thanks! + const QString ddg1 = QSL("@@||duckduckgo.com^$document"); + const QString ddg2 = QSL("duckduckgo.com#@#.has-ad"); + QString rules; - const QString ddg1 = QSL("@@||duckduckgo.com^$document"); - const QString ddg2 = QSL("duckduckgo.com#@#.has-ad"); - QString rules; + try { + rules = QString::fromUtf8(IOFactory::readTextFile(filePath())); + } - try { - rules = QString::fromUtf8(IOFactory::readTextFile(filePath())); - } - catch (ApplicationException&) { + catch (ApplicationException&) { + } - } + QFile file(filePath()); - QFile file(filePath()); + if (!file.exists()) { + saveSubscription(); + } - if (!file.exists()) { - saveSubscription(); - } + if (file.open(QFile::WriteOnly | QFile::Append)) { + QTextStream stream(&file); + stream.setCodec("UTF-8"); - if (file.open(QFile::WriteOnly | QFile::Append)) { - QTextStream stream(&file); - stream.setCodec("UTF-8"); + if (!rules.contains(ddg1 + QL1S("\n"))) { + stream << ddg1 << endl; + } - if (!rules.contains(ddg1 + QL1S("\n"))) - stream << ddg1 << endl; + if (!rules.contains(QL1S("\n") + ddg2)) { + stream << ddg2 << endl; + } + } - if (!rules.contains(QL1S("\n") + ddg2)) - stream << ddg2 << endl; - } - - file.close(); - - AdBlockSubscription::loadSubscription(disabledRules); + file.close(); + AdBlockSubscription::loadSubscription(disabledRules); } void AdBlockCustomList::saveSubscription() { - QFile file(filePath()); + QFile file(filePath()); - if (!file.open(QFile::ReadWrite | QFile::Truncate)) { - qWarning("Unable to open AdBlock file '%s' for writing.", qPrintable(filePath())); - return; - } + if (!file.open(QFile::ReadWrite | QFile::Truncate)) { + qWarning("Unable to open AdBlock file '%s' for writing.", qPrintable(filePath())); + return; + } - QTextStream textStream(&file); + QTextStream textStream(&file); + textStream.setCodec("UTF-8"); + textStream << "Title: " << title() << endl; + textStream << "Url: " << url().toString() << endl; + textStream << "[Adblock Plus 1.1.1]" << endl; - textStream.setCodec("UTF-8"); - textStream << "Title: " << title() << endl; - textStream << "Url: " << url().toString() << endl; - textStream << "[Adblock Plus 1.1.1]" << endl; + foreach (const AdBlockRule* rule, m_rules) { + textStream << rule->filter() << endl; + } - foreach (const AdBlockRule *rule, m_rules) { - textStream << rule->filter() << endl; - } - - file.close(); + file.close(); } bool AdBlockCustomList::canEditRules() const { - return true; + return true; } bool AdBlockCustomList::canBeRemoved() const { - return false; + return false; } -bool AdBlockCustomList::containsFilter(const QString &filter) const { - foreach (const AdBlockRule *rule, m_rules) { - if (rule->filter() == filter) { - return true; - } - } +bool AdBlockCustomList::containsFilter(const QString& filter) const { + foreach (const AdBlockRule* rule, m_rules) { + if (rule->filter() == filter) { + return true; + } + } - return false; + return false; } -bool AdBlockCustomList::removeFilter(const QString &filter) { - for (int i = 0; i < m_rules.count(); ++i) { - const AdBlockRule *rule = m_rules.at(i); +bool AdBlockCustomList::removeFilter(const QString& filter) { + for (int i = 0; i < m_rules.count(); ++i) { + const AdBlockRule* rule = m_rules.at(i); - if (rule->filter() == filter) { - return removeRule(i); - } - } + if (rule->filter() == filter) { + return removeRule(i); + } + } - return false; + return false; } int AdBlockCustomList::addRule(AdBlockRule* rule) { - m_rules.append(rule); + m_rules.append(rule); + emit subscriptionChanged(); - emit subscriptionChanged(); + if (rule->isCssRule()) { + // TODO: opravdu + //mApp->reloadUserStyleSheet(); + } - if (rule->isCssRule()) { - // TODO: opravdu - //mApp->reloadUserStyleSheet(); - } - - return m_rules.count() - 1; + return m_rules.count() - 1; } bool AdBlockCustomList::removeRule(int offset) { - if (!IS_IN_ARRAY(offset, m_rules)) { - return false; - } + if (!IS_IN_ARRAY(offset, m_rules)) { + return false; + } - AdBlockRule *rule = m_rules.at(offset); - const QString filter = rule->filter(); + AdBlockRule* rule = m_rules.at(offset); + const QString filter = rule->filter(); + m_rules.remove(offset); + emit subscriptionChanged(); - m_rules.remove(offset); + if (rule->isCssRule()) { + // TODO: opravdu + //mApp->reloadUserStyleSheet(); + } - emit subscriptionChanged(); - - if (rule->isCssRule()) { - // TODO: opravdu - //mApp->reloadUserStyleSheet(); - } - - AdBlockManager::instance()->removeDisabledRule(filter); - - delete rule; - return true; + AdBlockManager::instance()->removeDisabledRule(filter); + delete rule; + return true; } -const AdBlockRule *AdBlockCustomList::replaceRule(AdBlockRule *rule, int offset) { - if (!IS_IN_ARRAY(offset, m_rules)) { - return 0; - } +const AdBlockRule* AdBlockCustomList::replaceRule(AdBlockRule* rule, int offset) { + if (!IS_IN_ARRAY(offset, m_rules)) { + return 0; + } - AdBlockRule *oldRule = m_rules.at(offset); - m_rules[offset] = rule; + AdBlockRule* oldRule = m_rules.at(offset); + m_rules[offset] = rule; + emit subscriptionChanged(); - emit subscriptionChanged(); + if (rule->isCssRule() || oldRule->isCssRule()) { + // TODO: opravdu + //mApp->reloadUserStyleSheet(); + } - if (rule->isCssRule() || oldRule->isCssRule()) { - // TODO: opravdu - //mApp->reloadUserStyleSheet(); - } - - delete oldRule; - return m_rules[offset]; + delete oldRule; + return m_rules[offset]; } diff --git a/src/network-web/adblock/adblocksubscription.h b/src/network-web/adblock/adblocksubscription.h index 07fd296d5..e3ecfc56c 100755 --- a/src/network-web/adblock/adblocksubscription.h +++ b/src/network-web/adblock/adblocksubscription.h @@ -58,79 +58,79 @@ class QUrl; class QNetworkReply; class AdBlockSubscription : public QObject { - Q_OBJECT + Q_OBJECT - public: - explicit AdBlockSubscription(const QString &title, QObject *parent = 0); - virtual ~AdBlockSubscription(); + public: + explicit AdBlockSubscription(const QString& title, QObject* parent = 0); + virtual ~AdBlockSubscription(); - QString title() const; + QString title() const; - QString filePath() const; - void setFilePath(const QString &path); + QString filePath() const; + void setFilePath(const QString& path); - QUrl url() const; - void setUrl(const QUrl &url); + QUrl url() const; + void setUrl(const QUrl& url); - virtual void loadSubscription(const QStringList &disabledRules); - virtual void saveSubscription(); + virtual void loadSubscription(const QStringList& disabledRules); + virtual void saveSubscription(); - const AdBlockRule *rule(int offset) const; - QVector allRules() const; + const AdBlockRule* rule(int offset) const; + QVector allRules() const; - const AdBlockRule *enableRule(int offset); - const AdBlockRule *disableRule(int offset); + const AdBlockRule* enableRule(int offset); + const AdBlockRule* disableRule(int offset); - virtual bool canEditRules() const; - virtual bool canBeRemoved() const; + virtual bool canEditRules() const; + virtual bool canBeRemoved() const; - virtual int addRule(AdBlockRule* rule); - virtual bool removeRule(int offset); - virtual const AdBlockRule *replaceRule(AdBlockRule *rule, int offset); + virtual int addRule(AdBlockRule* rule); + virtual bool removeRule(int offset); + virtual const AdBlockRule* replaceRule(AdBlockRule* rule, int offset); - public slots: - void updateSubscription(); + public slots: + void updateSubscription(); - signals: - void subscriptionChanged(); - void subscriptionUpdated(); - void subscriptionError(const QString &message); + signals: + void subscriptionChanged(); + void subscriptionUpdated(); + void subscriptionError(const QString& message); - protected slots: - void subscriptionDownloaded(); + protected slots: + void subscriptionDownloaded(); - protected: - virtual bool saveDownloadedData(const QByteArray &data); + protected: + virtual bool saveDownloadedData(const QByteArray& data); - QNetworkReply *m_reply; - QVector m_rules; + QNetworkReply* m_reply; + QVector m_rules; - private: - QString m_title; - QString m_filePath; + private: + QString m_title; + QString m_filePath; - QUrl m_url; - bool m_updated; + QUrl m_url; + bool m_updated; }; class AdBlockCustomList : public AdBlockSubscription { - Q_OBJECT + Q_OBJECT - public: - explicit AdBlockCustomList(QObject *parent = 0); + public: + explicit AdBlockCustomList(QObject* parent = 0); - void loadSubscription(const QStringList &disabledRules); - void saveSubscription(); + void loadSubscription(const QStringList& disabledRules); + void saveSubscription(); - bool canEditRules() const; - bool canBeRemoved() const; + bool canEditRules() const; + bool canBeRemoved() const; - bool containsFilter(const QString &filter) const; - bool removeFilter(const QString &filter); + bool containsFilter(const QString& filter) const; + bool removeFilter(const QString& filter); - int addRule(AdBlockRule *rule); - bool removeRule(int offset); - const AdBlockRule *replaceRule(AdBlockRule *rule, int offset); + int addRule(AdBlockRule* rule); + bool removeRule(int offset); + const AdBlockRule* replaceRule(AdBlockRule* rule, int offset); }; #endif // ADBLOCKSUBSCRIPTION_H diff --git a/src/network-web/adblock/adblocktreewidget.cpp b/src/network-web/adblock/adblocktreewidget.cpp index db2970b37..821ed2813 100755 --- a/src/network-web/adblock/adblocktreewidget.cpp +++ b/src/network-web/adblock/adblocktreewidget.cpp @@ -27,232 +27,221 @@ #include -AdBlockTreeWidget::AdBlockTreeWidget(AdBlockSubscription *subscription, QWidget *parent) - : TreeWidget(parent), m_subscription(subscription), m_topItem(0), m_itemChangingBlock(false) { - setContextMenuPolicy(Qt::CustomContextMenu); - setDefaultItemShowMode(TreeWidget::ItemsExpanded); - setHeaderHidden(true); - setAlternatingRowColors(true); - setLayoutDirection(Qt::LeftToRight); - - connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint))); - connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(itemChanged(QTreeWidgetItem*))); - connect(m_subscription, SIGNAL(subscriptionUpdated()), this, SLOT(subscriptionUpdated())); - connect(m_subscription, SIGNAL(subscriptionError(QString)), this, SLOT(subscriptionError(QString))); +AdBlockTreeWidget::AdBlockTreeWidget(AdBlockSubscription* subscription, QWidget* parent) + : TreeWidget(parent), m_subscription(subscription), m_topItem(0), m_itemChangingBlock(false) { + setContextMenuPolicy(Qt::CustomContextMenu); + setDefaultItemShowMode(TreeWidget::ItemsExpanded); + setHeaderHidden(true); + setAlternatingRowColors(true); + setLayoutDirection(Qt::LeftToRight); + connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint))); + connect(this, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemChanged(QTreeWidgetItem*))); + connect(m_subscription, SIGNAL(subscriptionUpdated()), this, SLOT(subscriptionUpdated())); + connect(m_subscription, SIGNAL(subscriptionError(QString)), this, SLOT(subscriptionError(QString))); } -AdBlockSubscription *AdBlockTreeWidget::subscription() const { - return m_subscription; +AdBlockSubscription* AdBlockTreeWidget::subscription() const { + return m_subscription; } -void AdBlockTreeWidget::showRule(const AdBlockRule *rule) { - if (!m_topItem && rule) { - m_ruleToBeSelected = rule->filter(); - } - else if (!m_ruleToBeSelected.isEmpty()) { - QList items = findItems(m_ruleToBeSelected, Qt::MatchRecursive); +void AdBlockTreeWidget::showRule(const AdBlockRule* rule) { + if (!m_topItem && rule) { + m_ruleToBeSelected = rule->filter(); + } - if (!items.isEmpty()) { - QTreeWidgetItem *item = items.at(0); + else if (!m_ruleToBeSelected.isEmpty()) { + QList items = findItems(m_ruleToBeSelected, Qt::MatchRecursive); - setCurrentItem(item); - scrollToItem(item, QAbstractItemView::PositionAtCenter); - } + if (!items.isEmpty()) { + QTreeWidgetItem* item = items.at(0); + setCurrentItem(item); + scrollToItem(item, QAbstractItemView::PositionAtCenter); + } - m_ruleToBeSelected.clear(); - } + m_ruleToBeSelected.clear(); + } } -void AdBlockTreeWidget::contextMenuRequested(const QPoint &pos) { - if (!m_subscription->canEditRules()) { - return; - } +void AdBlockTreeWidget::contextMenuRequested(const QPoint& pos) { + if (!m_subscription->canEditRules()) { + return; + } - QTreeWidgetItem *item = itemAt(pos); + QTreeWidgetItem* item = itemAt(pos); - if (!item) { - return; - } + if (!item) { + return; + } - QMenu menu; - menu.addAction(tr("Add rule"), this, SLOT(addRule())); - menu.addSeparator(); - QAction* deleteAction = menu.addAction(tr("Remove rule"), this, SLOT(removeRule())); + QMenu menu; + menu.addAction(tr("Add rule"), this, SLOT(addRule())); + menu.addSeparator(); + QAction* deleteAction = menu.addAction(tr("Remove rule"), this, SLOT(removeRule())); - if (!item->parent()) { - deleteAction->setDisabled(true); - } + if (!item->parent()) { + deleteAction->setDisabled(true); + } - menu.exec(viewport()->mapToGlobal(pos)); + menu.exec(viewport()->mapToGlobal(pos)); } -void AdBlockTreeWidget::itemChanged(QTreeWidgetItem *item) { - if (!item || m_itemChangingBlock) { - return; - } +void AdBlockTreeWidget::itemChanged(QTreeWidgetItem* item) { + if (!item || m_itemChangingBlock) { + return; + } - m_itemChangingBlock = true; + m_itemChangingBlock = true; + int offset = item->data(0, Qt::UserRole + 10).toInt(); + const AdBlockRule* oldRule = m_subscription->rule(offset); - int offset = item->data(0, Qt::UserRole + 10).toInt(); - const AdBlockRule *oldRule = m_subscription->rule(offset); + if (item->checkState(0) == Qt::Unchecked && oldRule->isEnabled()) { + // Disable rule. + const AdBlockRule* rule = m_subscription->disableRule(offset); + adjustItemFeatures(item, rule); + } - if (item->checkState(0) == Qt::Unchecked && oldRule->isEnabled()) { - // Disable rule. - const AdBlockRule *rule = m_subscription->disableRule(offset); + else if (item->checkState(0) == Qt::Checked && !oldRule->isEnabled()) { + // Enable rule. + const AdBlockRule* rule = m_subscription->enableRule(offset); + adjustItemFeatures(item, rule); + } - adjustItemFeatures(item, rule); - } - else if (item->checkState(0) == Qt::Checked && !oldRule->isEnabled()) { - // Enable rule. - const AdBlockRule *rule = m_subscription->enableRule(offset); + else if (m_subscription->canEditRules()) { + // Custom rule has been changed. + AdBlockRule* newRule = new AdBlockRule(item->text(0), m_subscription); + const AdBlockRule* rule = m_subscription->replaceRule(newRule, offset); + adjustItemFeatures(item, rule); + } - adjustItemFeatures(item, rule); - } - else if (m_subscription->canEditRules()) { - // Custom rule has been changed. - AdBlockRule *newRule = new AdBlockRule(item->text(0), m_subscription); - const AdBlockRule *rule = m_subscription->replaceRule(newRule, offset); - - adjustItemFeatures(item, rule); - } - - m_itemChangingBlock = false; + m_itemChangingBlock = false; } void AdBlockTreeWidget::copyFilter() { - QTreeWidgetItem *item = currentItem(); + QTreeWidgetItem* item = currentItem(); - if (!item) { - return; - } + if (!item) { + return; + } - QApplication::clipboard()->setText(item->text(0)); + QApplication::clipboard()->setText(item->text(0)); } void AdBlockTreeWidget::addRule() { - if (!m_subscription->canEditRules()) { - return; - } + if (!m_subscription->canEditRules()) { + return; + } - QString newRule = QInputDialog::getText(this, tr("Add custom rule"), tr("Please write your rule here:")); + QString newRule = QInputDialog::getText(this, tr("Add custom rule"), tr("Please write your rule here:")); - if (newRule.isEmpty()) { - return; - } + if (newRule.isEmpty()) { + return; + } - AdBlockRule *rule = new AdBlockRule(newRule, m_subscription); - int offset = m_subscription->addRule(rule); - - QTreeWidgetItem *item = new QTreeWidgetItem(); - item->setText(0, newRule); - item->setData(0, Qt::UserRole + 10, offset); - item->setFlags(item->flags() | Qt::ItemIsEditable); - - m_itemChangingBlock = true; - m_topItem->addChild(item); - m_itemChangingBlock = false; - - adjustItemFeatures(item, rule); + AdBlockRule* rule = new AdBlockRule(newRule, m_subscription); + int offset = m_subscription->addRule(rule); + QTreeWidgetItem* item = new QTreeWidgetItem(); + item->setText(0, newRule); + item->setData(0, Qt::UserRole + 10, offset); + item->setFlags(item->flags() | Qt::ItemIsEditable); + m_itemChangingBlock = true; + m_topItem->addChild(item); + m_itemChangingBlock = false; + adjustItemFeatures(item, rule); } void AdBlockTreeWidget::removeRule() { - QTreeWidgetItem *item = currentItem(); + QTreeWidgetItem* item = currentItem(); - if (!item || !m_subscription->canEditRules() || item == m_topItem) { - return; - } + if (!item || !m_subscription->canEditRules() || item == m_topItem) { + return; + } - int offset = item->data(0, Qt::UserRole + 10).toInt(); - - m_subscription->removeRule(offset); - deleteItem(item); + int offset = item->data(0, Qt::UserRole + 10).toInt(); + m_subscription->removeRule(offset); + deleteItem(item); } void AdBlockTreeWidget::subscriptionUpdated() { - refresh(); - - m_itemChangingBlock = true; - m_topItem->setText(0, tr("%1 (recently updated)").arg(m_subscription->title())); - m_itemChangingBlock = false; + refresh(); + m_itemChangingBlock = true; + m_topItem->setText(0, tr("%1 (recently updated)").arg(m_subscription->title())); + m_itemChangingBlock = false; } -void AdBlockTreeWidget::subscriptionError(const QString &message) { - refresh(); - - m_itemChangingBlock = true; - m_topItem->setText(0, tr("%1 (Error: %2)").arg(m_subscription->title(), message)); - m_itemChangingBlock = false; +void AdBlockTreeWidget::subscriptionError(const QString& message) { + refresh(); + m_itemChangingBlock = true; + m_topItem->setText(0, tr("%1 (Error: %2)").arg(m_subscription->title(), message)); + m_itemChangingBlock = false; } void AdBlockTreeWidget::adjustItemFeatures(QTreeWidgetItem* item, const AdBlockRule* rule) { - if (!rule->isEnabled()) { - QFont font; - font.setItalic(true); - item->setForeground(0, QColor(Qt::gray)); + if (!rule->isEnabled()) { + QFont font; + font.setItalic(true); + item->setForeground(0, QColor(Qt::gray)); - if (!rule->isComment()) { - item->setFlags(item->flags() | Qt::ItemIsUserCheckable); - item->setCheckState(0, Qt::Unchecked); - item->setFont(0, font); - } + if (!rule->isComment()) { + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(0, Qt::Unchecked); + item->setFont(0, font); + } - return; - } + return; + } - item->setFlags(item->flags() | Qt::ItemIsUserCheckable); - item->setCheckState(0, Qt::Checked); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(0, Qt::Checked); - if (rule->isException()) { - item->setForeground(0, QColor(Qt::darkGreen)); - item->setFont(0, QFont()); - } - else if (rule->isCssRule()) { - item->setForeground(0, QColor(Qt::darkBlue)); - item->setFont(0, QFont()); - } + if (rule->isException()) { + item->setForeground(0, QColor(Qt::darkGreen)); + item->setFont(0, QFont()); + } + + else if (rule->isCssRule()) { + item->setForeground(0, QColor(Qt::darkBlue)); + item->setFont(0, QFont()); + } } void AdBlockTreeWidget::keyPressEvent(QKeyEvent* event) { - if (event->key() == Qt::Key_C && event->modifiers() & Qt::ControlModifier) { - copyFilter(); - } + if (event->key() == Qt::Key_C && event->modifiers() & Qt::ControlModifier) { + copyFilter(); + } - if (event->key() == Qt::Key_Delete) { - removeRule(); - } + if (event->key() == Qt::Key_Delete) { + removeRule(); + } - TreeWidget::keyPressEvent(event); + TreeWidget::keyPressEvent(event); } void AdBlockTreeWidget::refresh() { - m_itemChangingBlock = true; - clear(); + m_itemChangingBlock = true; + clear(); + QFont boldFont; + boldFont.setBold(true); + m_topItem = new QTreeWidgetItem(this); + m_topItem->setText(0, m_subscription->title()); + m_topItem->setFont(0, boldFont); + m_topItem->setExpanded(true); + addTopLevelItem(m_topItem); + const QVector& allRules = m_subscription->allRules(); + int index = 0; - QFont boldFont; - boldFont.setBold(true); + foreach (const AdBlockRule* rule, allRules) { + QTreeWidgetItem* item = new QTreeWidgetItem(m_topItem); + item->setText(0, rule->filter()); + item->setData(0, Qt::UserRole + 10, index); - m_topItem = new QTreeWidgetItem(this); - m_topItem->setText(0, m_subscription->title()); - m_topItem->setFont(0, boldFont); - m_topItem->setExpanded(true); - addTopLevelItem(m_topItem); + if (m_subscription->canEditRules()) { + item->setFlags(item->flags() | Qt::ItemIsEditable); + } - const QVector &allRules = m_subscription->allRules(); + adjustItemFeatures(item, rule); + ++index; + } - int index = 0; - foreach (const AdBlockRule *rule, allRules) { - QTreeWidgetItem *item = new QTreeWidgetItem(m_topItem); - item->setText(0, rule->filter()); - item->setData(0, Qt::UserRole + 10, index); - - if (m_subscription->canEditRules()) { - item->setFlags(item->flags() | Qt::ItemIsEditable); - } - - adjustItemFeatures(item, rule); - ++index; - } - - showRule(0); - m_itemChangingBlock = false; + showRule(0); + m_itemChangingBlock = false; } diff --git a/src/network-web/adblock/adblocktreewidget.h b/src/network-web/adblock/adblocktreewidget.h index 3407345f0..cc4d1c69f 100755 --- a/src/network-web/adblock/adblocktreewidget.h +++ b/src/network-web/adblock/adblocktreewidget.h @@ -26,37 +26,37 @@ class AdBlockSubscription; class AdBlockRule; class AdBlockTreeWidget : public TreeWidget { - Q_OBJECT + Q_OBJECT - public: - explicit AdBlockTreeWidget(AdBlockSubscription *subscription, QWidget *parent = 0); + public: + explicit AdBlockTreeWidget(AdBlockSubscription* subscription, QWidget* parent = 0); - AdBlockSubscription *subscription() const; + AdBlockSubscription* subscription() const; - void showRule(const AdBlockRule *rule); - void refresh(); + void showRule(const AdBlockRule* rule); + void refresh(); - public slots: - void addRule(); - void removeRule(); + public slots: + void addRule(); + void removeRule(); - private slots: - void contextMenuRequested(const QPoint &pos); - void itemChanged(QTreeWidgetItem *item); - void copyFilter(); + private slots: + void contextMenuRequested(const QPoint& pos); + void itemChanged(QTreeWidgetItem* item); + void copyFilter(); - void subscriptionUpdated(); - void subscriptionError(const QString &message); + void subscriptionUpdated(); + void subscriptionError(const QString& message); - private: - void adjustItemFeatures(QTreeWidgetItem *item, const AdBlockRule *rule); - void keyPressEvent(QKeyEvent* event); + private: + void adjustItemFeatures(QTreeWidgetItem* item, const AdBlockRule* rule); + void keyPressEvent(QKeyEvent* event); - AdBlockSubscription *m_subscription; - QTreeWidgetItem *m_topItem; + AdBlockSubscription* m_subscription; + QTreeWidgetItem* m_topItem; - QString m_ruleToBeSelected; - bool m_itemChangingBlock; + QString m_ruleToBeSelected; + bool m_itemChangingBlock; }; #endif // ADBLOCKTREEWIDGET_H diff --git a/src/network-web/adblock/adblockurlinterceptor.cpp b/src/network-web/adblock/adblockurlinterceptor.cpp index 63d07c705..d00fab6e7 100755 --- a/src/network-web/adblock/adblockurlinterceptor.cpp +++ b/src/network-web/adblock/adblockurlinterceptor.cpp @@ -21,12 +21,12 @@ #include "network-web/adblock/adblockmanager.h" -AdBlockUrlInterceptor::AdBlockUrlInterceptor(AdBlockManager *manager) - : UrlInterceptor(manager), m_manager(manager) { +AdBlockUrlInterceptor::AdBlockUrlInterceptor(AdBlockManager* manager) + : UrlInterceptor(manager), m_manager(manager) { } -void AdBlockUrlInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) { - if (m_manager->block(info)) { - info.block(true); - } +void AdBlockUrlInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) { + if (m_manager->block(info)) { + info.block(true); + } } diff --git a/src/network-web/adblock/adblockurlinterceptor.h b/src/network-web/adblock/adblockurlinterceptor.h index 76e627572..affc1d57e 100755 --- a/src/network-web/adblock/adblockurlinterceptor.h +++ b/src/network-web/adblock/adblockurlinterceptor.h @@ -25,15 +25,15 @@ class AdBlockManager; class AdBlockUrlInterceptor : public UrlInterceptor { - Q_OBJECT + Q_OBJECT - public: - explicit AdBlockUrlInterceptor(AdBlockManager *manager); + public: + explicit AdBlockUrlInterceptor(AdBlockManager* manager); - void interceptRequest(QWebEngineUrlRequestInfo &info); + void interceptRequest(QWebEngineUrlRequestInfo& info); - private: - AdBlockManager *m_manager; + private: + AdBlockManager* m_manager; }; #endif // ADBLOCKURLINTERCEPTOR_H diff --git a/src/network-web/basenetworkaccessmanager.cpp b/src/network-web/basenetworkaccessmanager.cpp index 3974fbfe7..00965aa51 100755 --- a/src/network-web/basenetworkaccessmanager.cpp +++ b/src/network-web/basenetworkaccessmanager.cpp @@ -1,82 +1,80 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "network-web/basenetworkaccessmanager.h" - -#include "miscellaneous/application.h" -#include "miscellaneous/textfactory.h" - -#include -#include -#include - - -BaseNetworkAccessManager::BaseNetworkAccessManager(QObject *parent) - : QNetworkAccessManager(parent) { - connect(this, &BaseNetworkAccessManager::sslErrors, this, &BaseNetworkAccessManager::onSslErrors); - loadSettings(); -} - -BaseNetworkAccessManager::~BaseNetworkAccessManager() { -} - -void BaseNetworkAccessManager::loadSettings() { - QNetworkProxy new_proxy; - const QNetworkProxy::ProxyType selected_proxy_type = static_cast(qApp->settings()->value(GROUP(Proxy), - SETTING(Proxy::Type)).toInt()); - - if (selected_proxy_type == QNetworkProxy::NoProxy) { - // No extra setting is needed, set new proxy and exit this method. - setProxy(QNetworkProxy::NoProxy); - } - else if (selected_proxy_type == QNetworkProxy::DefaultProxy) { - setProxy(QNetworkProxy::applicationProxy()); - } - else { - const Settings *settings = qApp->settings(); - - // Custom proxy is selected, set it up. - new_proxy.setType(selected_proxy_type); - new_proxy.setHostName(settings->value(GROUP(Proxy), SETTING(Proxy::Host)).toString()); - new_proxy.setPort(settings->value(GROUP(Proxy), SETTING(Proxy::Port)).toInt()); - new_proxy.setUser(settings->value(GROUP(Proxy), SETTING(Proxy::Username)).toString()); - new_proxy.setPassword(TextFactory::decrypt(settings->value(GROUP(Proxy), SETTING(Proxy::Password)).toString())); - setProxy(new_proxy); - } - - qDebug("Settings of BaseNetworkAccessManager loaded."); -} - -void BaseNetworkAccessManager::onSslErrors(QNetworkReply *reply, const QList &error) { - qWarning("Ignoring SSL errors for '%s': '%s' (code %d).", qPrintable(reply->url().toString()), qPrintable(reply->errorString()), (int) reply->error()); - reply->ignoreSslErrors(error); -} - -QNetworkReply *BaseNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op, - const QNetworkRequest &request, - QIODevice *outgoingData) { - QNetworkRequest new_request = request; - - // This rapidly speeds up loading of web sites. - // NOTE: https://en.wikipedia.org/wiki/HTTP_pipelining - new_request.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); - - // Setup custom user-agent. - new_request.setRawHeader(USER_AGENT_HTTP_HEADER, QString(APP_USERAGENT).toLocal8Bit()); - - return QNetworkAccessManager::createRequest(op, new_request, outgoingData); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "network-web/basenetworkaccessmanager.h" + +#include "miscellaneous/application.h" +#include "miscellaneous/textfactory.h" + +#include +#include +#include + + +BaseNetworkAccessManager::BaseNetworkAccessManager(QObject* parent) + : QNetworkAccessManager(parent) { + connect(this, &BaseNetworkAccessManager::sslErrors, this, &BaseNetworkAccessManager::onSslErrors); + loadSettings(); +} + +BaseNetworkAccessManager::~BaseNetworkAccessManager() { +} + +void BaseNetworkAccessManager::loadSettings() { + QNetworkProxy new_proxy; + const QNetworkProxy::ProxyType selected_proxy_type = static_cast(qApp->settings()->value(GROUP(Proxy), + SETTING(Proxy::Type)).toInt()); + + if (selected_proxy_type == QNetworkProxy::NoProxy) { + // No extra setting is needed, set new proxy and exit this method. + setProxy(QNetworkProxy::NoProxy); + } + + else if (selected_proxy_type == QNetworkProxy::DefaultProxy) { + setProxy(QNetworkProxy::applicationProxy()); + } + + else { + const Settings* settings = qApp->settings(); + // Custom proxy is selected, set it up. + new_proxy.setType(selected_proxy_type); + new_proxy.setHostName(settings->value(GROUP(Proxy), SETTING(Proxy::Host)).toString()); + new_proxy.setPort(settings->value(GROUP(Proxy), SETTING(Proxy::Port)).toInt()); + new_proxy.setUser(settings->value(GROUP(Proxy), SETTING(Proxy::Username)).toString()); + new_proxy.setPassword(TextFactory::decrypt(settings->value(GROUP(Proxy), SETTING(Proxy::Password)).toString())); + setProxy(new_proxy); + } + + qDebug("Settings of BaseNetworkAccessManager loaded."); +} + +void BaseNetworkAccessManager::onSslErrors(QNetworkReply* reply, const QList& error) { + qWarning("Ignoring SSL errors for '%s': '%s' (code %d).", qPrintable(reply->url().toString()), qPrintable(reply->errorString()), (int) reply->error()); + reply->ignoreSslErrors(error); +} + +QNetworkReply* BaseNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op, + const QNetworkRequest& request, + QIODevice* outgoingData) { + QNetworkRequest new_request = request; + // This rapidly speeds up loading of web sites. + // NOTE: https://en.wikipedia.org/wiki/HTTP_pipelining + new_request.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + // Setup custom user-agent. + new_request.setRawHeader(USER_AGENT_HTTP_HEADER, QString(APP_USERAGENT).toLocal8Bit()); + return QNetworkAccessManager::createRequest(op, new_request, outgoingData); +} diff --git a/src/network-web/basenetworkaccessmanager.h b/src/network-web/basenetworkaccessmanager.h index 3d8e2dfd1..1e2b25833 100755 --- a/src/network-web/basenetworkaccessmanager.h +++ b/src/network-web/basenetworkaccessmanager.h @@ -1,47 +1,47 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef BASENETWORKACCESSMANAGER_H -#define BASENETWORKACCESSMANAGER_H - -#include - - -// This is base class for all network access managers. -class BaseNetworkAccessManager : public QNetworkAccessManager { - Q_OBJECT - - public: - // Constructors and desctructors. - explicit BaseNetworkAccessManager(QObject *parent = 0); - virtual ~BaseNetworkAccessManager(); - - public slots: - // Loads network settings for this instance. - // NOTE: This sets up proxy settings. - virtual void loadSettings(); - - protected slots: - // Called when some SSL-related errors are detected. - void onSslErrors(QNetworkReply *reply, const QList &error); - - protected: - // Creates custom request. - QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData); -}; - -#endif // BASENETWORKACCESSMANAGER_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef BASENETWORKACCESSMANAGER_H +#define BASENETWORKACCESSMANAGER_H + +#include + + +// This is base class for all network access managers. +class BaseNetworkAccessManager : public QNetworkAccessManager { + Q_OBJECT + + public: + // Constructors and desctructors. + explicit BaseNetworkAccessManager(QObject* parent = 0); + virtual ~BaseNetworkAccessManager(); + + public slots: + // Loads network settings for this instance. + // NOTE: This sets up proxy settings. + virtual void loadSettings(); + + protected slots: + // Called when some SSL-related errors are detected. + void onSslErrors(QNetworkReply* reply, const QList& error); + + protected: + // Creates custom request. + QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData); +}; + +#endif // BASENETWORKACCESSMANAGER_H diff --git a/src/network-web/downloader.cpp b/src/network-web/downloader.cpp index 90d1c6177..b1abd25b4 100755 --- a/src/network-web/downloader.cpp +++ b/src/network-web/downloader.cpp @@ -1,224 +1,217 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "network-web/downloader.h" - -#include "network-web/silentnetworkaccessmanager.h" - -#include - - -Downloader::Downloader(QObject *parent) - : QObject(parent), m_activeReply(nullptr), m_downloadManager(new SilentNetworkAccessManager(this)), - m_timer(new QTimer(this)), m_customHeaders(QHash()), m_inputData(QByteArray()), - m_targetProtected(false), m_targetUsername(QString()), m_targetPassword(QString()), - m_lastOutputData(QByteArray()), m_lastOutputError(QNetworkReply::NoError), m_lastContentType(QVariant()) { - - m_timer->setInterval(DOWNLOAD_TIMEOUT); - m_timer->setSingleShot(true); - - connect(m_timer, &QTimer::timeout, this, &Downloader::cancel); -} - -Downloader::~Downloader() { -} - -void Downloader::downloadFile(const QString &url, int timeout, bool protected_contents, const QString &username, - const QString &password) { - manipulateData(url, QNetworkAccessManager::GetOperation, QByteArray(), timeout, - protected_contents, username, password); -} - -void Downloader::uploadFile(const QString &url, const QByteArray &data, int timeout, - bool protected_contents, const QString &username, const QString &password) { - manipulateData(url, QNetworkAccessManager::PostOperation, data, timeout, protected_contents, username, password); -} - -void Downloader::manipulateData(const QString &url, QNetworkAccessManager::Operation operation, const QByteArray &data, - int timeout, bool protected_contents, const QString &username, const QString &password) { - QNetworkRequest request; - QString non_const_url = url; - - QHashIterator i(m_customHeaders); - - while (i.hasNext()) { - i.next(); - request.setRawHeader(i.key(), i.value()); - } - - m_inputData = data; - - // Set url for this request and fire it up. - m_timer->setInterval(timeout); - - if (non_const_url.startsWith(URI_SCHEME_FEED)) { - qDebug("Replacing URI schemes for '%s'.", qPrintable(non_const_url)); - request.setUrl(non_const_url.replace(QRegExp(QString('^') + URI_SCHEME_FEED), QString(URI_SCHEME_HTTP))); - } - else { - request.setUrl(non_const_url); - } - - m_targetProtected = protected_contents; - m_targetUsername = username; - m_targetPassword = password; - - if (operation == QNetworkAccessManager::PostOperation) { - runPostRequest(request, m_inputData); - } - else if (operation == QNetworkAccessManager::GetOperation) { - runGetRequest(request); - } - else if (operation == QNetworkAccessManager::PutOperation) { - runPutRequest(request, m_inputData); - } - else if (operation == QNetworkAccessManager::DeleteOperation) { - runDeleteRequest(request); - } -} - -void Downloader::finished() { - QNetworkReply *reply = qobject_cast(sender()); - QNetworkAccessManager::Operation reply_operation = reply->operation(); - - m_timer->stop(); - - // In this phase, some part of downloading process is completed. - const QUrl redirection_url = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); - - if (redirection_url.isValid()) { - // Communication indicates that HTTP redirection is needed. - // Setup redirection URL and download again. - QNetworkRequest request = reply->request(); - - if (redirection_url.host().isEmpty()) { - request.setUrl(QUrl(reply->request().url().scheme() + QSL("://") + reply->request().url().host() + redirection_url.toString())); - } - else { - request.setUrl(redirection_url); - } - - m_activeReply->deleteLater(); - m_activeReply = nullptr; - - if (reply_operation == QNetworkAccessManager::GetOperation) { - runGetRequest(request); - } - else if (reply_operation == QNetworkAccessManager::PostOperation) { - runPostRequest(request, m_inputData); - } - else if (reply_operation == QNetworkAccessManager::PutOperation) { - runPutRequest(request, m_inputData); - } - else if (reply_operation == QNetworkAccessManager::DeleteOperation) { - runDeleteRequest(request); - } - } - else { - // No redirection is indicated. Final file is obtained in our "reply" object. - // Read the data into output buffer. - m_lastOutputData = reply->readAll(); - m_lastContentType = reply->header(QNetworkRequest::ContentTypeHeader); - m_lastOutputError = reply->error(); - - m_activeReply->deleteLater(); - m_activeReply = nullptr; - - emit completed(m_lastOutputError, m_lastOutputData); - } -} - -void Downloader::progressInternal(qint64 bytes_received, qint64 bytes_total) { - if (m_timer->interval() > 0) { - m_timer->start(); - } - - emit progress(bytes_received, bytes_total); -} - -void Downloader::runDeleteRequest(const QNetworkRequest &request) { - m_timer->start(); - m_activeReply = m_downloadManager->deleteResource(request); - - m_activeReply->setProperty("protected", m_targetProtected); - m_activeReply->setProperty("username", m_targetUsername); - m_activeReply->setProperty("password", m_targetPassword); - - connect(m_activeReply, &QNetworkReply::downloadProgress, this, &Downloader::progressInternal); - connect(m_activeReply, &QNetworkReply::finished, this, &Downloader::finished); -} - -void Downloader::runPutRequest(const QNetworkRequest &request, const QByteArray &data) { - m_timer->start(); - m_activeReply = m_downloadManager->put(request, data); - - m_activeReply->setProperty("protected", m_targetProtected); - m_activeReply->setProperty("username", m_targetUsername); - m_activeReply->setProperty("password", m_targetPassword); - - connect(m_activeReply, &QNetworkReply::downloadProgress, this, &Downloader::progressInternal); - connect(m_activeReply, &QNetworkReply::finished, this, &Downloader::finished); -} - -void Downloader::runPostRequest(const QNetworkRequest &request, const QByteArray &data) { - m_timer->start(); - m_activeReply = m_downloadManager->post(request, data); - - m_activeReply->setProperty("protected", m_targetProtected); - m_activeReply->setProperty("username", m_targetUsername); - m_activeReply->setProperty("password", m_targetPassword); - - connect(m_activeReply, &QNetworkReply::downloadProgress, this, &Downloader::progressInternal); - connect(m_activeReply, &QNetworkReply::finished, this, &Downloader::finished); -} - -void Downloader::runGetRequest(const QNetworkRequest &request) { - m_timer->start(); - m_activeReply = m_downloadManager->get(request); - - m_activeReply->setProperty("protected", m_targetProtected); - m_activeReply->setProperty("username", m_targetUsername); - m_activeReply->setProperty("password", m_targetPassword); - - connect(m_activeReply, &QNetworkReply::downloadProgress, this, &Downloader::progressInternal); - connect(m_activeReply, &QNetworkReply::finished, this, &Downloader::finished); -} - -QVariant Downloader::lastContentType() const { - return m_lastContentType; -} - -void Downloader::cancel() { - if (m_activeReply != nullptr) { - // Download action timed-out, too slow connection or target is not reachable. - m_activeReply->abort(); - } -} - -void Downloader::appendRawHeader(const QByteArray &name, const QByteArray &value) { - if (!value.isEmpty()) { - m_customHeaders.insert(name, value); - } -} - -QNetworkReply::NetworkError Downloader::lastOutputError() const { - return m_lastOutputError; -} - -QByteArray Downloader::lastOutputData() const { - return m_lastOutputData; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "network-web/downloader.h" + +#include "network-web/silentnetworkaccessmanager.h" + +#include + + +Downloader::Downloader(QObject* parent) + : QObject(parent), m_activeReply(nullptr), m_downloadManager(new SilentNetworkAccessManager(this)), + m_timer(new QTimer(this)), m_customHeaders(QHash()), m_inputData(QByteArray()), + m_targetProtected(false), m_targetUsername(QString()), m_targetPassword(QString()), + m_lastOutputData(QByteArray()), m_lastOutputError(QNetworkReply::NoError), m_lastContentType(QVariant()) { + m_timer->setInterval(DOWNLOAD_TIMEOUT); + m_timer->setSingleShot(true); + connect(m_timer, &QTimer::timeout, this, &Downloader::cancel); +} + +Downloader::~Downloader() { +} + +void Downloader::downloadFile(const QString& url, int timeout, bool protected_contents, const QString& username, + const QString& password) { + manipulateData(url, QNetworkAccessManager::GetOperation, QByteArray(), timeout, + protected_contents, username, password); +} + +void Downloader::uploadFile(const QString& url, const QByteArray& data, int timeout, + bool protected_contents, const QString& username, const QString& password) { + manipulateData(url, QNetworkAccessManager::PostOperation, data, timeout, protected_contents, username, password); +} + +void Downloader::manipulateData(const QString& url, QNetworkAccessManager::Operation operation, const QByteArray& data, + int timeout, bool protected_contents, const QString& username, const QString& password) { + QNetworkRequest request; + QString non_const_url = url; + QHashIterator i(m_customHeaders); + + while (i.hasNext()) { + i.next(); + request.setRawHeader(i.key(), i.value()); + } + + m_inputData = data; + // Set url for this request and fire it up. + m_timer->setInterval(timeout); + + if (non_const_url.startsWith(URI_SCHEME_FEED)) { + qDebug("Replacing URI schemes for '%s'.", qPrintable(non_const_url)); + request.setUrl(non_const_url.replace(QRegExp(QString('^') + URI_SCHEME_FEED), QString(URI_SCHEME_HTTP))); + } + + else { + request.setUrl(non_const_url); + } + + m_targetProtected = protected_contents; + m_targetUsername = username; + m_targetPassword = password; + + if (operation == QNetworkAccessManager::PostOperation) { + runPostRequest(request, m_inputData); + } + + else if (operation == QNetworkAccessManager::GetOperation) { + runGetRequest(request); + } + + else if (operation == QNetworkAccessManager::PutOperation) { + runPutRequest(request, m_inputData); + } + + else if (operation == QNetworkAccessManager::DeleteOperation) { + runDeleteRequest(request); + } +} + +void Downloader::finished() { + QNetworkReply* reply = qobject_cast(sender()); + QNetworkAccessManager::Operation reply_operation = reply->operation(); + m_timer->stop(); + // In this phase, some part of downloading process is completed. + const QUrl redirection_url = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + + if (redirection_url.isValid()) { + // Communication indicates that HTTP redirection is needed. + // Setup redirection URL and download again. + QNetworkRequest request = reply->request(); + + if (redirection_url.host().isEmpty()) { + request.setUrl(QUrl(reply->request().url().scheme() + QSL("://") + reply->request().url().host() + redirection_url.toString())); + } + + else { + request.setUrl(redirection_url); + } + + m_activeReply->deleteLater(); + m_activeReply = nullptr; + + if (reply_operation == QNetworkAccessManager::GetOperation) { + runGetRequest(request); + } + + else if (reply_operation == QNetworkAccessManager::PostOperation) { + runPostRequest(request, m_inputData); + } + + else if (reply_operation == QNetworkAccessManager::PutOperation) { + runPutRequest(request, m_inputData); + } + + else if (reply_operation == QNetworkAccessManager::DeleteOperation) { + runDeleteRequest(request); + } + } + + else { + // No redirection is indicated. Final file is obtained in our "reply" object. + // Read the data into output buffer. + m_lastOutputData = reply->readAll(); + m_lastContentType = reply->header(QNetworkRequest::ContentTypeHeader); + m_lastOutputError = reply->error(); + m_activeReply->deleteLater(); + m_activeReply = nullptr; + emit completed(m_lastOutputError, m_lastOutputData); + } +} + +void Downloader::progressInternal(qint64 bytes_received, qint64 bytes_total) { + if (m_timer->interval() > 0) { + m_timer->start(); + } + + emit progress(bytes_received, bytes_total); +} + +void Downloader::runDeleteRequest(const QNetworkRequest& request) { + m_timer->start(); + m_activeReply = m_downloadManager->deleteResource(request); + m_activeReply->setProperty("protected", m_targetProtected); + m_activeReply->setProperty("username", m_targetUsername); + m_activeReply->setProperty("password", m_targetPassword); + connect(m_activeReply, &QNetworkReply::downloadProgress, this, &Downloader::progressInternal); + connect(m_activeReply, &QNetworkReply::finished, this, &Downloader::finished); +} + +void Downloader::runPutRequest(const QNetworkRequest& request, const QByteArray& data) { + m_timer->start(); + m_activeReply = m_downloadManager->put(request, data); + m_activeReply->setProperty("protected", m_targetProtected); + m_activeReply->setProperty("username", m_targetUsername); + m_activeReply->setProperty("password", m_targetPassword); + connect(m_activeReply, &QNetworkReply::downloadProgress, this, &Downloader::progressInternal); + connect(m_activeReply, &QNetworkReply::finished, this, &Downloader::finished); +} + +void Downloader::runPostRequest(const QNetworkRequest& request, const QByteArray& data) { + m_timer->start(); + m_activeReply = m_downloadManager->post(request, data); + m_activeReply->setProperty("protected", m_targetProtected); + m_activeReply->setProperty("username", m_targetUsername); + m_activeReply->setProperty("password", m_targetPassword); + connect(m_activeReply, &QNetworkReply::downloadProgress, this, &Downloader::progressInternal); + connect(m_activeReply, &QNetworkReply::finished, this, &Downloader::finished); +} + +void Downloader::runGetRequest(const QNetworkRequest& request) { + m_timer->start(); + m_activeReply = m_downloadManager->get(request); + m_activeReply->setProperty("protected", m_targetProtected); + m_activeReply->setProperty("username", m_targetUsername); + m_activeReply->setProperty("password", m_targetPassword); + connect(m_activeReply, &QNetworkReply::downloadProgress, this, &Downloader::progressInternal); + connect(m_activeReply, &QNetworkReply::finished, this, &Downloader::finished); +} + +QVariant Downloader::lastContentType() const { + return m_lastContentType; +} + +void Downloader::cancel() { + if (m_activeReply != nullptr) { + // Download action timed-out, too slow connection or target is not reachable. + m_activeReply->abort(); + } +} + +void Downloader::appendRawHeader(const QByteArray& name, const QByteArray& value) { + if (!value.isEmpty()) { + m_customHeaders.insert(name, value); + } +} + +QNetworkReply::NetworkError Downloader::lastOutputError() const { + return m_lastOutputError; +} + +QByteArray Downloader::lastOutputData() const { + return m_lastOutputData; +} diff --git a/src/network-web/downloader.h b/src/network-web/downloader.h index 273e47e78..f3d075830 100755 --- a/src/network-web/downloader.h +++ b/src/network-web/downloader.h @@ -1,101 +1,101 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef DOWNLOADER_H -#define DOWNLOADER_H - -#include - -#include "definitions/definitions.h" - -#include -#include - - -class SilentNetworkAccessManager; -class QTimer; - -class Downloader : public QObject { - Q_OBJECT - - public: - // Constructors and destructors. - explicit Downloader(QObject *parent = 0); - virtual ~Downloader(); - - // Access to last received full output data/error/content-type. - QByteArray lastOutputData() const; - QNetworkReply::NetworkError lastOutputError() const; - QVariant lastContentType() const; - - public slots: - void cancel(); - - void appendRawHeader(const QByteArray &name, const QByteArray &value); - - // Performs asynchronous download of given file. Redirections are handled. - void downloadFile(const QString &url, int timeout = DOWNLOAD_TIMEOUT, bool protected_contents = false, - const QString &username = QString(), const QString &password = QString()); - - void uploadFile(const QString &url, const QByteArray &data, int timeout = DOWNLOAD_TIMEOUT, - bool protected_contents = false, const QString &username = QString(), - const QString &password = QString()); - - // Performs asynchronous upload of given data as HTTP POST. - // User needs to setup "Content-Encoding" header which - // matches encoding of the data. - void manipulateData(const QString &url, QNetworkAccessManager::Operation operation, - const QByteArray &data = QByteArray(), - int timeout = DOWNLOAD_TIMEOUT, bool protected_contents = false, - const QString &username = QString(), const QString &password = QString()); - - signals: - // Emitted when new progress is known. - void progress(qint64 bytes_received, qint64 bytes_total); - void completed(QNetworkReply::NetworkError status, QByteArray contents = QByteArray()); - - private slots: - // Called when current reply is processed. - void finished(); - - // Called when progress of downloaded file changes. - void progressInternal(qint64 bytes_received, qint64 bytes_total); - - private: - void runDeleteRequest(const QNetworkRequest &request); - void runPutRequest(const QNetworkRequest &request, const QByteArray &data); - void runPostRequest(const QNetworkRequest &request, const QByteArray &data); - void runGetRequest(const QNetworkRequest &request); - - private: - QNetworkReply *m_activeReply; - QScopedPointer m_downloadManager; - QTimer *m_timer; - QHash m_customHeaders; - QByteArray m_inputData; - - bool m_targetProtected; - QString m_targetUsername; - QString m_targetPassword; - - // Response data. - QByteArray m_lastOutputData; - QNetworkReply::NetworkError m_lastOutputError; - QVariant m_lastContentType; -}; - -#endif // DOWNLOADER_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef DOWNLOADER_H +#define DOWNLOADER_H + +#include + +#include "definitions/definitions.h" + +#include +#include + + +class SilentNetworkAccessManager; +class QTimer; + +class Downloader : public QObject { + Q_OBJECT + + public: + // Constructors and destructors. + explicit Downloader(QObject* parent = 0); + virtual ~Downloader(); + + // Access to last received full output data/error/content-type. + QByteArray lastOutputData() const; + QNetworkReply::NetworkError lastOutputError() const; + QVariant lastContentType() const; + + public slots: + void cancel(); + + void appendRawHeader(const QByteArray& name, const QByteArray& value); + + // Performs asynchronous download of given file. Redirections are handled. + void downloadFile(const QString& url, int timeout = DOWNLOAD_TIMEOUT, bool protected_contents = false, + const QString& username = QString(), const QString& password = QString()); + + void uploadFile(const QString& url, const QByteArray& data, int timeout = DOWNLOAD_TIMEOUT, + bool protected_contents = false, const QString& username = QString(), + const QString& password = QString()); + + // Performs asynchronous upload of given data as HTTP POST. + // User needs to setup "Content-Encoding" header which + // matches encoding of the data. + void manipulateData(const QString& url, QNetworkAccessManager::Operation operation, + const QByteArray& data = QByteArray(), + int timeout = DOWNLOAD_TIMEOUT, bool protected_contents = false, + const QString& username = QString(), const QString& password = QString()); + + signals: + // Emitted when new progress is known. + void progress(qint64 bytes_received, qint64 bytes_total); + void completed(QNetworkReply::NetworkError status, QByteArray contents = QByteArray()); + + private slots: + // Called when current reply is processed. + void finished(); + + // Called when progress of downloaded file changes. + void progressInternal(qint64 bytes_received, qint64 bytes_total); + + private: + void runDeleteRequest(const QNetworkRequest& request); + void runPutRequest(const QNetworkRequest& request, const QByteArray& data); + void runPostRequest(const QNetworkRequest& request, const QByteArray& data); + void runGetRequest(const QNetworkRequest& request); + + private: + QNetworkReply* m_activeReply; + QScopedPointer m_downloadManager; + QTimer* m_timer; + QHash m_customHeaders; + QByteArray m_inputData; + + bool m_targetProtected; + QString m_targetUsername; + QString m_targetPassword; + + // Response data. + QByteArray m_lastOutputData; + QNetworkReply::NetworkError m_lastOutputError; + QVariant m_lastContentType; +}; + +#endif // DOWNLOADER_H diff --git a/src/network-web/downloadmanager.cpp b/src/network-web/downloadmanager.cpp index 824bce131..98dae51ce 100755 --- a/src/network-web/downloadmanager.cpp +++ b/src/network-web/downloadmanager.cpp @@ -40,782 +40,762 @@ #include -DownloadItem::DownloadItem(QNetworkReply *reply, QWidget *parent) : QWidget(parent), - m_ui(new Ui::DownloadItem), m_reply(reply), - m_bytesReceived(0), m_requestFileName(false), m_startedSaving(false), m_finishedDownloading(false), - m_gettingFileName(false), m_canceledFileSelect(false) { - 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_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); - init(); +DownloadItem::DownloadItem(QNetworkReply* reply, QWidget* parent) : QWidget(parent), + m_ui(new Ui::DownloadItem), m_reply(reply), + m_bytesReceived(0), m_requestFileName(false), m_startedSaving(false), m_finishedDownloading(false), + m_gettingFileName(false), m_canceledFileSelect(false) { + 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_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); + init(); } DownloadItem::~DownloadItem() { - delete m_ui; + delete m_ui; } void DownloadItem::init() { - if (m_reply == nullptr) { - return; - } + if (m_reply == nullptr) { + return; + } - m_startedSaving = false; - m_finishedDownloading = false; - m_ui->m_btnOpenFile->setEnabled(false); - m_ui->m_btnOpenFolder->setEnabled(false); - m_url = m_reply->url(); - m_reply->setParent(this); + m_startedSaving = false; + m_finishedDownloading = false; + m_ui->m_btnOpenFile->setEnabled(false); + 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, 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. + m_ui->m_lblInfoDownload->clear(); + m_ui->m_progressDownload->setValue(0); + getFileName(); + // Start timer for the download estimation. + m_downloadTime.start(); - 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. - m_ui->m_lblInfoDownload->clear(); - m_ui->m_progressDownload->setValue(0); - getFileName(); - - // Start timer for the download estimation. - m_downloadTime.start(); - - if (m_reply->error() != QNetworkReply::NoError) { - error(m_reply->error()); - finished(); - } + if (m_reply->error() != QNetworkReply::NoError) { + error(m_reply->error()); + finished(); + } } void DownloadItem::getFileName() { - if (m_gettingFileName) { - return; - } + if (m_gettingFileName) { + return; + } - const QString download_directory = qApp->downloadManager()->downloadDirectory(); - QString chosen_filename = saveFileName(download_directory); - const QString filename_for_prompt = qApp->settings()->value(GROUP(Downloads), SETTING(Downloads::TargetExplicitDirectory)).toString() + - QDir::separator() + - QFileInfo(chosen_filename).fileName(); + const QString download_directory = qApp->downloadManager()->downloadDirectory(); + QString chosen_filename = saveFileName(download_directory); + const QString filename_for_prompt = qApp->settings()->value(GROUP(Downloads), SETTING(Downloads::TargetExplicitDirectory)).toString() + + QDir::separator() + + QFileInfo(chosen_filename).fileName(); - if (m_requestFileName) { - // User must provide the path where he wants to save downloaded file in. - m_gettingFileName = true; - chosen_filename = QFileDialog::getSaveFileName(this, tr("Select destination for downloaded file"), filename_for_prompt); - m_gettingFileName = false; + if (m_requestFileName) { + // User must provide the path where he wants to save downloaded file in. + m_gettingFileName = true; + chosen_filename = QFileDialog::getSaveFileName(this, tr("Select destination for downloaded file"), filename_for_prompt); + m_gettingFileName = false; - if (chosen_filename.isEmpty()) { - stop(); + if (chosen_filename.isEmpty()) { + stop(); + m_ui->m_progressDownload->setVisible(false); + m_ui->m_lblLocalFilename->setText(tr("Selection of local file cancelled.")); + m_canceledFileSelect = true; + return; + } - m_ui->m_progressDownload->setVisible(false); - m_ui->m_lblLocalFilename->setText(tr("Selection of local file cancelled.")); - m_canceledFileSelect = true; - return; - } + const QFileInfo file_info = QFileInfo(chosen_filename); + qApp->settings()->setValue(GROUP(Downloads), Downloads::TargetExplicitDirectory, + QDir::toNativeSeparators(QFileInfo(chosen_filename).absolutePath())); + qApp->downloadManager()->setDownloadDirectory(file_info.absoluteDir().absolutePath()); + } - const QFileInfo file_info = QFileInfo(chosen_filename); + m_output.setFileName(chosen_filename); + // Check file path for saving. + const QDir save_dir = QFileInfo(m_output.fileName()).dir(); - qApp->settings()->setValue(GROUP(Downloads), Downloads::TargetExplicitDirectory, - QDir::toNativeSeparators(QFileInfo(chosen_filename).absolutePath())); - qApp->downloadManager()->setDownloadDirectory(file_info.absoluteDir().absolutePath()); - } + if (!save_dir.exists() && !save_dir.mkpath(save_dir.absolutePath())) { + stop(); + m_ui->m_progressDownload->setVisible(false); + m_ui->m_lblInfoDownload->setText(tr("Download directory couldn't be created")); + return; + } - m_output.setFileName(chosen_filename); + updateInfoAndUrlLabel(); - // Check file path for saving. - const QDir save_dir = QFileInfo(m_output.fileName()).dir(); - - if (!save_dir.exists() && !save_dir.mkpath(save_dir.absolutePath())) { - stop(); - - m_ui->m_progressDownload->setVisible(false); - m_ui->m_lblInfoDownload->setText(tr("Download directory couldn't be created")); - return; - } - - updateInfoAndUrlLabel(); - - if (m_requestFileName) { - downloadReadyRead(); - } + if (m_requestFileName) { + downloadReadyRead(); + } } -QString DownloadItem::saveFileName(const QString &directory) const { - QString path; +QString DownloadItem::saveFileName(const QString& directory) const { + QString path; - if (m_reply->hasRawHeader("Content-Disposition")) { - const QString value = QLatin1String(m_reply->rawHeader("Content-Disposition")); - const int pos = value.indexOf(QL1S("filename=")); + if (m_reply->hasRawHeader("Content-Disposition")) { + const QString value = QLatin1String(m_reply->rawHeader("Content-Disposition")); + const int pos = value.indexOf(QL1S("filename=")); - if (pos != -1) { - QString name = value.mid(pos + 9); + if (pos != -1) { + QString name = value.mid(pos + 9); - if (name.startsWith(QL1C('"')) && name.endsWith(QL1C('"'))) { - name = name.mid(1, name.size() - 2); - } + if (name.startsWith(QL1C('"')) && name.endsWith(QL1C('"'))) { + name = name.mid(1, name.size() - 2); + } - path = name; - } - } + path = name; + } + } - if (path.isEmpty()) { - path = m_url.path(); - } + if (path.isEmpty()) { + path = m_url.path(); + } - const QFileInfo info(path); - QString base_name = info.completeBaseName(); - QString end_name = info.suffix(); + const QFileInfo info(path); + QString base_name = info.completeBaseName(); + QString end_name = info.suffix(); - if (base_name.isEmpty()) { - base_name = QSL("unnamed_download"); - } + if (base_name.isEmpty()) { + base_name = QSL("unnamed_download"); + } - if (!end_name.isEmpty()) { - end_name = QL1C('.') + end_name; - } + if (!end_name.isEmpty()) { + end_name = QL1C('.') + end_name; + } - QString name = directory + base_name + end_name; + QString name = directory + base_name + end_name; - if (!m_requestFileName && QFile::exists(name)) { - int i = 1; + if (!m_requestFileName && QFile::exists(name)) { + int i = 1; - do { - name = directory + base_name + QL1C('-') + QString::number(i++) + end_name; - } while (QFile::exists(name)); - } + do { + name = directory + base_name + QL1C('-') + QString::number(i++) + end_name; + } + while (QFile::exists(name)); + } - return name; + return name; } void DownloadItem::stop() { - setUpdatesEnabled(false); - m_ui->m_btnStopDownload->setEnabled(false); - m_ui->m_btnStopDownload->hide(); - m_ui->m_btnTryAgain->setEnabled(true); - m_ui->m_btnTryAgain->show(); - setUpdatesEnabled(true); - - m_reply->abort(); - emit downloadFinished(); + setUpdatesEnabled(false); + m_ui->m_btnStopDownload->setEnabled(false); + m_ui->m_btnStopDownload->hide(); + m_ui->m_btnTryAgain->setEnabled(true); + m_ui->m_btnTryAgain->show(); + setUpdatesEnabled(true); + m_reply->abort(); + emit downloadFinished(); } void DownloadItem::openFile() { - if (!QDesktopServices::openUrl(QUrl::fromLocalFile(m_output.fileName()))) { - qApp->showGuiMessage(tr("Cannot open file"), tr("Cannot open output file. Open it manually."), - QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); - } + if (!QDesktopServices::openUrl(QUrl::fromLocalFile(m_output.fileName()))) { + qApp->showGuiMessage(tr("Cannot open file"), tr("Cannot open output file. Open it manually."), + QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); + } } void DownloadItem::openFolder() { - if (m_output.exists()) { - if (!SystemFactory::openFolderFile(m_output.fileName())) { - MessageBox::show(this, - QMessageBox::Warning, - tr("Cannot open directory"), - tr("Cannot open output directory. Open it manually."), - QString(), - m_output.fileName()); - } - } + if (m_output.exists()) { + if (!SystemFactory::openFolderFile(m_output.fileName())) { + MessageBox::show(this, + QMessageBox::Warning, + tr("Cannot open directory"), + tr("Cannot open output directory. Open it manually."), + QString(), + m_output.fileName()); + } + } } void DownloadItem::tryAgain() { - if (!m_ui->m_btnTryAgain->isEnabled()) { - return; - } + if (!m_ui->m_btnTryAgain->isEnabled()) { + return; + } - m_ui->m_btnTryAgain->setEnabled(false); - m_ui->m_btnTryAgain->setVisible(false); - m_ui->m_btnStopDownload->setEnabled(true); - m_ui->m_btnStopDownload->setVisible(true); - m_ui->m_progressDownload->setVisible(true); + m_ui->m_btnTryAgain->setEnabled(false); + m_ui->m_btnTryAgain->setVisible(false); + m_ui->m_btnStopDownload->setEnabled(true); + m_ui->m_btnStopDownload->setVisible(true); + m_ui->m_progressDownload->setVisible(true); + QNetworkReply* new_download = qApp->downloadManager()->networkManager()->get(QNetworkRequest(m_url)); - QNetworkReply *new_download = qApp->downloadManager()->networkManager()->get(QNetworkRequest(m_url)); + if (m_reply) { + m_reply->deleteLater(); + } - if (m_reply) { - m_reply->deleteLater(); - } + if (m_output.exists()) { + m_output.remove(); + } - if (m_output.exists()) { - m_output.remove(); - } - - m_reply = new_download; - - init(); - emit statusChanged(); + m_reply = new_download; + init(); + emit statusChanged(); } void DownloadItem::downloadReadyRead() { - if (m_requestFileName && m_output.fileName().isEmpty()) { - return; - } + if (m_requestFileName && m_output.fileName().isEmpty()) { + return; + } - if (!m_output.isOpen()) { - if (!m_requestFileName) { - getFileName(); - } + if (!m_output.isOpen()) { + if (!m_requestFileName) { + getFileName(); + } - if (!m_output.open(QIODevice::WriteOnly)) { - m_ui->m_lblInfoDownload->setText(tr("Error opening output file: %1").arg(m_output.errorString())); - stop(); + if (!m_output.open(QIODevice::WriteOnly)) { + m_ui->m_lblInfoDownload->setText(tr("Error opening output file: %1").arg(m_output.errorString())); + stop(); + emit statusChanged(); + return; + } - emit statusChanged(); - return; - } + emit statusChanged(); + } - emit statusChanged(); - } + if (-1 == m_output.write(m_reply->readAll())) { + m_ui->m_lblInfoDownload->setText(tr("Error when saving file: %1").arg(m_output.errorString())); + m_ui->m_btnStopDownload->click(); + } - if (-1 == m_output.write(m_reply->readAll())) { - m_ui->m_lblInfoDownload->setText(tr("Error when saving file: %1").arg(m_output.errorString())); - m_ui->m_btnStopDownload->click(); - } - else { - m_startedSaving = true; + else { + m_startedSaving = true; - if (m_finishedDownloading) { - finished(); - } - } + if (m_finishedDownloading) { + finished(); + } + } } void DownloadItem::error(QNetworkReply::NetworkError code) { - Q_UNUSED(code) - - 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(); + Q_UNUSED(code) + 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(); } void DownloadItem::metaDataChanged() { - QVariant locationHeader = m_reply->header(QNetworkRequest::LocationHeader); + QVariant locationHeader = m_reply->header(QNetworkRequest::LocationHeader); - if (locationHeader.isValid()) { - m_url = locationHeader.toUrl(); - m_reply->deleteLater(); - m_reply = qApp->downloadManager()->networkManager()->get(QNetworkRequest(m_url)); - - init(); - return; - } + if (locationHeader.isValid()) { + m_url = locationHeader.toUrl(); + m_reply->deleteLater(); + m_reply = qApp->downloadManager()->networkManager()->get(QNetworkRequest(m_url)); + init(); + return; + } } void DownloadItem::downloadProgress(qint64 bytes_received, qint64 bytes_total) { - QTime now = QTime::currentTime(); + QTime now = QTime::currentTime(); - if (m_lastProgressTime.isValid() && m_lastProgressTime.msecsTo(now) < 25) { - return; - } + if (m_lastProgressTime.isValid() && m_lastProgressTime.msecsTo(now) < 25) { + return; + } - m_lastProgressTime = now; - m_bytesReceived = bytes_received; + m_lastProgressTime = now; + m_bytesReceived = bytes_received; + qint64 currentValue = 0; + qint64 totalValue = 0; - qint64 currentValue = 0; - qint64 totalValue = 0; + if (bytes_total > 0) { + currentValue = bytes_received * 100 / bytes_total; + totalValue = 100; + } - if (bytes_total > 0) { - currentValue = bytes_received * 100 / bytes_total; - totalValue = 100; - } - - m_ui->m_progressDownload->setValue(currentValue); - m_ui->m_progressDownload->setMaximum(totalValue); - - emit progress(currentValue, totalValue); - updateDownloadInfoLabel(); + m_ui->m_progressDownload->setValue(currentValue); + m_ui->m_progressDownload->setMaximum(totalValue); + emit progress(currentValue, totalValue); + updateDownloadInfoLabel(); } qint64 DownloadItem::bytesTotal() const { - return m_reply->header(QNetworkRequest::ContentLengthHeader).toULongLong(); + return m_reply->header(QNetworkRequest::ContentLengthHeader).toULongLong(); } qint64 DownloadItem::bytesReceived() const { - return m_bytesReceived; + return m_bytesReceived; } double DownloadItem::remainingTime() const { - if (!downloading()) { - return -1.0; - } + if (!downloading()) { + return -1.0; + } - double time_remaining = ((double)(bytesTotal() - bytesReceived())) / currentSpeed(); + double time_remaining = ((double)(bytesTotal() - bytesReceived())) / currentSpeed(); - // When downloading the ETA should never be 0. - if ((int) time_remaining == 0) { - time_remaining = 1.0; - } + // When downloading the ETA should never be 0. + if ((int) time_remaining == 0) { + time_remaining = 1.0; + } - return time_remaining; + return time_remaining; } double DownloadItem::currentSpeed() const { - if (!downloading()) { - return -1.0; - } - else { - return m_bytesReceived * 1000.0 / m_downloadTime.elapsed(); - } + if (!downloading()) { + return -1.0; + } + + else { + return m_bytesReceived * 1000.0 / m_downloadTime.elapsed(); + } } void DownloadItem::updateDownloadInfoLabel() { - if (m_reply->error() != QNetworkReply::NoError) { - return; - } + if (m_reply->error() != QNetworkReply::NoError) { + return; + } - const qint64 bytes_total = m_reply->header(QNetworkRequest::ContentLengthHeader).toULongLong(); - bool running = !downloadedSuccessfully(); - double speed = currentSpeed(); - double time_remaining = remainingTime(); + const qint64 bytes_total = m_reply->header(QNetworkRequest::ContentLengthHeader).toULongLong(); + bool running = !downloadedSuccessfully(); + double speed = currentSpeed(); + double time_remaining = remainingTime(); + QString info; - QString info; + if (running) { + QString remaining; - if (running) { - QString remaining; + if (bytes_total != 0) { + remaining = DownloadManager::timeString(time_remaining); + } - if (bytes_total != 0) { - remaining = DownloadManager::timeString(time_remaining); - } + info = QString(tr("%1 of %2 (%3 per second) - %4")).arg(DownloadManager::dataString(m_bytesReceived), + bytes_total == 0 ? QSL("?") : DownloadManager::dataString(bytes_total), + DownloadManager::dataString((int)speed), + remaining); + } - info = QString(tr("%1 of %2 (%3 per second) - %4")).arg(DownloadManager::dataString(m_bytesReceived), - bytes_total == 0 ? QSL("?") : DownloadManager::dataString(bytes_total), - DownloadManager::dataString((int)speed), - remaining); - } - else { - if (m_bytesReceived == bytes_total) { - info = DownloadManager::dataString(m_output.size()); - } - else { - info = tr("%1 of %2 - download completed").arg(DownloadManager::dataString(m_bytesReceived), - DownloadManager::dataString(m_bytesReceived)); - } - } + else { + if (m_bytesReceived == bytes_total) { + info = DownloadManager::dataString(m_output.size()); + } - m_ui->m_lblInfoDownload->setText(info); + else { + info = tr("%1 of %2 - download completed").arg(DownloadManager::dataString(m_bytesReceived), + DownloadManager::dataString(m_bytesReceived)); + } + } + + m_ui->m_lblInfoDownload->setText(info); } bool DownloadItem::downloading() const { - return (m_ui->m_progressDownload->isVisible()); + return (m_ui->m_progressDownload->isVisible()); } bool DownloadItem::downloadedSuccessfully() const { - return (m_ui->m_btnStopDownload->isHidden() && m_ui->m_btnTryAgain->isHidden()); + return (m_ui->m_btnStopDownload->isHidden() && m_ui->m_btnTryAgain->isHidden()); } void DownloadItem::finished() { - m_finishedDownloading = true; + m_finishedDownloading = true; - if (!m_startedSaving) { - return; - } + if (!m_startedSaving) { + return; + } - m_ui->m_progressDownload->hide(); - m_ui->m_btnStopDownload->setEnabled(false); - m_ui->m_btnStopDownload->hide(); - m_ui->m_btnOpenFile->setEnabled(true); - m_ui->m_btnOpenFolder->setEnabled(true); - m_output.close(); - updateDownloadInfoLabel(); + m_ui->m_progressDownload->hide(); + m_ui->m_btnStopDownload->setEnabled(false); + m_ui->m_btnStopDownload->hide(); + m_ui->m_btnOpenFile->setEnabled(true); + m_ui->m_btnOpenFolder->setEnabled(true); + m_output.close(); + updateDownloadInfoLabel(); + emit statusChanged(); + emit downloadFinished(); - emit statusChanged(); - emit downloadFinished(); - - if (downloadedSuccessfully()) { - qApp->showGuiMessage(tr("Download finished"), - tr("File '%1' is downloaded.\nClick here to open parent directory.").arg(QDir::toNativeSeparators(m_output.fileName())), - QSystemTrayIcon::Information, 0, false, this, SLOT(openFolder())); - } + if (downloadedSuccessfully()) { + qApp->showGuiMessage(tr("Download finished"), + tr("File '%1' is downloaded.\nClick here to open parent directory.").arg(QDir::toNativeSeparators(m_output.fileName())), + QSystemTrayIcon::Information, 0, false, this, SLOT(openFolder())); + } } void DownloadItem::updateInfoAndUrlLabel() { - m_ui->m_lblRemoteFilename->setText(tr("URL: %1").arg(m_url.toString())); - m_ui->m_lblLocalFilename->setText(tr("Local file: %1").arg(QDir::toNativeSeparators(m_output.fileName()))); + m_ui->m_lblRemoteFilename->setText(tr("URL: %1").arg(m_url.toString())); + m_ui->m_lblLocalFilename->setText(tr("Local file: %1").arg(QDir::toNativeSeparators(m_output.fileName()))); } -DownloadManager::DownloadManager(QWidget *parent) : TabContent(parent), m_ui(new Ui::DownloadManager), - m_autoSaver(new AutoSaver(this)), m_model(new DownloadModel(this)), - m_networkManager(SilentNetworkAccessManager::instance()), m_iconProvider(nullptr), m_removePolicy(Never) { - m_ui->setupUi(this); - m_ui->m_viewDownloads->setShowGrid(false); - m_ui->m_viewDownloads->verticalHeader()->hide(); - m_ui->m_viewDownloads->horizontalHeader()->hide(); - m_ui->m_viewDownloads->setAlternatingRowColors(true); - m_ui->m_viewDownloads->horizontalHeader()->setStretchLastSection(true); - m_ui->m_viewDownloads->setModel(m_model); - - setDownloadDirectory(qApp->settings()->value(GROUP(Downloads), SETTING(Downloads::TargetDirectory)).toString()); - connect(m_ui->m_btnCleanup, &QPushButton::clicked, this, &DownloadManager::cleanup); - load(); +DownloadManager::DownloadManager(QWidget* parent) : TabContent(parent), m_ui(new Ui::DownloadManager), + m_autoSaver(new AutoSaver(this)), m_model(new DownloadModel(this)), + m_networkManager(SilentNetworkAccessManager::instance()), m_iconProvider(nullptr), m_removePolicy(Never) { + m_ui->setupUi(this); + m_ui->m_viewDownloads->setShowGrid(false); + m_ui->m_viewDownloads->verticalHeader()->hide(); + m_ui->m_viewDownloads->horizontalHeader()->hide(); + m_ui->m_viewDownloads->setAlternatingRowColors(true); + m_ui->m_viewDownloads->horizontalHeader()->setStretchLastSection(true); + m_ui->m_viewDownloads->setModel(m_model); + setDownloadDirectory(qApp->settings()->value(GROUP(Downloads), SETTING(Downloads::TargetDirectory)).toString()); + connect(m_ui->m_btnCleanup, &QPushButton::clicked, this, &DownloadManager::cleanup); + load(); } DownloadManager::~DownloadManager() { - m_autoSaver->changeOccurred(); - m_autoSaver->saveIfNeccessary(); - - qDebug("Destroying DownloadManager instance."); + m_autoSaver->changeOccurred(); + m_autoSaver->saveIfNeccessary(); + qDebug("Destroying DownloadManager instance."); } int DownloadManager::activeDownloads() const { - int count = 0; + int count = 0; - foreach (const DownloadItem *download, m_downloads) { - if (download->downloading()) { - count++; - } - } + foreach (const DownloadItem* download, m_downloads) { + if (download->downloading()) { + count++; + } + } - return count; + return count; } int DownloadManager::downloadProgress() const { - qint64 bytes_total = 0; - qint64 bytes_received = 0; + qint64 bytes_total = 0; + qint64 bytes_received = 0; - foreach (const DownloadItem *download, m_downloads) { - if (download->downloading()) { - bytes_total += download->bytesTotal(); - bytes_received += download->bytesReceived(); - } - } + foreach (const DownloadItem* download, m_downloads) { + if (download->downloading()) { + bytes_total += download->bytesTotal(); + bytes_received += download->bytesReceived(); + } + } - if (bytes_total <= 0) { - return -1; - } - else { - return (bytes_received * 100.0) / bytes_total; - } + if (bytes_total <= 0) { + return -1; + } + + else { + return (bytes_received * 100.0) / bytes_total; + } } -void DownloadManager::download(const QNetworkRequest &request) { - if (!request.url().isEmpty()) { - handleUnsupportedContent(m_networkManager->get(request)); - } +void DownloadManager::download(const QNetworkRequest& request) { + if (!request.url().isEmpty()) { + handleUnsupportedContent(m_networkManager->get(request)); + } } -void DownloadManager::download(const QUrl &url) { - download(QNetworkRequest(url)); +void DownloadManager::download(const QUrl& url) { + download(QNetworkRequest(url)); } -void DownloadManager::handleUnsupportedContent(QNetworkReply *reply) { - if (reply == nullptr || reply->url().isEmpty()) { - return; - } +void DownloadManager::handleUnsupportedContent(QNetworkReply* reply) { + if (reply == nullptr || reply->url().isEmpty()) { + return; + } - const QVariant header = reply->header(QNetworkRequest::ContentLengthHeader); - bool ok; - const int size = header.toInt(&ok); + const QVariant header = reply->header(QNetworkRequest::ContentLengthHeader); + bool ok; + const int size = header.toInt(&ok); - if (ok && size == 0) { - return; - } + if (ok && size == 0) { + return; + } - DownloadItem *item = new DownloadItem(reply, this); - addItem(item); + DownloadItem* item = new DownloadItem(reply, this); + addItem(item); - if (!item->m_canceledFileSelect && qApp->settings()->value(GROUP(Downloads), - SETTING(Downloads::ShowDownloadsWhenNewDownloadStarts)).toBool()) { - qApp->mainForm()->tabWidget()->showDownloadManager(); - } + if (!item->m_canceledFileSelect && qApp->settings()->value(GROUP(Downloads), + SETTING(Downloads::ShowDownloadsWhenNewDownloadStarts)).toBool()) { + qApp->mainForm()->tabWidget()->showDownloadManager(); + } } -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(); - m_model->beginInsertRows(QModelIndex(), row, row); - m_downloads.append(item); - m_model->endInsertRows(); - m_ui->m_viewDownloads->setIndexWidget(m_model->index(row, 0), 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. - updateRow(item); +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(); + m_model->beginInsertRows(QModelIndex(), row, row); + m_downloads.append(item); + m_model->endInsertRows(); + m_ui->m_viewDownloads->setIndexWidget(m_model->index(row, 0), 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. + updateRow(item); } -QNetworkAccessManager *DownloadManager::networkManager() const { - return m_networkManager; +QNetworkAccessManager* DownloadManager::networkManager() const { + return m_networkManager; } int DownloadManager::totalDownloads() const { - return m_downloads.size(); + return m_downloads.size(); } void DownloadManager::itemFinished() { - emit downloadFinished(); + emit downloadFinished(); } void DownloadManager::updateRow() { - if (DownloadItem *item = qobject_cast(sender())) { - updateRow(item); - } + if (DownloadItem* item = qobject_cast(sender())) { + updateRow(item); + } } void DownloadManager::itemProgress() { - int progress = downloadProgress(); + int progress = downloadProgress(); - if (progress < 0) { - emit downloadFinished(); - } - else { - emit downloadProgressed(progress, tr("Downloading %n file(s)...", "", activeDownloads())); - } + if (progress < 0) { + emit downloadFinished(); + } + + else { + emit downloadProgressed(progress, tr("Downloading %n file(s)...", "", activeDownloads())); + } } -void DownloadManager::updateRow(DownloadItem *item) { - const int row = m_downloads.indexOf(item); +void DownloadManager::updateRow(DownloadItem* item) { + const int row = m_downloads.indexOf(item); - if (row == -1) { - return; - } + if (row == -1) { + return; + } - if (m_iconProvider.isNull()) { - m_iconProvider.reset(new QFileIconProvider()); - } + if (m_iconProvider.isNull()) { + m_iconProvider.reset(new QFileIconProvider()); + } - QIcon icon = m_iconProvider->icon(item->m_output.fileName()); + QIcon icon = m_iconProvider->icon(item->m_output.fileName()); - if (icon.isNull()) { - icon = style()->standardIcon(QStyle::SP_FileIcon); - } + if (icon.isNull()) { + icon = style()->standardIcon(QStyle::SP_FileIcon); + } - item->m_ui->m_lblFileIcon->setPixmap(icon.pixmap(DOWNLOADER_ICON_SIZE, DOWNLOADER_ICON_SIZE)); + 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: + // a) It is not downloading and private browsing is enabled. + // OR + // b) Item is already downloaded and it should be remove from downloader list. + bool remove = item->downloadedSuccessfully() && removePolicy() == DownloadManager::OnSuccessfullDownload; - int old_height = m_ui->m_viewDownloads->rowHeight(row); - m_ui->m_viewDownloads->setRowHeight(row, qMax(old_height, item->minimumSizeHint().height())); + if (remove) { + m_model->removeRow(row); + } - // 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. - bool remove = item->downloadedSuccessfully() && removePolicy() == DownloadManager::OnSuccessfullDownload; - - if (remove) { - m_model->removeRow(row); - } - - m_ui->m_btnCleanup->setEnabled(m_downloads.count() - activeDownloads() > 0); + m_ui->m_btnCleanup->setEnabled(m_downloads.count() - activeDownloads() > 0); } DownloadManager::RemovePolicy DownloadManager::removePolicy() const { - return m_removePolicy; + return m_removePolicy; } void DownloadManager::setRemovePolicy(RemovePolicy policy) { - if (policy != m_removePolicy) { - m_removePolicy = policy; - m_autoSaver->changeOccurred(); - } + if (policy != m_removePolicy) { + m_removePolicy = policy; + m_autoSaver->changeOccurred(); + } } void DownloadManager::save() const { - if (m_removePolicy == OnExit) { - // No saving. - return; - } + if (m_removePolicy == OnExit) { + // No saving. + return; + } - Settings *settings = qApp->settings(); - QString key; - settings->setValue(GROUP(Downloads), Downloads::RemovePolicy, (int) removePolicy()); + Settings* settings = qApp->settings(); + QString key; + settings->setValue(GROUP(Downloads), Downloads::RemovePolicy, (int) removePolicy()); - // Save all download items. - for (int i = 0; i < m_downloads.count(); i++) { - settings->setValue(GROUP(Downloads), QString(Downloads::ItemUrl).arg(i), m_downloads[i]->m_url); - settings->setValue(GROUP(Downloads), QString(Downloads::ItemLocation).arg(i), QFileInfo(m_downloads[i]->m_output).filePath()); - settings->setValue(GROUP(Downloads), QString(Downloads::ItemDone).arg(i), m_downloads[i]->downloadedSuccessfully()); - } + // Save all download items. + for (int i = 0; i < m_downloads.count(); i++) { + settings->setValue(GROUP(Downloads), QString(Downloads::ItemUrl).arg(i), m_downloads[i]->m_url); + settings->setValue(GROUP(Downloads), QString(Downloads::ItemLocation).arg(i), QFileInfo(m_downloads[i]->m_output).filePath()); + settings->setValue(GROUP(Downloads), QString(Downloads::ItemDone).arg(i), m_downloads[i]->downloadedSuccessfully()); + } - // Remove all redundant saved download items. - int i = m_downloads.size(); + // Remove all redundant saved download items. + int i = m_downloads.size(); - while (!(key = QString(Downloads::ItemUrl).arg(i)).isEmpty() && settings->contains(GROUP(Downloads), key)) { - settings->remove(GROUP(Downloads), key); - settings->remove(GROUP(Downloads), QString(Downloads::ItemLocation).arg(i)); - settings->remove(GROUP(Downloads), QString(Downloads::ItemDone).arg(i)); - - i++; - } + while (!(key = QString(Downloads::ItemUrl).arg(i)).isEmpty() && settings->contains(GROUP(Downloads), key)) { + settings->remove(GROUP(Downloads), key); + settings->remove(GROUP(Downloads), QString(Downloads::ItemLocation).arg(i)); + settings->remove(GROUP(Downloads), QString(Downloads::ItemDone).arg(i)); + i++; + } } void DownloadManager::load() { - const Settings *settings = qApp->settings(); - int i = 0; + const Settings* settings = qApp->settings(); + int i = 0; + // Restore the policy. + m_removePolicy = static_cast(settings->value(GROUP(Downloads), SETTING(Downloads::RemovePolicy)).toInt()); - // Restore the policy. - m_removePolicy = static_cast(settings->value(GROUP(Downloads), SETTING(Downloads::RemovePolicy)).toInt()); + // Restore downloads. + while (settings->contains(GROUP(Downloads), QString(Downloads::ItemUrl).arg(i))) { + QUrl url = settings->value(GROUP(Downloads), QString(Downloads::ItemUrl).arg(i)).toUrl(); + QString file_name = settings->value(GROUP(Downloads), QString(Downloads::ItemLocation).arg(i)).toString(); + bool done = settings->value(GROUP(Downloads), QString(Downloads::ItemDone).arg(i), true).toBool(); - // Restore downloads. - while (settings->contains(GROUP(Downloads), QString(Downloads::ItemUrl).arg(i))) { - QUrl url = settings->value(GROUP(Downloads), QString(Downloads::ItemUrl).arg(i)).toUrl(); - QString file_name = settings->value(GROUP(Downloads), QString(Downloads::ItemLocation).arg(i)).toString(); - bool done = settings->value(GROUP(Downloads), QString(Downloads::ItemDone).arg(i), true).toBool(); + if (!url.isEmpty() && !file_name.isEmpty()) { + DownloadItem* item = new DownloadItem(0, this); + item->m_output.setFileName(file_name); + item->m_url = url; + item->updateInfoAndUrlLabel(); + item->m_ui->m_btnStopDownload->setVisible(false); + item->m_ui->m_btnStopDownload->setEnabled(false); + item->m_ui->m_btnTryAgain->setVisible(!done); + item->m_ui->m_btnTryAgain->setEnabled(!done); + item->m_ui->m_progressDownload->setVisible(false); + addItem(item); + } - if (!url.isEmpty() && !file_name.isEmpty()) { - DownloadItem *item = new DownloadItem(0, this); - item->m_output.setFileName(file_name); - item->m_url = url; + i++; + } - item->updateInfoAndUrlLabel(); - - item->m_ui->m_btnStopDownload->setVisible(false); - item->m_ui->m_btnStopDownload->setEnabled(false); - item->m_ui->m_btnTryAgain->setVisible(!done); - item->m_ui->m_btnTryAgain->setEnabled(!done); - item->m_ui->m_progressDownload->setVisible(false); - addItem(item); - } - - i++; - } - - m_ui->m_btnCleanup->setEnabled(m_downloads.size() - activeDownloads() > 0); + m_ui->m_btnCleanup->setEnabled(m_downloads.size() - activeDownloads() > 0); } void DownloadManager::cleanup() { - if (!m_downloads.isEmpty()) { - m_model->removeRows(0, m_downloads.count()); - m_ui->m_btnCleanup->setEnabled(false); - } + if (!m_downloads.isEmpty()) { + m_model->removeRows(0, m_downloads.count()); + m_ui->m_btnCleanup->setEnabled(false); + } } -void DownloadManager::setDownloadDirectory(const QString &directory) { - m_downloadDirectory = directory; +void DownloadManager::setDownloadDirectory(const QString& directory) { + m_downloadDirectory = directory; - if (!m_downloadDirectory.isEmpty() && !m_downloadDirectory.endsWith(QDir::separator())) { - m_downloadDirectory += QDir::separator(); - } + if (!m_downloadDirectory.isEmpty() && !m_downloadDirectory.endsWith(QDir::separator())) { + m_downloadDirectory += QDir::separator(); + } } QString DownloadManager::downloadDirectory() { - return m_downloadDirectory; + return m_downloadDirectory; } QString DownloadManager::timeString(double time_remaining) { - QString remaining; + QString remaining; - if (time_remaining > 60) { - time_remaining = time_remaining / 60; - time_remaining = floor(time_remaining); - remaining = tr("%n minutes remaining", "", (int) time_remaining); - } - else { - time_remaining = floor(time_remaining); - remaining = tr("%n seconds remaining", "", (int) time_remaining); - } + if (time_remaining > 60) { + time_remaining = time_remaining / 60; + time_remaining = floor(time_remaining); + remaining = tr("%n minutes remaining", "", (int) time_remaining); + } - return remaining; + else { + time_remaining = floor(time_remaining); + remaining = tr("%n seconds remaining", "", (int) time_remaining); + } + + return remaining; } QString DownloadManager::dataString(qint64 size) { - QString unit; - double new_size; + QString unit; + double new_size; - if (size < 1024) { - new_size = size; - unit = tr("bytes"); - } - else if (size < 1024 * 1024) { - new_size = (double)size / (double)1024; - unit = tr("kB"); - } - else if (size < 1024 * 1024 * 1024) { - new_size = (double)size / (double)(1024 * 1024); - unit = tr("MB"); - } - else { - new_size = (double)size / (double)(1024 * 1024 * 1024); - unit = tr("GB"); - } + if (size < 1024) { + new_size = size; + unit = tr("bytes"); + } - return QString(QL1S("%1 %2")).arg(new_size, 0, 'f', 1).arg(unit); + else if (size < 1024 * 1024) { + new_size = (double)size / (double)1024; + unit = tr("kB"); + } + + else if (size < 1024 * 1024 * 1024) { + new_size = (double)size / (double)(1024 * 1024); + unit = tr("MB"); + } + + else { + new_size = (double)size / (double)(1024 * 1024 * 1024); + unit = tr("GB"); + } + + return QString(QL1S("%1 %2")).arg(new_size, 0, 'f', 1).arg(unit); } -DownloadModel::DownloadModel(DownloadManager *download_manager, QObject *parent) - : QAbstractListModel(parent), m_downloadManager(download_manager) { +DownloadModel::DownloadModel(DownloadManager* download_manager, QObject* parent) + : QAbstractListModel(parent), m_downloadManager(download_manager) { } -QVariant DownloadModel::data(const QModelIndex &index, int role) const { - if (index.row() < 0 || index.row() >= rowCount(index.parent())) { - return QVariant(); - } +QVariant DownloadModel::data(const QModelIndex& index, int role) const { + if (index.row() < 0 || index.row() >= rowCount(index.parent())) { + return QVariant(); + } - if (role == Qt::ToolTipRole) { - if (!m_downloadManager->m_downloads.at(index.row())->downloadedSuccessfully()) { - return m_downloadManager->m_downloads.at(index.row())->m_ui->m_lblInfoDownload->text(); - } - } + if (role == Qt::ToolTipRole) { + if (!m_downloadManager->m_downloads.at(index.row())->downloadedSuccessfully()) { + return m_downloadManager->m_downloads.at(index.row())->m_ui->m_lblInfoDownload->text(); + } + } - return QVariant(); + return QVariant(); } -int DownloadModel::rowCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : m_downloadManager->m_downloads.count(); +int DownloadModel::rowCount(const QModelIndex& parent) const { + return parent.isValid() ? 0 : m_downloadManager->m_downloads.count(); } -bool DownloadModel::removeRows(int row, int count, const QModelIndex &parent) { - if (parent.isValid()) { - return false; - } +bool DownloadModel::removeRows(int row, int count, const QModelIndex& parent) { + if (parent.isValid()) { + return false; + } - int lastRow = row + count - 1; + int lastRow = row + count - 1; - for (int i = lastRow; i >= row; --i) { - if (m_downloadManager->m_downloads.at(i)->downloadedSuccessfully() || - m_downloadManager->m_downloads.at(i)->m_ui->m_btnTryAgain->isEnabled()) { - beginRemoveRows(parent, i, i); - m_downloadManager->m_downloads.takeAt(i)->deleteLater(); - endRemoveRows(); - } - } + for (int i = lastRow; i >= row; --i) { + if (m_downloadManager->m_downloads.at(i)->downloadedSuccessfully() || + m_downloadManager->m_downloads.at(i)->m_ui->m_btnTryAgain->isEnabled()) { + beginRemoveRows(parent, i, i); + m_downloadManager->m_downloads.takeAt(i)->deleteLater(); + endRemoveRows(); + } + } - m_downloadManager->m_autoSaver->changeOccurred(); + m_downloadManager->m_autoSaver->changeOccurred(); - if (m_downloadManager->totalDownloads() == 0) { - m_downloadManager->m_ui->m_btnCleanup->setEnabled(false); - } + if (m_downloadManager->totalDownloads() == 0) { + m_downloadManager->m_ui->m_btnCleanup->setEnabled(false); + } - return true; + return true; } -Qt::ItemFlags DownloadModel::flags(const QModelIndex &index) const { - if (index.row() < 0 || index.row() >= rowCount(index.parent())) { - return Qt::NoItemFlags; - } +Qt::ItemFlags DownloadModel::flags(const QModelIndex& index) const { + if (index.row() < 0 || index.row() >= rowCount(index.parent())) { + return Qt::NoItemFlags; + } - Qt::ItemFlags default_flags = QAbstractItemModel::flags(index); - DownloadItem *item = m_downloadManager->m_downloads.at(index.row()); + Qt::ItemFlags default_flags = QAbstractItemModel::flags(index); + DownloadItem* item = m_downloadManager->m_downloads.at(index.row()); - if (item->downloadedSuccessfully()) { - return default_flags | Qt::ItemIsDragEnabled; - } + if (item->downloadedSuccessfully()) { + return default_flags | Qt::ItemIsDragEnabled; + } - return default_flags; + return default_flags; } -QMimeData *DownloadModel::mimeData(const QModelIndexList &indexes) const { - QMimeData *mimeData = new QMimeData(); - QList urls; +QMimeData* DownloadModel::mimeData(const QModelIndexList& indexes) const { + QMimeData* mimeData = new QMimeData(); + QList urls; - foreach (const QModelIndex &index, indexes) { - if (!index.isValid()) { - continue; - } + foreach (const QModelIndex& index, indexes) { + if (!index.isValid()) { + continue; + } - urls.append(QUrl::fromLocalFile(QFileInfo(m_downloadManager->m_downloads.at(index.row())->m_output).absoluteFilePath())); - } + urls.append(QUrl::fromLocalFile(QFileInfo(m_downloadManager->m_downloads.at(index.row())->m_output).absoluteFilePath())); + } - mimeData->setUrls(urls); - return mimeData; + mimeData->setUrls(urls); + return mimeData; } diff --git a/src/network-web/downloadmanager.h b/src/network-web/downloadmanager.h index a91496ff6..712a7b9c9 100755 --- a/src/network-web/downloadmanager.h +++ b/src/network-web/downloadmanager.h @@ -34,61 +34,61 @@ class QFileIconProvider; class QMimeData; class DownloadItem : public QWidget { - Q_OBJECT + Q_OBJECT - // Friends of this class. - friend class DownloadManager; - friend class DownloadModel; + // Friends of this class. + friend class DownloadManager; + friend class DownloadModel; - public: - // Constructors. - explicit DownloadItem(QNetworkReply *reply = 0, QWidget *parent = 0); - virtual ~DownloadItem(); + public: + // Constructors. + explicit DownloadItem(QNetworkReply* reply = 0, QWidget* parent = 0); + virtual ~DownloadItem(); - bool downloading() const; - bool downloadedSuccessfully() const; + bool downloading() const; + bool downloadedSuccessfully() const; - qint64 bytesTotal() const; - qint64 bytesReceived() const; - double remainingTime() const; - double currentSpeed() const; + qint64 bytesTotal() const; + qint64 bytesReceived() const; + double remainingTime() const; + double currentSpeed() const; - private slots: - void stop(); - void tryAgain(); - void openFile(); - void openFolder(); + private slots: + void stop(); + void tryAgain(); + void openFile(); + void openFolder(); - void downloadReadyRead(); - void error(QNetworkReply::NetworkError code); - void downloadProgress(qint64 bytes_received, qint64 bytes_total); - void metaDataChanged(); - void finished(); + void downloadReadyRead(); + void error(QNetworkReply::NetworkError code); + void downloadProgress(qint64 bytes_received, qint64 bytes_total); + void metaDataChanged(); + void finished(); - signals: - void statusChanged(); - void progress(qint64 bytes_received, qint64 bytes_total); - void downloadFinished(); + signals: + void statusChanged(); + void progress(qint64 bytes_received, qint64 bytes_total); + void downloadFinished(); - private: - void updateInfoAndUrlLabel(); - void getFileName(); - void init(); - void updateDownloadInfoLabel(); - QString saveFileName(const QString &directory) const; + private: + void updateInfoAndUrlLabel(); + void getFileName(); + void init(); + void updateDownloadInfoLabel(); + QString saveFileName(const QString& directory) const; - Ui::DownloadItem *m_ui; - QUrl m_url; - QFile m_output; - QNetworkReply *m_reply; - qint64 m_bytesReceived; - QTime m_downloadTime; - QTime m_lastProgressTime; - bool m_requestFileName; - bool m_startedSaving; - bool m_finishedDownloading; - bool m_gettingFileName; - bool m_canceledFileSelect; + Ui::DownloadItem* m_ui; + QUrl m_url; + QFile m_output; + QNetworkReply* m_reply; + qint64 m_bytesReceived; + QTime m_downloadTime; + QTime m_lastProgressTime; + bool m_requestFileName; + bool m_startedSaving; + bool m_finishedDownloading; + bool m_gettingFileName; + bool m_canceledFileSelect; }; #if defined(USE_WEBENGINE) @@ -96,92 +96,92 @@ class WebBrowser; #endif class DownloadManager : public TabContent { - Q_OBJECT - Q_PROPERTY(RemovePolicy removePolicy READ removePolicy WRITE setRemovePolicy) + Q_OBJECT + Q_PROPERTY(RemovePolicy removePolicy READ removePolicy WRITE setRemovePolicy) - friend class DownloadModel; + friend class DownloadModel; - public: - enum RemovePolicy { - Never, - OnExit, - OnSuccessfullDownload - }; + public: + enum RemovePolicy { + Never, + OnExit, + OnSuccessfullDownload + }; - Q_ENUM(RemovePolicy) + Q_ENUM(RemovePolicy) - explicit DownloadManager(QWidget *parent = 0); - virtual ~DownloadManager(); + explicit DownloadManager(QWidget* parent = 0); + virtual ~DownloadManager(); #if defined(USE_WEBENGINE) - WebBrowser *webBrowser() const { - return nullptr; - } + WebBrowser* webBrowser() const { + return nullptr; + } #endif - QNetworkAccessManager *networkManager() const; + QNetworkAccessManager* networkManager() const; - int totalDownloads() const; - int activeDownloads() const; - int downloadProgress() const; + int totalDownloads() const; + int activeDownloads() const; + int downloadProgress() const; - RemovePolicy removePolicy() const; - void setRemovePolicy(RemovePolicy policy); + RemovePolicy removePolicy() const; + void setRemovePolicy(RemovePolicy policy); - void setDownloadDirectory(const QString &directory); - QString downloadDirectory(); + void setDownloadDirectory(const QString& directory); + QString downloadDirectory(); - static QString timeString(double time_remaining); - static QString dataString(qint64 size); + static QString timeString(double time_remaining); + static QString dataString(qint64 size); - public slots: - void download(const QNetworkRequest &request); - void download(const QUrl &url); - void handleUnsupportedContent(QNetworkReply *reply); - void cleanup(); + public slots: + void download(const QNetworkRequest& request); + void download(const QUrl& url); + void handleUnsupportedContent(QNetworkReply* reply); + void cleanup(); - private slots: - void save() const; - void load(); + private slots: + void save() const; + void load(); - void updateRow(DownloadItem *item); - void updateRow(); - void itemProgress(); - void itemFinished(); + void updateRow(DownloadItem* item); + void updateRow(); + void itemProgress(); + void itemFinished(); - signals: - void downloadProgressed(int progress, const QString &description); - void downloadFinished(); + signals: + void downloadProgressed(int progress, const QString& description); + void downloadFinished(); - private: - void addItem(DownloadItem *item); + private: + void addItem(DownloadItem* item); - QScopedPointer m_ui; - AutoSaver *m_autoSaver; - DownloadModel *m_model; - QNetworkAccessManager *m_networkManager; - QScopedPointer m_iconProvider; - QList m_downloads; - RemovePolicy m_removePolicy; - QString m_downloadDirectory; + QScopedPointer m_ui; + AutoSaver* m_autoSaver; + DownloadModel* m_model; + QNetworkAccessManager* m_networkManager; + QScopedPointer m_iconProvider; + QList m_downloads; + RemovePolicy m_removePolicy; + QString m_downloadDirectory; }; class DownloadModel : public QAbstractListModel { - Q_OBJECT + Q_OBJECT - friend class DownloadManager; + friend class DownloadManager; - public: - explicit DownloadModel(DownloadManager *download_manager, QObject *parent = 0); + public: + explicit DownloadModel(DownloadManager* download_manager, QObject* parent = 0); - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - int rowCount(const QModelIndex &parent = QModelIndex()) const; - bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); - Qt::ItemFlags flags(const QModelIndex &index) const; - QMimeData *mimeData(const QModelIndexList &indexes) const; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex& parent = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()); + Qt::ItemFlags flags(const QModelIndex& index) const; + QMimeData* mimeData(const QModelIndexList& indexes) const; - private: - DownloadManager *m_downloadManager; + private: + DownloadManager* m_downloadManager; }; #endif // DOWNLOADMANAGER_H diff --git a/src/network-web/googlesuggest.cpp b/src/network-web/googlesuggest.cpp index da5322918..53e482940 100755 --- a/src/network-web/googlesuggest.cpp +++ b/src/network-web/googlesuggest.cpp @@ -1,204 +1,198 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -// You may use this file under the terms of the BSD license as follows: -// -// "Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in -// the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -// of its contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." - -#include "network-web/googlesuggest.h" - -#include "definitions/definitions.h" -#include "network-web/silentnetworkaccessmanager.h" -#include "gui/locationlineedit.h" - -#include -#include -#include -#include -#include -#include -#include -#include - - -GoogleSuggest::GoogleSuggest(LocationLineEdit *editor, QObject *parent) - : QObject(parent), editor(editor), popup(new QListWidget()), m_enteredText(QString()) { - popup->setWindowFlags(Qt::Popup); - popup->setFocusPolicy(Qt::NoFocus); - popup->setFocusProxy(editor); - popup->setMouseTracking(true); - popup->setSelectionBehavior(QAbstractItemView::SelectRows); - popup->setFrameStyle(QFrame::Box | QFrame::Plain); - popup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - popup->installEventFilter(this); - - timer = new QTimer(this); - timer->setSingleShot(true); - timer->setInterval(500); - - connect(popup.data(), &QListWidget::itemClicked, this, &GoogleSuggest::doneCompletion); - connect(timer, &QTimer::timeout, this, &GoogleSuggest::autoSuggest); - connect(editor, &LocationLineEdit::textEdited, timer, static_cast(&QTimer::start)); -} - -GoogleSuggest::~GoogleSuggest() { -} - -bool GoogleSuggest::eventFilter(QObject *object, QEvent *event) { - if (object != popup.data()) { - return false; - } - - if (event->type() == QEvent::MouseButtonPress) { - popup->hide(); - editor->setFocus(); - return true; - } - - if (event->type() == QEvent::KeyPress) { - bool consumed = false; - const int key = static_cast(event)->key(); - - switch (key) { - case Qt::Key_Enter: - case Qt::Key_Return: - doneCompletion(); - consumed = true; - - case Qt::Key_Escape: - editor->setFocus(); - popup->hide(); - consumed = true; - - case Qt::Key_Up: - case Qt::Key_Down: - case Qt::Key_Home: - case Qt::Key_End: - case Qt::Key_PageUp: - case Qt::Key_PageDown: - break; - - default: - editor->setFocus(); - editor->event(event); - popup->hide(); - break; - } - - return consumed; - } - - return false; -} - -void GoogleSuggest::showCompletion(const QStringList &choices) { - if (choices.isEmpty()) { - return; - } - - popup->setUpdatesEnabled(false); - popup->clear(); - - foreach (const QString &choice, choices) { - new QListWidgetItem(choice, popup.data()); - } - - popup->setCurrentItem(popup->item(0)); - popup->adjustSize(); - popup->setUpdatesEnabled(true); - popup->resize(editor->width(), popup->sizeHintForRow(0) * qMin(7, choices.count()) + 3); - popup->move(editor->mapToGlobal(QPoint(0, editor->height()))); - popup->setFocus(); - popup->show(); -} - -void GoogleSuggest::doneCompletion() { - timer->stop(); - popup->hide(); - editor->setFocus(); - - QListWidgetItem *item = popup->currentItem(); - - if (item != nullptr) { - editor->submit(QString(GOOGLE_SEARCH_URL).arg(item->text())); - } -} - -void GoogleSuggest::preventSuggest() { - timer->stop(); -} - -void GoogleSuggest::autoSuggest() { - m_enteredText = QUrl::toPercentEncoding(editor->text()); - QString url = QString(GOOGLE_SUGGEST_URL).arg(m_enteredText); - - connect(SilentNetworkAccessManager::instance()->get(QNetworkRequest(QString(url))), &QNetworkReply::finished, - this, &GoogleSuggest::handleNetworkData); -} - -void GoogleSuggest::handleNetworkData() { - QScopedPointer reply(static_cast(sender())); - - if (!reply->error()) { - QStringList choices; - QDomDocument xml; - QByteArray response = reply->readAll(); - - const QTextCodec *c = QTextCodec::codecForUtfText(response); - xml.setContent(c->toUnicode(response)); - - QDomNodeList suggestions = xml.elementsByTagName(QSL("suggestion")); - - for (int i = 0; i < suggestions.size(); i++) { - const QDomElement element = suggestions.at(i).toElement(); - - if (element.attributes().contains(QSL("data"))) { - choices.append(element.attribute(QSL("data"))); - } - } - - if (choices.isEmpty()) { - choices.append(m_enteredText); - } - - showCompletion(choices); - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +// You may use this file under the terms of the BSD license as follows: +// +// "Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +// of its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + +#include "network-web/googlesuggest.h" + +#include "definitions/definitions.h" +#include "network-web/silentnetworkaccessmanager.h" +#include "gui/locationlineedit.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +GoogleSuggest::GoogleSuggest(LocationLineEdit* editor, QObject* parent) + : QObject(parent), editor(editor), popup(new QListWidget()), m_enteredText(QString()) { + popup->setWindowFlags(Qt::Popup); + popup->setFocusPolicy(Qt::NoFocus); + popup->setFocusProxy(editor); + popup->setMouseTracking(true); + popup->setSelectionBehavior(QAbstractItemView::SelectRows); + popup->setFrameStyle(QFrame::Box | QFrame::Plain); + popup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + popup->installEventFilter(this); + timer = new QTimer(this); + timer->setSingleShot(true); + timer->setInterval(500); + connect(popup.data(), &QListWidget::itemClicked, this, &GoogleSuggest::doneCompletion); + connect(timer, &QTimer::timeout, this, &GoogleSuggest::autoSuggest); + connect(editor, &LocationLineEdit::textEdited, timer, static_cast(&QTimer::start)); +} + +GoogleSuggest::~GoogleSuggest() { +} + +bool GoogleSuggest::eventFilter(QObject* object, QEvent* event) { + if (object != popup.data()) { + return false; + } + + if (event->type() == QEvent::MouseButtonPress) { + popup->hide(); + editor->setFocus(); + return true; + } + + if (event->type() == QEvent::KeyPress) { + bool consumed = false; + const int key = static_cast(event)->key(); + + switch (key) { + case Qt::Key_Enter: + case Qt::Key_Return: + doneCompletion(); + consumed = true; + + case Qt::Key_Escape: + editor->setFocus(); + popup->hide(); + consumed = true; + + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Home: + case Qt::Key_End: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + break; + + default: + editor->setFocus(); + editor->event(event); + popup->hide(); + break; + } + + return consumed; + } + + return false; +} + +void GoogleSuggest::showCompletion(const QStringList& choices) { + if (choices.isEmpty()) { + return; + } + + popup->setUpdatesEnabled(false); + popup->clear(); + + foreach (const QString& choice, choices) { + new QListWidgetItem(choice, popup.data()); + } + + popup->setCurrentItem(popup->item(0)); + popup->adjustSize(); + popup->setUpdatesEnabled(true); + popup->resize(editor->width(), popup->sizeHintForRow(0) * qMin(7, choices.count()) + 3); + popup->move(editor->mapToGlobal(QPoint(0, editor->height()))); + popup->setFocus(); + popup->show(); +} + +void GoogleSuggest::doneCompletion() { + timer->stop(); + popup->hide(); + editor->setFocus(); + QListWidgetItem* item = popup->currentItem(); + + if (item != nullptr) { + editor->submit(QString(GOOGLE_SEARCH_URL).arg(item->text())); + } +} + +void GoogleSuggest::preventSuggest() { + timer->stop(); +} + +void GoogleSuggest::autoSuggest() { + m_enteredText = QUrl::toPercentEncoding(editor->text()); + QString url = QString(GOOGLE_SUGGEST_URL).arg(m_enteredText); + connect(SilentNetworkAccessManager::instance()->get(QNetworkRequest(QString(url))), &QNetworkReply::finished, + this, &GoogleSuggest::handleNetworkData); +} + +void GoogleSuggest::handleNetworkData() { + QScopedPointer reply(static_cast(sender())); + + if (!reply->error()) { + QStringList choices; + QDomDocument xml; + QByteArray response = reply->readAll(); + const QTextCodec* c = QTextCodec::codecForUtfText(response); + xml.setContent(c->toUnicode(response)); + QDomNodeList suggestions = xml.elementsByTagName(QSL("suggestion")); + + for (int i = 0; i < suggestions.size(); i++) { + const QDomElement element = suggestions.at(i).toElement(); + + if (element.attributes().contains(QSL("data"))) { + choices.append(element.attribute(QSL("data"))); + } + } + + if (choices.isEmpty()) { + choices.append(m_enteredText); + } + + showCompletion(choices); + } +} diff --git a/src/network-web/googlesuggest.h b/src/network-web/googlesuggest.h index d83fa1b0b..52afc7f47 100755 --- a/src/network-web/googlesuggest.h +++ b/src/network-web/googlesuggest.h @@ -1,82 +1,82 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -// You may use this file under the terms of the BSD license as follows: -// -// "Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in -// the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -// of its contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." - -#ifndef GOOGLESUGGEST_H -#define GOOGLESUGGEST_H - -#include - - -class LocationLineEdit; -class QNetworkReply; -class QTimer; -class QListWidget; -class QNetworkAccessManager; - -class GoogleSuggest : public QObject { - Q_OBJECT - - public: - // Constructors. - explicit GoogleSuggest(LocationLineEdit *editor, QObject *parent = 0); - virtual ~GoogleSuggest(); - - bool eventFilter(QObject *object, QEvent *event); - void showCompletion(const QStringList &choices); - - public slots: - void doneCompletion(); - void preventSuggest(); - void autoSuggest(); - void handleNetworkData(); - - private: - LocationLineEdit *editor; - QScopedPointer popup; - QTimer *timer; - QString m_enteredText; -}; - -#endif // GOOGLESUGGEST_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +// You may use this file under the terms of the BSD license as follows: +// +// "Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +// of its contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + +#ifndef GOOGLESUGGEST_H +#define GOOGLESUGGEST_H + +#include + + +class LocationLineEdit; +class QNetworkReply; +class QTimer; +class QListWidget; +class QNetworkAccessManager; + +class GoogleSuggest : public QObject { + Q_OBJECT + + public: + // Constructors. + explicit GoogleSuggest(LocationLineEdit* editor, QObject* parent = 0); + virtual ~GoogleSuggest(); + + bool eventFilter(QObject* object, QEvent* event); + void showCompletion(const QStringList& choices); + + public slots: + void doneCompletion(); + void preventSuggest(); + void autoSuggest(); + void handleNetworkData(); + + private: + LocationLineEdit* editor; + QScopedPointer popup; + QTimer* timer; + QString m_enteredText; +}; + +#endif // GOOGLESUGGEST_H diff --git a/src/network-web/networkfactory.cpp b/src/network-web/networkfactory.cpp index 21d809eac..9bd71f04a 100755 --- a/src/network-web/networkfactory.cpp +++ b/src/network-web/networkfactory.cpp @@ -32,172 +32,166 @@ NetworkFactory::NetworkFactory() { } -QStringList NetworkFactory::extractFeedLinksFromHtmlPage(const QUrl &url, const QString &html) { - QStringList feeds; - const QRegExp rx(FEED_REGEX_MATCHER, Qt::CaseInsensitive); - const QRegExp rx_href(FEED_HREF_REGEX_MATCHER, Qt::CaseInsensitive); +QStringList NetworkFactory::extractFeedLinksFromHtmlPage(const QUrl& url, const QString& html) { + QStringList feeds; + const QRegExp rx(FEED_REGEX_MATCHER, Qt::CaseInsensitive); + const QRegExp rx_href(FEED_HREF_REGEX_MATCHER, Qt::CaseInsensitive); - for (int pos = 0; (pos = rx.indexIn(html, pos)) != -1; pos += rx.matchedLength()) { - QString link_element = html.mid(pos, rx.matchedLength()); + for (int pos = 0; (pos = rx.indexIn(html, pos)) != -1; pos += rx.matchedLength()) { + QString link_element = html.mid(pos, rx.matchedLength()); - if (rx_href.indexIn(link_element) != -1) { - QString href_attribute = rx_href.capturedTexts().at(0); - QString feed_link = href_attribute.mid(6, href_attribute.size() - 7); + if (rx_href.indexIn(link_element) != -1) { + QString href_attribute = rx_href.capturedTexts().at(0); + QString feed_link = href_attribute.mid(6, href_attribute.size() - 7); - if (feed_link.startsWith(QL1S("//"))) { - feed_link = QString(URI_SCHEME_HTTP) + feed_link.mid(2); - } - else if (feed_link.startsWith(QL1C('/'))) { - feed_link = url.toString(QUrl::RemovePath | QUrl::RemoveQuery | QUrl::StripTrailingSlash) + feed_link; - } + if (feed_link.startsWith(QL1S("//"))) { + feed_link = QString(URI_SCHEME_HTTP) + feed_link.mid(2); + } - feeds.append(feed_link); - } - } + else if (feed_link.startsWith(QL1C('/'))) { + feed_link = url.toString(QUrl::RemovePath | QUrl::RemoveQuery | QUrl::StripTrailingSlash) + feed_link; + } - return feeds; + feeds.append(feed_link); + } + } + + return feeds; } QString NetworkFactory::networkErrorText(QNetworkReply::NetworkError error_code) { - switch (error_code) { - case QNetworkReply::ProtocolUnknownError: - case QNetworkReply::ProtocolFailure: - //: Network status. - return tr("protocol error"); + switch (error_code) { + case QNetworkReply::ProtocolUnknownError: + case QNetworkReply::ProtocolFailure: + //: Network status. + return tr("protocol error"); - case QNetworkReply::ContentAccessDenied: - return tr("access to content was denied"); + case QNetworkReply::ContentAccessDenied: + return tr("access to content was denied"); - case QNetworkReply::HostNotFoundError: - //: Network status. - return tr("host not found"); + case QNetworkReply::HostNotFoundError: + //: Network status. + return tr("host not found"); - case QNetworkReply::OperationCanceledError: - case QNetworkReply::TimeoutError: - return tr("connection timed out or was cancelled"); + case QNetworkReply::OperationCanceledError: + case QNetworkReply::TimeoutError: + return tr("connection timed out or was cancelled"); - case QNetworkReply::RemoteHostClosedError: - case QNetworkReply::ConnectionRefusedError: - //: Network status. - return tr("connection refused"); + case QNetworkReply::RemoteHostClosedError: + case QNetworkReply::ConnectionRefusedError: + //: Network status. + return tr("connection refused"); - case QNetworkReply::ProxyTimeoutError: - //: Network status. - return tr("connection timed out"); + case QNetworkReply::ProxyTimeoutError: + //: Network status. + return tr("connection timed out"); - case QNetworkReply::SslHandshakeFailedError: - //: Network status. - return tr("SSL handshake failed"); + case QNetworkReply::SslHandshakeFailedError: + //: Network status. + return tr("SSL handshake failed"); - case QNetworkReply::ProxyConnectionClosedError: - case QNetworkReply::ProxyConnectionRefusedError: - //: Network status. - return tr("proxy server connection refused"); + case QNetworkReply::ProxyConnectionClosedError: + case QNetworkReply::ProxyConnectionRefusedError: + //: Network status. + return tr("proxy server connection refused"); - case QNetworkReply::TemporaryNetworkFailureError: - //: Network status. - return tr("temporary failure"); + case QNetworkReply::TemporaryNetworkFailureError: + //: Network status. + return tr("temporary failure"); - case QNetworkReply::AuthenticationRequiredError: - //: Network status. - return tr("authentication failed"); + case QNetworkReply::AuthenticationRequiredError: + //: Network status. + return tr("authentication failed"); - case QNetworkReply::ProxyAuthenticationRequiredError: - //: Network status. - return tr("proxy authentication required"); + case QNetworkReply::ProxyAuthenticationRequiredError: + //: Network status. + return tr("proxy authentication required"); - case QNetworkReply::ProxyNotFoundError: - //: Network status. - return tr("proxy server not found"); + case QNetworkReply::ProxyNotFoundError: + //: Network status. + return tr("proxy server not found"); - case QNetworkReply::NoError: - //: Network status. - return tr("no errors"); + case QNetworkReply::NoError: + //: Network status. + return tr("no errors"); - case QNetworkReply::UnknownContentError: - //: Network status. - return tr("unknown content"); + case QNetworkReply::UnknownContentError: + //: Network status. + return tr("unknown content"); - case QNetworkReply::ContentNotFoundError: - //: Network status. - return tr("content not found"); + case QNetworkReply::ContentNotFoundError: + //: Network status. + return tr("content not found"); - default: - //: Network status. - return tr("unknown error"); - } + default: + //: Network status. + return tr("unknown error"); + } } -QNetworkReply::NetworkError NetworkFactory::downloadIcon(const QList &urls, int timeout, QIcon &output) { - QNetworkReply::NetworkError network_result = QNetworkReply::UnknownNetworkError; +QNetworkReply::NetworkError NetworkFactory::downloadIcon(const QList& urls, int timeout, QIcon& output) { + QNetworkReply::NetworkError network_result = QNetworkReply::UnknownNetworkError; - foreach (const QString &url, urls) { - const QString google_s2_with_url = QString("http://www.google.com/s2/favicons?domain=%1").arg(QUrl(url).host()); - QByteArray icon_data; - network_result = performNetworkOperation(google_s2_with_url, timeout, QByteArray(), QString(), icon_data, - QNetworkAccessManager::GetOperation).first; + foreach (const QString& url, urls) { + const QString google_s2_with_url = QString("http://www.google.com/s2/favicons?domain=%1").arg(QUrl(url).host()); + QByteArray icon_data; + network_result = performNetworkOperation(google_s2_with_url, timeout, QByteArray(), QString(), icon_data, + QNetworkAccessManager::GetOperation).first; - if (network_result == QNetworkReply::NoError) { - QPixmap icon_pixmap; - icon_pixmap.loadFromData(icon_data); - output = QIcon(icon_pixmap); - break; - } - } + if (network_result == QNetworkReply::NoError) { + QPixmap icon_pixmap; + icon_pixmap.loadFromData(icon_data); + output = QIcon(icon_pixmap); + break; + } + } - return network_result; + return network_result; } -NetworkResult NetworkFactory::performNetworkOperation(const QString &url, int timeout, const QByteArray &input_data, - const QString &input_content_type, QByteArray &output, +NetworkResult NetworkFactory::performNetworkOperation(const QString& url, int timeout, const QByteArray& input_data, + const QString& input_content_type, QByteArray& output, QNetworkAccessManager::Operation operation, bool protected_contents, - const QString &username, const QString &password, bool set_basic_header) { - Downloader downloader; - QEventLoop loop; - NetworkResult result; + const QString& username, const QString& password, bool set_basic_header) { + Downloader downloader; + QEventLoop loop; + NetworkResult result; - if (!input_content_type.isEmpty()) { - downloader.appendRawHeader("Content-Type", input_content_type.toLocal8Bit()); - } + if (!input_content_type.isEmpty()) { + downloader.appendRawHeader("Content-Type", input_content_type.toLocal8Bit()); + } - if (set_basic_header) { - QString basic_value = username + ":" + password; - QString header_value = QString("Basic ") + QString(basic_value.toUtf8().toBase64()); + if (set_basic_header) { + QString basic_value = username + ":" + password; + QString header_value = QString("Basic ") + QString(basic_value.toUtf8().toBase64()); + downloader.appendRawHeader("Authorization", header_value.toLocal8Bit()); + } - downloader.appendRawHeader("Authorization", header_value.toLocal8Bit()); - } - - // We need to quit event loop when the download finishes. - QObject::connect(&downloader, &Downloader::completed, &loop, &QEventLoop::quit); - - downloader.manipulateData(url, operation, input_data, timeout, protected_contents, username, password); - loop.exec(); - output = downloader.lastOutputData(); - result.first = downloader.lastOutputError(); - result.second = downloader.lastContentType(); - - return result; + // We need to quit event loop when the download finishes. + QObject::connect(&downloader, &Downloader::completed, &loop, &QEventLoop::quit); + downloader.manipulateData(url, operation, input_data, timeout, protected_contents, username, password); + loop.exec(); + output = downloader.lastOutputData(); + result.first = downloader.lastOutputError(); + result.second = downloader.lastContentType(); + return result; } -NetworkResult NetworkFactory::downloadFeedFile(const QString &url, int timeout, - QByteArray &output, bool protected_contents, - const QString &username, const QString &password) { - // Here, we want to achieve "synchronous" approach because we want synchronout download API for - // some use-cases too. - Downloader downloader; - QEventLoop loop; - NetworkResult result; - - downloader.appendRawHeader("Accept", ACCEPT_HEADER_FOR_FEED_DOWNLOADER); - - // We need to quit event loop when the download finishes. - QObject::connect(&downloader, &Downloader::completed, &loop, &QEventLoop::quit); - - downloader.downloadFile(url, timeout, protected_contents, username, password); - loop.exec(); - output = downloader.lastOutputData(); - result.first = downloader.lastOutputError(); - result.second = downloader.lastContentType(); - - return result; +NetworkResult NetworkFactory::downloadFeedFile(const QString& url, int timeout, + QByteArray& output, bool protected_contents, + const QString& username, const QString& password) { + // Here, we want to achieve "synchronous" approach because we want synchronout download API for + // some use-cases too. + Downloader downloader; + QEventLoop loop; + NetworkResult result; + downloader.appendRawHeader("Accept", ACCEPT_HEADER_FOR_FEED_DOWNLOADER); + // We need to quit event loop when the download finishes. + QObject::connect(&downloader, &Downloader::completed, &loop, &QEventLoop::quit); + downloader.downloadFile(url, timeout, protected_contents, username, password); + loop.exec(); + output = downloader.lastOutputData(); + result.first = downloader.lastOutputError(); + result.second = downloader.lastContentType(); + return result; } diff --git a/src/network-web/networkfactory.h b/src/network-web/networkfactory.h index 7174daba5..6891c5a91 100755 --- a/src/network-web/networkfactory.h +++ b/src/network-web/networkfactory.h @@ -27,31 +27,31 @@ typedef QPair NetworkResult; class NetworkFactory { - Q_DECLARE_TR_FUNCTIONS(NetworkFactory) + Q_DECLARE_TR_FUNCTIONS(NetworkFactory) - private: - // Constructor. - explicit NetworkFactory(); + private: + // Constructor. + explicit NetworkFactory(); - public: - static QStringList extractFeedLinksFromHtmlPage(const QUrl &url, const QString &html); + public: + static QStringList extractFeedLinksFromHtmlPage(const QUrl& url, const QString& html); - // Returns human readable text for given network error. - static QString networkErrorText(QNetworkReply::NetworkError error_code); + // Returns human readable text for given network error. + static QString networkErrorText(QNetworkReply::NetworkError error_code); - // Performs SYNCHRONOUS download if favicon for the site, - // given URL belongs to. - static QNetworkReply::NetworkError downloadIcon(const QList &urls, int timeout, QIcon &output); + // Performs SYNCHRONOUS download if favicon for the site, + // given URL belongs to. + static QNetworkReply::NetworkError downloadIcon(const QList& urls, int timeout, QIcon& output); - static NetworkResult performNetworkOperation(const QString &url, int timeout, const QByteArray &input_data, - const QString &input_content_type, QByteArray &output, - QNetworkAccessManager::Operation operation, - bool protected_contents = false, const QString &username = QString(), - const QString &password = QString(), bool set_basic_header = false); + static NetworkResult performNetworkOperation(const QString& url, int timeout, const QByteArray& input_data, + const QString& input_content_type, QByteArray& output, + QNetworkAccessManager::Operation operation, + bool protected_contents = false, const QString& username = QString(), + const QString& password = QString(), bool set_basic_header = false); - static NetworkResult downloadFeedFile(const QString &url, int timeout, QByteArray &output, - bool protected_contents = false, const QString &username = QString(), - const QString &password = QString()); + static NetworkResult downloadFeedFile(const QString& url, int timeout, QByteArray& output, + bool protected_contents = false, const QString& username = QString(), + const QString& password = QString()); }; #endif // NETWORKFACTORY_H diff --git a/src/network-web/networkurlinterceptor.cpp b/src/network-web/networkurlinterceptor.cpp index 27b0344ac..ee7338676 100755 --- a/src/network-web/networkurlinterceptor.cpp +++ b/src/network-web/networkurlinterceptor.cpp @@ -23,34 +23,34 @@ #include "miscellaneous/settings.h" -NetworkUrlInterceptor::NetworkUrlInterceptor(QObject *parent) - : QWebEngineUrlRequestInterceptor(parent), m_sendDNT(false) { +NetworkUrlInterceptor::NetworkUrlInterceptor(QObject* parent) + : QWebEngineUrlRequestInterceptor(parent), m_sendDNT(false) { } -void NetworkUrlInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) { - if (m_sendDNT) { - info.setHttpHeader(QByteArrayLiteral("DNT"), QByteArrayLiteral("1")); - } +void NetworkUrlInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) { + if (m_sendDNT) { + info.setHttpHeader(QByteArrayLiteral("DNT"), QByteArrayLiteral("1")); + } - // TODO: mužeme zde nastavovat custom věci pro každej webengine sitovej pozadavek - // treba user agenta - //info.setHttpHeader(QByteArrayLiteral("User-Agent"), mApp->userAgentManager()->userAgentForUrl(info.firstPartyUrl()).toUtf8()); + // TODO: mužeme zde nastavovat custom věci pro každej webengine sitovej pozadavek + // treba user agenta + //info.setHttpHeader(QByteArrayLiteral("User-Agent"), mApp->userAgentManager()->userAgentForUrl(info.firstPartyUrl()).toUtf8()); - foreach (UrlInterceptor *interceptor, m_interceptors) { - interceptor->interceptRequest(info); - } + foreach (UrlInterceptor* interceptor, m_interceptors) { + interceptor->interceptRequest(info); + } } -void NetworkUrlInterceptor::installUrlInterceptor(UrlInterceptor *interceptor) { - if (!m_interceptors.contains(interceptor)) { - m_interceptors.append(interceptor); - } +void NetworkUrlInterceptor::installUrlInterceptor(UrlInterceptor* interceptor) { + if (!m_interceptors.contains(interceptor)) { + m_interceptors.append(interceptor); + } } -void NetworkUrlInterceptor::removeUrlInterceptor(UrlInterceptor *interceptor) { - m_interceptors.removeOne(interceptor); +void NetworkUrlInterceptor::removeUrlInterceptor(UrlInterceptor* interceptor) { + m_interceptors.removeOne(interceptor); } void NetworkUrlInterceptor::loadSettings() { - m_sendDNT = qApp->settings()->value(GROUP(Browser), SETTING(Browser::SendDNT)).toBool(); + m_sendDNT = qApp->settings()->value(GROUP(Browser), SETTING(Browser::SendDNT)).toBool(); } diff --git a/src/network-web/networkurlinterceptor.h b/src/network-web/networkurlinterceptor.h index 2ab533577..c3d42ceb4 100755 --- a/src/network-web/networkurlinterceptor.h +++ b/src/network-web/networkurlinterceptor.h @@ -25,21 +25,21 @@ class UrlInterceptor; class NetworkUrlInterceptor : public QWebEngineUrlRequestInterceptor { - Q_OBJECT + Q_OBJECT - public: - explicit NetworkUrlInterceptor(QObject *parent = nullptr); + public: + explicit NetworkUrlInterceptor(QObject* parent = nullptr); - void interceptRequest(QWebEngineUrlRequestInfo &info) Q_DECL_OVERRIDE; + void interceptRequest(QWebEngineUrlRequestInfo& info) Q_DECL_OVERRIDE; - void installUrlInterceptor(UrlInterceptor *interceptor); - void removeUrlInterceptor(UrlInterceptor *interceptor); + void installUrlInterceptor(UrlInterceptor* interceptor); + void removeUrlInterceptor(UrlInterceptor* interceptor); - void loadSettings(); + void loadSettings(); - private: - QList m_interceptors; - bool m_sendDNT; + private: + QList m_interceptors; + bool m_sendDNT; }; #endif // NETWORKURLINTERCEPTOR_H diff --git a/src/network-web/silentnetworkaccessmanager.cpp b/src/network-web/silentnetworkaccessmanager.cpp index 6da49c6ed..59fdd2c0d 100755 --- a/src/network-web/silentnetworkaccessmanager.cpp +++ b/src/network-web/silentnetworkaccessmanager.cpp @@ -25,37 +25,36 @@ QPointer SilentNetworkAccessManager::s_instance; -SilentNetworkAccessManager::SilentNetworkAccessManager(QObject *parent) - : BaseNetworkAccessManager(parent) { - connect(this, &SilentNetworkAccessManager::authenticationRequired, - this, &SilentNetworkAccessManager::onAuthenticationRequired, Qt::DirectConnection); +SilentNetworkAccessManager::SilentNetworkAccessManager(QObject* parent) + : BaseNetworkAccessManager(parent) { + connect(this, &SilentNetworkAccessManager::authenticationRequired, + this, &SilentNetworkAccessManager::onAuthenticationRequired, Qt::DirectConnection); } SilentNetworkAccessManager::~SilentNetworkAccessManager() { - qDebug("Destroying SilentNetworkAccessManager instance."); + qDebug("Destroying SilentNetworkAccessManager instance."); } -SilentNetworkAccessManager *SilentNetworkAccessManager::instance() { - if (s_instance.isNull()) { - s_instance = new SilentNetworkAccessManager(qApp); - } +SilentNetworkAccessManager* SilentNetworkAccessManager::instance() { + if (s_instance.isNull()) { + s_instance = new SilentNetworkAccessManager(qApp); + } - return s_instance; + return s_instance; } -void SilentNetworkAccessManager::onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) { - if (reply->property("protected").toBool()) { - // This feed contains authentication information, it is good. - authenticator->setUser(reply->property("username").toString()); - authenticator->setPassword(reply->property("password").toString()); - reply->setProperty("authentication-given", true); +void SilentNetworkAccessManager::onAuthenticationRequired(QNetworkReply* reply, QAuthenticator* authenticator) { + if (reply->property("protected").toBool()) { + // This feed contains authentication information, it is good. + authenticator->setUser(reply->property("username").toString()); + authenticator->setPassword(reply->property("password").toString()); + reply->setProperty("authentication-given", true); + qDebug("Item '%s' requested authentication and got it.", qPrintable(reply->url().toString())); + } - qDebug("Item '%s' requested authentication and got it.", qPrintable(reply->url().toString())); - } - else { - reply->setProperty("authentication-given", false); - - // Authentication is required but this feed does not contain it. - qWarning("Item '%s' requested authentication but username/password is not available.", qPrintable(reply->url().toString())); - } + else { + reply->setProperty("authentication-given", false); + // Authentication is required but this feed does not contain it. + qWarning("Item '%s' requested authentication but username/password is not available.", qPrintable(reply->url().toString())); + } } diff --git a/src/network-web/silentnetworkaccessmanager.h b/src/network-web/silentnetworkaccessmanager.h index c5879e901..6fb512aaa 100755 --- a/src/network-web/silentnetworkaccessmanager.h +++ b/src/network-web/silentnetworkaccessmanager.h @@ -26,22 +26,22 @@ // Network manager used for more communication for feeds. // This network manager does not provide any GUI interaction options. class SilentNetworkAccessManager : public BaseNetworkAccessManager { - Q_OBJECT + Q_OBJECT - public: - // Constructors and destructors. - explicit SilentNetworkAccessManager(QObject *parent = 0); - virtual ~SilentNetworkAccessManager(); + public: + // Constructors and destructors. + explicit SilentNetworkAccessManager(QObject* parent = 0); + virtual ~SilentNetworkAccessManager(); - // Returns pointer to global silent network manager - static SilentNetworkAccessManager *instance(); + // Returns pointer to global silent network manager + static SilentNetworkAccessManager* instance(); - public slots: - // This cannot do any GUI stuff. - void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); + public slots: + // This cannot do any GUI stuff. + void onAuthenticationRequired(QNetworkReply* reply, QAuthenticator* authenticator); - private: - static QPointer s_instance; + private: + static QPointer s_instance; }; #endif // SILENTNETWORKACCESSMANAGER_H diff --git a/src/network-web/urlinterceptor.h b/src/network-web/urlinterceptor.h index 50ba1435c..12f4e43f3 100755 --- a/src/network-web/urlinterceptor.h +++ b/src/network-web/urlinterceptor.h @@ -25,11 +25,11 @@ class UrlInterceptor : public QObject { - Q_OBJECT + Q_OBJECT - public: - explicit UrlInterceptor(QObject *parent = Q_NULLPTR) : QObject(parent) { } - virtual void interceptRequest(QWebEngineUrlRequestInfo &info) = 0; + public: + explicit UrlInterceptor(QObject* parent = Q_NULLPTR) : QObject(parent) { } + virtual void interceptRequest(QWebEngineUrlRequestInfo& info) = 0; }; #endif // URLINTERCEPTOR_H diff --git a/src/network-web/webfactory.cpp b/src/network-web/webfactory.cpp index b24bae77a..3d2bcbee3 100755 --- a/src/network-web/webfactory.cpp +++ b/src/network-web/webfactory.cpp @@ -1,160 +1,158 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "network-web/webfactory.h" - -#include "miscellaneous/application.h" - -#include -#include -#include -#include - - -QPointer WebFactory::s_instance; - -WebFactory::WebFactory(QObject *parent) - : QObject(parent), m_escapes(QMap()), - m_deEscapes(QMap()) { -} - -WebFactory::~WebFactory() { -} - -bool WebFactory::sendMessageViaEmail(const Message &message) { - if (qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalEmailEnabled)).toBool()) { - const QString browser = qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalEmailExecutable)).toString(); - const QString arguments = qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalEmailArguments)).toString(); - - return QProcess::startDetached(QString("\"") + browser + QSL("\" ") + arguments.arg(message.m_title, - stripTags(message.m_contents))); - } - else { - // Send it via mailto protocol. - // NOTE: http://en.wikipedia.org/wiki/Mailto - return QDesktopServices::openUrl(QString("mailto:?subject=%1&body=%2").arg(QString(QUrl::toPercentEncoding(message.m_title)), - QString(QUrl::toPercentEncoding(stripTags(message.m_contents))))); - } -} - -bool WebFactory::openUrlInExternalBrowser(const QString &url) { - if (qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalBrowserEnabled)).toBool()) { - const QString browser = qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalBrowserExecutable)).toString(); - const QString arguments = qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalBrowserArguments)).toString(); - - const QString call_line = "\"" + browser + "\" \"" + arguments.arg(url) + "\""; - - qDebug("Running command '%s'.", qPrintable(call_line)); - - const bool result = QProcess::startDetached(call_line); - - if (!result) { - qDebug("External web browser call failed."); - } - - return result; - } - else { - return QDesktopServices::openUrl(url); - } -} - -WebFactory *WebFactory::instance() { - if (s_instance.isNull()) { - s_instance = new WebFactory(qApp); - } - - return s_instance; -} - -QString WebFactory::stripTags(QString text) { - return text.remove(QRegExp(QSL("<[^>]*>"))); -} - -QString WebFactory::escapeHtml(const QString &html) { - if (m_escapes.isEmpty()) { - generetaEscapes(); - } - - QString output = html; - QMapIterator i(m_escapes); - - while (i.hasNext()) { - i.next(); - output = output.replace(i.key(), i.value()); - } - - return output; -} - -QString WebFactory::deEscapeHtml(const QString &text) { - if (m_deEscapes.isEmpty()) { - generateDeescapes(); - } - - QString output = text; - QMapIterator i(m_deEscapes); - - while (i.hasNext()) { - i.next(); - output = output.replace(i.key(), i.value()); - } - - return output; -} - -QString WebFactory::toSecondLevelDomain(const QUrl &url) { - const QString top_level_domain = url.topLevelDomain(); - const QString url_host = url.host(); - - if (top_level_domain.isEmpty() || url_host.isEmpty()) { - return QString(); - } - - QString domain = url_host.left(url_host.size() - top_level_domain.size()); - - if (domain.count(QL1C('.')) == 0) { - return url_host; - } - - while (domain.count(QL1C('.')) != 0) { - domain = domain.mid(domain.indexOf(QL1C('.')) + 1); - } - - return domain + top_level_domain; -} - -void WebFactory::generetaEscapes() { - m_escapes[QSL("<")] = QL1C('<'); - m_escapes[QSL(">")] = QL1C('>'); - m_escapes[QSL("&")] = QL1C('&'); - m_escapes[QSL(""")] = QL1C('\"'); - m_escapes[QSL(" ")] = QL1C(' '); - m_escapes[QSL("±")] = QSL("±"); - m_escapes[QSL("×")] = QSL("×"); - m_escapes[QSL("'")] = QL1C('\''); -} - -void WebFactory::generateDeescapes() { - m_deEscapes[QSL("<")] = QSL("<"); - m_deEscapes[QSL(">")] = QSL(">"); - m_deEscapes[QSL("&")] = QSL("&"); - m_deEscapes[QSL("\"")] = QSL("""); - m_deEscapes[QSL("±")] = QSL("±"); - m_deEscapes[QSL("×")] = QSL("×"); - m_deEscapes[QSL("\'")] = QSL("'"); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "network-web/webfactory.h" + +#include "miscellaneous/application.h" + +#include +#include +#include +#include + + +QPointer WebFactory::s_instance; + +WebFactory::WebFactory(QObject* parent) + : QObject(parent), m_escapes(QMap()), + m_deEscapes(QMap()) { +} + +WebFactory::~WebFactory() { +} + +bool WebFactory::sendMessageViaEmail(const Message& message) { + if (qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalEmailEnabled)).toBool()) { + const QString browser = qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalEmailExecutable)).toString(); + const QString arguments = qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalEmailArguments)).toString(); + return QProcess::startDetached(QString("\"") + browser + QSL("\" ") + arguments.arg(message.m_title, + stripTags(message.m_contents))); + } + + else { + // Send it via mailto protocol. + // NOTE: http://en.wikipedia.org/wiki/Mailto + return QDesktopServices::openUrl(QString("mailto:?subject=%1&body=%2").arg(QString(QUrl::toPercentEncoding(message.m_title)), + QString(QUrl::toPercentEncoding(stripTags(message.m_contents))))); + } +} + +bool WebFactory::openUrlInExternalBrowser(const QString& url) { + if (qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalBrowserEnabled)).toBool()) { + const QString browser = qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalBrowserExecutable)).toString(); + const QString arguments = qApp->settings()->value(GROUP(Browser), SETTING(Browser::CustomExternalBrowserArguments)).toString(); + const QString call_line = "\"" + browser + "\" \"" + arguments.arg(url) + "\""; + qDebug("Running command '%s'.", qPrintable(call_line)); + const bool result = QProcess::startDetached(call_line); + + if (!result) { + qDebug("External web browser call failed."); + } + + return result; + } + + else { + return QDesktopServices::openUrl(url); + } +} + +WebFactory* WebFactory::instance() { + if (s_instance.isNull()) { + s_instance = new WebFactory(qApp); + } + + return s_instance; +} + +QString WebFactory::stripTags(QString text) { + return text.remove(QRegExp(QSL("<[^>]*>"))); +} + +QString WebFactory::escapeHtml(const QString& html) { + if (m_escapes.isEmpty()) { + generetaEscapes(); + } + + QString output = html; + QMapIterator i(m_escapes); + + while (i.hasNext()) { + i.next(); + output = output.replace(i.key(), i.value()); + } + + return output; +} + +QString WebFactory::deEscapeHtml(const QString& text) { + if (m_deEscapes.isEmpty()) { + generateDeescapes(); + } + + QString output = text; + QMapIterator i(m_deEscapes); + + while (i.hasNext()) { + i.next(); + output = output.replace(i.key(), i.value()); + } + + return output; +} + +QString WebFactory::toSecondLevelDomain(const QUrl& url) { + const QString top_level_domain = url.topLevelDomain(); + const QString url_host = url.host(); + + if (top_level_domain.isEmpty() || url_host.isEmpty()) { + return QString(); + } + + QString domain = url_host.left(url_host.size() - top_level_domain.size()); + + if (domain.count(QL1C('.')) == 0) { + return url_host; + } + + while (domain.count(QL1C('.')) != 0) { + domain = domain.mid(domain.indexOf(QL1C('.')) + 1); + } + + return domain + top_level_domain; +} + +void WebFactory::generetaEscapes() { + m_escapes[QSL("<")] = QL1C('<'); + m_escapes[QSL(">")] = QL1C('>'); + m_escapes[QSL("&")] = QL1C('&'); + m_escapes[QSL(""")] = QL1C('\"'); + m_escapes[QSL(" ")] = QL1C(' '); + m_escapes[QSL("±")] = QSL("±"); + m_escapes[QSL("×")] = QSL("×"); + m_escapes[QSL("'")] = QL1C('\''); +} + +void WebFactory::generateDeescapes() { + m_deEscapes[QSL("<")] = QSL("<"); + m_deEscapes[QSL(">")] = QSL(">"); + m_deEscapes[QSL("&")] = QSL("&"); + m_deEscapes[QSL("\"")] = QSL("""); + m_deEscapes[QSL("±")] = QSL("±"); + m_deEscapes[QSL("×")] = QSL("×"); + m_deEscapes[QSL("\'")] = QSL("'"); +} diff --git a/src/network-web/webfactory.h b/src/network-web/webfactory.h index 069aae789..b27189e50 100755 --- a/src/network-web/webfactory.h +++ b/src/network-web/webfactory.h @@ -1,72 +1,72 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef WEBFACTORY_H -#define WEBFACTORY_H - -#include - -#include "core/messagesmodel.h" - -#include -#include - - -class QWebEngineSettings; - -class WebFactory : public QObject { - Q_OBJECT - - public: - // Destructor. - virtual ~WebFactory(); - - // Strips "<....>" (HTML, XML) tags from given text. - QString stripTags(QString text); - - // HTML entity escaping. - QString escapeHtml(const QString &html); - QString deEscapeHtml(const QString &text); - - // BUG: Version for Qt < 4.8 has one issue, it will wrongly - // count .co.uk (and others) as second-level domain - QString toSecondLevelDomain(const QUrl &url); - - // Singleton getter. - static WebFactory *instance(); - - public slots: - // Opens given string URL in external browser. - bool openUrlInExternalBrowser(const QString &url); - bool sendMessageViaEmail(const Message &message); - - private: - // Constructor. - explicit WebFactory(QObject *parent = 0); - - // Escape sequences generators. - void generetaEscapes(); - void generateDeescapes(); - - QMap m_escapes; - QMap m_deEscapes; - - // Singleton. - static QPointer s_instance; -}; - -#endif // WEBFACTORY_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef WEBFACTORY_H +#define WEBFACTORY_H + +#include + +#include "core/messagesmodel.h" + +#include +#include + + +class QWebEngineSettings; + +class WebFactory : public QObject { + Q_OBJECT + + public: + // Destructor. + virtual ~WebFactory(); + + // Strips "<....>" (HTML, XML) tags from given text. + QString stripTags(QString text); + + // HTML entity escaping. + QString escapeHtml(const QString& html); + QString deEscapeHtml(const QString& text); + + // BUG: Version for Qt < 4.8 has one issue, it will wrongly + // count .co.uk (and others) as second-level domain + QString toSecondLevelDomain(const QUrl& url); + + // Singleton getter. + static WebFactory* instance(); + + public slots: + // Opens given string URL in external browser. + bool openUrlInExternalBrowser(const QString& url); + bool sendMessageViaEmail(const Message& message); + + private: + // Constructor. + explicit WebFactory(QObject* parent = 0); + + // Escape sequences generators. + void generetaEscapes(); + void generateDeescapes(); + + QMap m_escapes; + QMap m_deEscapes; + + // Singleton. + static QPointer s_instance; +}; + +#endif // WEBFACTORY_H diff --git a/src/network-web/webpage.cpp b/src/network-web/webpage.cpp index 25607cc9a..eebd64a88 100755 --- a/src/network-web/webpage.cpp +++ b/src/network-web/webpage.cpp @@ -1,71 +1,77 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "network-web/webpage.h" - -#include "definitions/definitions.h" -#include "gui/webviewer.h" - -#include -#include - - -WebPage::WebPage(QObject *parent) : QWebEnginePage(parent) { - setBackgroundColor(Qt::transparent); -} - -WebViewer *WebPage::view() const { - return qobject_cast(QWebEnginePage::view()); -} - -void WebPage::javaScriptAlert(const QUrl &securityOrigin, const QString &msg) { - QStringList parts = msg.split(QL1C('-')); - - if (parts.size() == 2) { - int message_id = parts.at(0).toInt(); - QString action = parts.at(1); - - if (action == QSL("read")) { - emit messageStatusChangeRequested(message_id, MarkRead); - } - else if (action == QSL("unread")) { - emit messageStatusChangeRequested(message_id, MarkUnread); - } - else if (action == QSL("starred")) { - emit messageStatusChangeRequested(message_id, MarkStarred); - } - else if (action == QSL("unstarred")) { - emit messageStatusChangeRequested(message_id, MarkUnstarred); - } - else { - QWebEnginePage::javaScriptAlert(securityOrigin, msg); - } - } - else { - QWebEnginePage::javaScriptAlert(securityOrigin, msg); - } -} - -bool WebPage::acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame) { - if (url.host() == INTERNAL_URL_MESSAGE_HOST) { - setHtml(view()->messageContents(), QUrl(INTERNAL_URL_MESSAGE)); - return true; - } - else { - return QWebEnginePage::acceptNavigationRequest(url, type, isMainFrame); - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "network-web/webpage.h" + +#include "definitions/definitions.h" +#include "gui/webviewer.h" + +#include +#include + + +WebPage::WebPage(QObject* parent) : QWebEnginePage(parent) { + setBackgroundColor(Qt::transparent); +} + +WebViewer* WebPage::view() const { + return qobject_cast(QWebEnginePage::view()); +} + +void WebPage::javaScriptAlert(const QUrl& securityOrigin, const QString& msg) { + QStringList parts = msg.split(QL1C('-')); + + if (parts.size() == 2) { + int message_id = parts.at(0).toInt(); + QString action = parts.at(1); + + if (action == QSL("read")) { + emit messageStatusChangeRequested(message_id, MarkRead); + } + + else if (action == QSL("unread")) { + emit messageStatusChangeRequested(message_id, MarkUnread); + } + + else if (action == QSL("starred")) { + emit messageStatusChangeRequested(message_id, MarkStarred); + } + + else if (action == QSL("unstarred")) { + emit messageStatusChangeRequested(message_id, MarkUnstarred); + } + + else { + QWebEnginePage::javaScriptAlert(securityOrigin, msg); + } + } + + else { + QWebEnginePage::javaScriptAlert(securityOrigin, msg); + } +} + +bool WebPage::acceptNavigationRequest(const QUrl& url, NavigationType type, bool isMainFrame) { + if (url.host() == INTERNAL_URL_MESSAGE_HOST) { + setHtml(view()->messageContents(), QUrl(INTERNAL_URL_MESSAGE)); + return true; + } + + else { + return QWebEnginePage::acceptNavigationRequest(url, type, isMainFrame); + } +} diff --git a/src/network-web/webpage.h b/src/network-web/webpage.h index 665bcf2ee..3d6c7c68c 100755 --- a/src/network-web/webpage.h +++ b/src/network-web/webpage.h @@ -1,49 +1,49 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef WEBPAGE_H -#define WEBPAGE_H - -#include - - -class WebViewer; - -class WebPage : public QWebEnginePage { - Q_OBJECT - - public: - enum MessageStatusChange { - MarkRead, - MarkUnread, - MarkStarred, - MarkUnstarred - }; - - explicit WebPage(QObject *parent = 0); - - WebViewer *view() const; - - protected: - void javaScriptAlert(const QUrl &securityOrigin, const QString &msg); - bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame); - - signals: - void messageStatusChangeRequested(int message_id, WebPage::MessageStatusChange change); -}; - -#endif // WEBPAGE_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef WEBPAGE_H +#define WEBPAGE_H + +#include + + +class WebViewer; + +class WebPage : public QWebEnginePage { + Q_OBJECT + + public: + enum MessageStatusChange { + MarkRead, + MarkUnread, + MarkStarred, + MarkUnstarred + }; + + explicit WebPage(QObject* parent = 0); + + WebViewer* view() const; + + protected: + void javaScriptAlert(const QUrl& securityOrigin, const QString& msg); + bool acceptNavigationRequest(const QUrl& url, NavigationType type, bool isMainFrame); + + signals: + void messageStatusChangeRequested(int message_id, WebPage::MessageStatusChange change); +}; + +#endif // WEBPAGE_H diff --git a/src/qtsingleapplication/qtlocalpeer.cpp b/src/qtsingleapplication/qtlocalpeer.cpp index cd7e38cd8..694da68a1 100755 --- a/src/qtsingleapplication/qtlocalpeer.cpp +++ b/src/qtsingleapplication/qtlocalpeer.cpp @@ -47,7 +47,7 @@ #if defined(Q_OS_WIN) #include #include -typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); +typedef BOOL(WINAPI* PProcessIdToSessionId)(DWORD, DWORD*); static PProcessIdToSessionId pProcessIdToSessionId = 0; #endif #if defined(Q_OS_UNIX) || defined(Q_OS_OS2) @@ -67,144 +67,168 @@ namespace QtLP_Private { const char* QtLocalPeer::ack = "ack"; -QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) - : QObject(parent), id(appId) -{ - QString prefix = id; - if (id.isEmpty()) { - id = QCoreApplication::applicationFilePath(); +QtLocalPeer::QtLocalPeer(QObject* parent, const QString& appId) + : QObject(parent), id(appId) { + QString prefix = id; + + if (id.isEmpty()) { + id = QCoreApplication::applicationFilePath(); #if defined(Q_OS_WIN) - id = id.toLower(); + id = id.toLower(); #endif - prefix = id.section(QLatin1Char('/'), -1); - } - prefix.remove(QRegExp("[^a-zA-Z]")); - prefix.truncate(6); - - QByteArray idc = id.toUtf8(); - quint16 idNum = qChecksum(idc.constData(), idc.size()); - socketName = QLatin1String("qtsingleapp-") + prefix - + QLatin1Char('-') + QString::number(idNum, 16); + prefix = id.section(QLatin1Char('/'), -1); + } + prefix.remove(QRegExp("[^a-zA-Z]")); + prefix.truncate(6); + QByteArray idc = id.toUtf8(); + quint16 idNum = qChecksum(idc.constData(), idc.size()); + socketName = QLatin1String("qtsingleapp-") + prefix + + QLatin1Char('-') + QString::number(idNum, 16); #if defined(Q_OS_WIN) - if (!pProcessIdToSessionId) { - QLibrary lib("kernel32"); - pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); - } - if (pProcessIdToSessionId) { - DWORD sessionId = 0; - pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); - socketName += QLatin1Char('-') + QString::number(sessionId, 16); - } + + if (!pProcessIdToSessionId) { + QLibrary lib("kernel32"); + pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); + } + + if (pProcessIdToSessionId) { + DWORD sessionId = 0; + pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); + socketName += QLatin1Char('-') + QString::number(sessionId, 16); + } + #else - socketName += QLatin1Char('-') + QString::number(::getuid(), 16); + socketName += QLatin1Char('-') + QString::number(::getuid(), 16); #endif - - server = new QLocalServer(this); - QString lockName = QDir(QDir::tempPath()).absolutePath() - + QLatin1Char('/') + socketName - + QLatin1String("-lockfile"); - lockFile.setFileName(lockName); - lockFile.open(QIODevice::ReadWrite); + server = new QLocalServer(this); + QString lockName = QDir(QDir::tempPath()).absolutePath() + + QLatin1Char('/') + socketName + + QLatin1String("-lockfile"); + lockFile.setFileName(lockName); + lockFile.open(QIODevice::ReadWrite); } QtLocalPeer::~QtLocalPeer() { - if (server != nullptr) { - server->close(); - } + if (server != nullptr) { + server->close(); + } } -bool QtLocalPeer::isClient() -{ - if (lockFile.isLocked()) - return false; +bool QtLocalPeer::isClient() { + if (lockFile.isLocked()) { + return false; + } - if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) - return true; + if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) { + return true; + } - bool res = server->listen(socketName); + bool res = server->listen(socketName); #if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) - // ### Workaround - if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { - QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); - res = server->listen(socketName); - } + + // ### Workaround + if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { + QFile::remove(QDir::cleanPath(QDir::tempPath()) + QLatin1Char('/') + socketName); + res = server->listen(socketName); + } + #endif - if (!res) - qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); - QObject::connect(server, &QLocalServer::newConnection, this, &QtLocalPeer::receiveConnection); - return false; + + if (!res) { + qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); + } + + QObject::connect(server, &QLocalServer::newConnection, this, &QtLocalPeer::receiveConnection); + return false; } -bool QtLocalPeer::sendMessage(const QString &message, int timeout) -{ - if (!isClient()) - return false; +bool QtLocalPeer::sendMessage(const QString& message, int timeout) { + if (!isClient()) { + return false; + } - QLocalSocket socket; - bool connOk = false; - for(int i = 0; i < 2; i++) { - // Try twice, in case the other instance is just starting up - socket.connectToServer(socketName); - connOk = socket.waitForConnected(timeout/2); - if (connOk || i) - break; - int ms = 250; + QLocalSocket socket; + bool connOk = false; + + for (int i = 0; i < 2; i++) { + // Try twice, in case the other instance is just starting up + socket.connectToServer(socketName); + connOk = socket.waitForConnected(timeout / 2); + + if (connOk || i) { + break; + } + + int ms = 250; #if defined(Q_OS_WIN) - Sleep(DWORD(ms)); + Sleep(DWORD(ms)); #else - struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; - nanosleep(&ts, nullptr); + struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; + nanosleep(&ts, nullptr); #endif - } - if (!connOk) - return false; + } - QByteArray uMsg(message.toUtf8()); - QDataStream ds(&socket); - ds.writeBytes(uMsg.constData(), uMsg.size()); - bool res = socket.waitForBytesWritten(timeout); - if (res) { - res &= socket.waitForReadyRead(timeout); // wait for ack - if (res) - res &= (socket.read(qstrlen(ack)) == ack); - } - return res; + if (!connOk) { + return false; + } + + QByteArray uMsg(message.toUtf8()); + QDataStream ds(&socket); + ds.writeBytes(uMsg.constData(), uMsg.size()); + bool res = socket.waitForBytesWritten(timeout); + + if (res) { + res &= socket.waitForReadyRead(timeout); // wait for ack + + if (res) { + res &= (socket.read(qstrlen(ack)) == ack); + } + } + + return res; } -void QtLocalPeer::receiveConnection() -{ - QLocalSocket* socket = server->nextPendingConnection(); - if (!socket) - return; +void QtLocalPeer::receiveConnection() { + QLocalSocket* socket = server->nextPendingConnection(); - while (socket->bytesAvailable() < (int)sizeof(quint32)) - socket->waitForReadyRead(); - QDataStream ds(socket); - QByteArray uMsg; - quint32 remaining; - ds >> remaining; - uMsg.resize(remaining); - int got = 0; - char* uMsgBuf = uMsg.data(); - do { - got = ds.readRawData(uMsgBuf, remaining); - remaining -= got; - uMsgBuf += got; - } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); - if (got < 0) { - qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); - delete socket; - return; - } - QString message(QString::fromUtf8(uMsg)); - socket->write(ack, qstrlen(ack)); - socket->waitForBytesWritten(1000); - socket->waitForDisconnected(1000); // make sure client reads ack - delete socket; - emit messageReceived(message); //### (might take a long time to return) + if (!socket) { + return; + } + + while (socket->bytesAvailable() < (int)sizeof(quint32)) { + socket->waitForReadyRead(); + } + + QDataStream ds(socket); + QByteArray uMsg; + quint32 remaining; + ds >> remaining; + uMsg.resize(remaining); + int got = 0; + char* uMsgBuf = uMsg.data(); + + do { + got = ds.readRawData(uMsgBuf, remaining); + remaining -= got; + uMsgBuf += got; + } + while (remaining && got >= 0 && socket->waitForReadyRead(2000)); + + if (got < 0) { + qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); + delete socket; + return; + } + + QString message(QString::fromUtf8(uMsg)); + socket->write(ack, qstrlen(ack)); + socket->waitForBytesWritten(1000); + socket->waitForDisconnected(1000); // make sure client reads ack + delete socket; + emit messageReceived(message); //### (might take a long time to return) } diff --git a/src/qtsingleapplication/qtlocalpeer.h b/src/qtsingleapplication/qtlocalpeer.h old mode 100644 new mode 100755 index 807a44ce4..bd61252b2 --- a/src/qtsingleapplication/qtlocalpeer.h +++ b/src/qtsingleapplication/qtlocalpeer.h @@ -47,32 +47,32 @@ #include "qtlockedfile.h" -class QtLocalPeer : public QObject -{ - Q_OBJECT +class QtLocalPeer : public QObject { + Q_OBJECT -public: - QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); - ~QtLocalPeer(); - bool isClient(); - bool sendMessage(const QString &message, int timeout); - QString applicationId() const - { return id; } + public: + QtLocalPeer(QObject* parent = 0, const QString& appId = QString()); + ~QtLocalPeer(); + bool isClient(); + bool sendMessage(const QString& message, int timeout); + QString applicationId() const { + return id; + } -Q_SIGNALS: - void messageReceived(const QString &message); + Q_SIGNALS: + void messageReceived(const QString& message); -protected Q_SLOTS: - void receiveConnection(); + protected Q_SLOTS: + void receiveConnection(); -public: - QString id; - QString socketName; - QLocalServer* server; - QtLP_Private::QtLockedFile lockFile; + public: + QString id; + QString socketName; + QLocalServer* server; + QtLP_Private::QtLockedFile lockFile; -private: - static const char* ack; + private: + static const char* ack; }; #endif // QTLOCALPEER_H diff --git a/src/qtsingleapplication/qtlockedfile.cpp b/src/qtsingleapplication/qtlockedfile.cpp old mode 100644 new mode 100755 index c142a863a..35def70e9 --- a/src/qtsingleapplication/qtlockedfile.cpp +++ b/src/qtsingleapplication/qtlockedfile.cpp @@ -81,13 +81,12 @@ \sa QFile::QFile() */ QtLockedFile::QtLockedFile() - : QFile() -{ + : QFile() { #ifdef Q_OS_WIN - wmutex = 0; - rmutex = 0; + wmutex = 0; + rmutex = 0; #endif - m_lock_mode = NoLock; + m_lock_mode = NoLock; } /*! @@ -97,14 +96,13 @@ QtLockedFile::QtLockedFile() \sa QFile::QFile() */ -QtLockedFile::QtLockedFile(const QString &name) - : QFile(name) -{ +QtLockedFile::QtLockedFile(const QString& name) + : QFile(name) { #ifdef Q_OS_WIN - wmutex = 0; - rmutex = 0; + wmutex = 0; + rmutex = 0; #endif - m_lock_mode = NoLock; + m_lock_mode = NoLock; } /*! @@ -120,13 +118,13 @@ QtLockedFile::QtLockedFile(const QString &name) \sa QFile::open(), QFile::resize() */ -bool QtLockedFile::open(OpenMode mode) -{ - if (mode & QIODevice::Truncate) { - qWarning("QtLockedFile::open(): Truncate mode not allowed."); - return false; - } - return QFile::open(mode); +bool QtLockedFile::open(OpenMode mode) { + if (mode & QIODevice::Truncate) { + qWarning("QtLockedFile::open(): Truncate mode not allowed."); + return false; + } + + return QFile::open(mode); } /*! @@ -135,9 +133,8 @@ bool QtLockedFile::open(OpenMode mode) \sa lockMode() */ -bool QtLockedFile::isLocked() const -{ - return m_lock_mode != NoLock; +bool QtLockedFile::isLocked() const { + return m_lock_mode != NoLock; } /*! @@ -146,9 +143,8 @@ bool QtLockedFile::isLocked() const \sa isLocked() */ -QtLockedFile::LockMode QtLockedFile::lockMode() const -{ - return m_lock_mode; +QtLockedFile::LockMode QtLockedFile::lockMode() const { + return m_lock_mode; } /*! diff --git a/src/qtsingleapplication/qtlockedfile.h b/src/qtsingleapplication/qtlockedfile.h old mode 100644 new mode 100755 index cbcd0cc78..bb9449d4e --- a/src/qtsingleapplication/qtlockedfile.h +++ b/src/qtsingleapplication/qtlockedfile.h @@ -64,36 +64,35 @@ namespace QtLP_Private { -class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile -{ - Q_OBJECT + class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile { + Q_OBJECT -public: - enum LockMode { NoLock = 0, ReadLock, WriteLock }; + public: + enum LockMode { NoLock = 0, ReadLock, WriteLock }; - QtLockedFile(); - QtLockedFile(const QString &name); - ~QtLockedFile(); + QtLockedFile(); + QtLockedFile(const QString& name); + ~QtLockedFile(); - bool open(OpenMode mode); + bool open(OpenMode mode); - bool lock(LockMode mode, bool block = true); - bool unlock(); - bool isLocked() const; - LockMode lockMode() const; + bool lock(LockMode mode, bool block = true); + bool unlock(); + bool isLocked() const; + LockMode lockMode() const; -private: + private: #ifdef Q_OS_WIN - Qt::HANDLE wmutex; - Qt::HANDLE rmutex; - QVector rmutexes; - QString mutexname; + Qt::HANDLE wmutex; + Qt::HANDLE rmutex; + QVector rmutexes; + QString mutexname; - Qt::HANDLE getMutexHandle(int idx, bool doCreate); - bool waitMutex(Qt::HANDLE mutex, bool doBlock); + Qt::HANDLE getMutexHandle(int idx, bool doCreate); + bool waitMutex(Qt::HANDLE mutex, bool doBlock); #endif - LockMode m_lock_mode; -}; + LockMode m_lock_mode; + }; } #endif diff --git a/src/qtsingleapplication/qtlockedfile_unix.cpp b/src/qtsingleapplication/qtlockedfile_unix.cpp old mode 100644 new mode 100755 index 976c1b9ee..58a0f6f9a --- a/src/qtsingleapplication/qtlockedfile_unix.cpp +++ b/src/qtsingleapplication/qtlockedfile_unix.cpp @@ -45,71 +45,85 @@ #include "qtlockedfile.h" -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } - - if (mode == NoLock) - return unlock(); - - if (mode == m_lock_mode) - return true; +bool QtLockedFile::lock(LockMode mode, bool block) { + if (!isOpen()) { + qWarning("QtLockedFile::lock(): file is not opened"); + return false; + } - if (m_lock_mode != NoLock) - unlock(); + if (mode == NoLock) { + return unlock(); + } - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; - int cmd = block ? F_SETLKW : F_SETLK; - int ret = fcntl(handle(), cmd, &fl); - - if (ret == -1) { - if (errno != EINTR && errno != EAGAIN) - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } + if (mode == m_lock_mode) { + return true; + } - - m_lock_mode = mode; - return true; + if (m_lock_mode != NoLock) { + unlock(); + } + + struct flock fl; + + fl.l_whence = SEEK_SET; + + fl.l_start = 0; + + fl.l_len = 0; + + fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; + + int cmd = block ? F_SETLKW : F_SETLK; + + int ret = fcntl(handle(), cmd, &fl); + + if (ret == -1) { + if (errno != EINTR && errno != EAGAIN) { + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + } + + return false; + } + + m_lock_mode = mode; + return true; } -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } +bool QtLockedFile::unlock() { + if (!isOpen()) { + qWarning("QtLockedFile::unlock(): file is not opened"); + return false; + } - if (!isLocked()) - return true; + if (!isLocked()) { + return true; + } - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = F_UNLCK; - int ret = fcntl(handle(), F_SETLKW, &fl); - - if (ret == -1) { - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } - - m_lock_mode = NoLock; - return true; + struct flock fl; + + fl.l_whence = SEEK_SET; + + fl.l_start = 0; + + fl.l_len = 0; + + fl.l_type = F_UNLCK; + + int ret = fcntl(handle(), F_SETLKW, &fl); + + if (ret == -1) { + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + return false; + } + + m_lock_mode = NoLock; + return true; } -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); +QtLockedFile::~QtLockedFile() { + if (isOpen()) { + unlock(); + } } diff --git a/src/qtsingleapplication/qtlockedfile_win.cpp b/src/qtsingleapplication/qtlockedfile_win.cpp old mode 100644 new mode 100755 index 5e2126204..5ffae9f66 --- a/src/qtsingleapplication/qtlockedfile_win.cpp +++ b/src/qtsingleapplication/qtlockedfile_win.cpp @@ -50,162 +50,203 @@ #define QT_WA(unicode, ansi) unicode #endif -Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) -{ - if (mutexname.isEmpty()) { - QFileInfo fi(*this); - mutexname = QString::fromLatin1(MUTEX_PREFIX) - + fi.absoluteFilePath().toLower(); - } - QString mname(mutexname); - if (idx >= 0) - mname += QString::number(idx); +Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) { + if (mutexname.isEmpty()) { + QFileInfo fi(*this); + mutexname = QString::fromLatin1(MUTEX_PREFIX) + + fi.absoluteFilePath().toLower(); + } - Qt::HANDLE mutex; - if (doCreate) { - QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); - return 0; - } - } - else { - QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - if (GetLastError() != ERROR_FILE_NOT_FOUND) - qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); - return 0; - } - } - return mutex; + QString mname(mutexname); + + if (idx >= 0) { + mname += QString::number(idx); + } + + Qt::HANDLE mutex; + + if (doCreate) { + QT_WA({ mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, + { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); }); + + if (!mutex) { + qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); + return 0; + } + } + + else { + QT_WA({ mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, + { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); }); + + if (!mutex) { + if (GetLastError() != ERROR_FILE_NOT_FOUND) { + qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); + } + + return 0; + } + } + + return mutex; } -bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) -{ - Q_ASSERT(mutex); - DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); - switch (res) { - case WAIT_OBJECT_0: - case WAIT_ABANDONED: - return true; - break; - case WAIT_TIMEOUT: - break; - default: - qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); - } - return false; +bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) { + Q_ASSERT(mutex); + DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); + + switch (res) { + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + return true; + break; + + case WAIT_TIMEOUT: + break; + + default: + qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); + } + + return false; } -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } +bool QtLockedFile::lock(LockMode mode, bool block) { + if (!isOpen()) { + qWarning("QtLockedFile::lock(): file is not opened"); + return false; + } - if (mode == NoLock) - return unlock(); + if (mode == NoLock) { + return unlock(); + } - if (mode == m_lock_mode) - return true; + if (mode == m_lock_mode) { + return true; + } - if (m_lock_mode != NoLock) - unlock(); + if (m_lock_mode != NoLock) { + unlock(); + } - if (!wmutex && !(wmutex = getMutexHandle(-1, true))) - return false; + if (!wmutex && !(wmutex = getMutexHandle(-1, true))) { + return false; + } - if (!waitMutex(wmutex, block)) - return false; + if (!waitMutex(wmutex, block)) { + return false; + } - if (mode == ReadLock) { - int idx = 0; - for (; idx < MAX_READERS; idx++) { - rmutex = getMutexHandle(idx, false); - if (!rmutex || waitMutex(rmutex, false)) - break; - CloseHandle(rmutex); - } - bool ok = true; - if (idx >= MAX_READERS) { - qWarning("QtLockedFile::lock(): too many readers"); - rmutex = 0; - ok = false; - } - else if (!rmutex) { - rmutex = getMutexHandle(idx, true); - if (!rmutex || !waitMutex(rmutex, false)) - ok = false; - } - if (!ok && rmutex) { - CloseHandle(rmutex); - rmutex = 0; - } - ReleaseMutex(wmutex); - if (!ok) - return false; - } - else { - Q_ASSERT(rmutexes.isEmpty()); - for (int i = 0; i < MAX_READERS; i++) { - Qt::HANDLE mutex = getMutexHandle(i, false); - if (mutex) - rmutexes.append(mutex); - } - if (rmutexes.size()) { - DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), - TRUE, block ? INFINITE : 0); - if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { - if (res != WAIT_TIMEOUT) - qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); - m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky - unlock(); - return false; - } - } - } + if (mode == ReadLock) { + int idx = 0; - m_lock_mode = mode; - return true; + for (; idx < MAX_READERS; idx++) { + rmutex = getMutexHandle(idx, false); + + if (!rmutex || waitMutex(rmutex, false)) { + break; + } + + CloseHandle(rmutex); + } + + bool ok = true; + + if (idx >= MAX_READERS) { + qWarning("QtLockedFile::lock(): too many readers"); + rmutex = 0; + ok = false; + } + + else if (!rmutex) { + rmutex = getMutexHandle(idx, true); + + if (!rmutex || !waitMutex(rmutex, false)) { + ok = false; + } + } + + if (!ok && rmutex) { + CloseHandle(rmutex); + rmutex = 0; + } + + ReleaseMutex(wmutex); + + if (!ok) { + return false; + } + } + + else { + Q_ASSERT(rmutexes.isEmpty()); + + for (int i = 0; i < MAX_READERS; i++) { + Qt::HANDLE mutex = getMutexHandle(i, false); + + if (mutex) { + rmutexes.append(mutex); + } + } + + if (rmutexes.size()) { + DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), + TRUE, block ? INFINITE : 0); + + if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { + if (res != WAIT_TIMEOUT) { + qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); + } + + m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky + unlock(); + return false; + } + } + } + + m_lock_mode = mode; + return true; } -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } +bool QtLockedFile::unlock() { + if (!isOpen()) { + qWarning("QtLockedFile::unlock(): file is not opened"); + return false; + } - if (!isLocked()) - return true; + if (!isLocked()) { + return true; + } - if (m_lock_mode == ReadLock) { - ReleaseMutex(rmutex); - CloseHandle(rmutex); - rmutex = 0; - } - else { - foreach(Qt::HANDLE mutex, rmutexes) { - ReleaseMutex(mutex); - CloseHandle(mutex); - } - rmutexes.clear(); - ReleaseMutex(wmutex); - } + if (m_lock_mode == ReadLock) { + ReleaseMutex(rmutex); + CloseHandle(rmutex); + rmutex = 0; + } - m_lock_mode = QtLockedFile::NoLock; - return true; + else { + foreach (Qt::HANDLE mutex, rmutexes) { + ReleaseMutex(mutex); + CloseHandle(mutex); + } + + rmutexes.clear(); + ReleaseMutex(wmutex); + } + + m_lock_mode = QtLockedFile::NoLock; + return true; } -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); - if (wmutex) - CloseHandle(wmutex); +QtLockedFile::~QtLockedFile() { + if (isOpen()) { + unlock(); + } + + if (wmutex) { + CloseHandle(wmutex); + } } diff --git a/src/qtsingleapplication/qtsingleapplication.cpp b/src/qtsingleapplication/qtsingleapplication.cpp index 26e3f85ea..54d114f05 100755 --- a/src/qtsingleapplication/qtsingleapplication.cpp +++ b/src/qtsingleapplication/qtsingleapplication.cpp @@ -133,11 +133,10 @@ */ -void QtSingleApplication::sysInit(const QString &appId) -{ - actWin = 0; - peer = new QtLocalPeer(this, appId); - connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::messageReceived); +void QtSingleApplication::sysInit(const QString& appId) { + actWin = 0; + peer = new QtLocalPeer(this, appId); + connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::messageReceived); } @@ -151,10 +150,9 @@ void QtSingleApplication::sysInit(const QString &appId) QtSingleCoreApplication instead. */ -QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) - : QApplication(argc, argv, GUIenabled) -{ - sysInit(); +QtSingleApplication::QtSingleApplication(int& argc, char** argv, bool GUIenabled) + : QApplication(argc, argv, GUIenabled) { + sysInit(); } @@ -164,10 +162,9 @@ QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled QAppliation constructor. */ -QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) - : QApplication(argc, argv) -{ - sysInit(appId); +QtSingleApplication::QtSingleApplication(const QString& appId, int& argc, char** argv) + : QApplication(argc, argv) { + sysInit(appId); } #if QT_VERSION < 0x050000 @@ -177,10 +174,9 @@ QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char * will be QCoreApplication::applicationFilePath(). \a argc, \a argv, and \a type are passed on to the QAppliation constructor. */ -QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) - : QApplication(argc, argv, type) -{ - sysInit(); +QtSingleApplication::QtSingleApplication(int& argc, char** argv, Type type) + : QApplication(argc, argv, type) { + sysInit(); } @@ -192,9 +188,8 @@ QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) and \a cmap are passed on to the QApplication constructor. */ QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, visual, cmap) -{ - sysInit(); + : QApplication(dpy, visual, cmap) { + sysInit(); } /*! @@ -204,10 +199,9 @@ QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HA argv, \a visual, and \a cmap are passed on to the QApplication constructor. */ -QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(); +QtSingleApplication::QtSingleApplication(Display* dpy, int& argc, char** argv, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, argc, argv, visual, cmap) { + sysInit(); } /*! @@ -217,10 +211,9 @@ QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Q argv, \a visual, and \a cmap are passed on to the QApplication constructor. */ -QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(appId); +QtSingleApplication::QtSingleApplication(Display* dpy, const QString& appId, int argc, char** argv, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, argc, argv, visual, cmap) { + sysInit(appId); } # endif // Q_WS_X11 #endif // QT_VERSION < 0x050000 @@ -237,9 +230,8 @@ QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int \sa sendMessage() */ -bool QtSingleApplication::isRunning() -{ - return peer->isClient(); +bool QtSingleApplication::isRunning() { + return peer->isClient(); } @@ -256,9 +248,8 @@ bool QtSingleApplication::isRunning() \sa isRunning(), messageReceived() */ -bool QtSingleApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); +bool QtSingleApplication::sendMessage(const QString& message, int timeout) { + return peer->sendMessage(message, timeout); } @@ -266,9 +257,8 @@ bool QtSingleApplication::sendMessage(const QString &message, int timeout) Returns the application identifier. Two processes with the same identifier will be regarded as instances of the same application. */ -QString QtSingleApplication::id() const -{ - return peer->applicationId(); +QString QtSingleApplication::id() const { + return peer->applicationId(); } @@ -284,13 +274,16 @@ QString QtSingleApplication::id() const \sa activateWindow(), messageReceived() */ -void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) -{ - actWin = aw; - if (activateOnMessage) - connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow); - else - disconnect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow); +void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) { + actWin = aw; + + if (activateOnMessage) { + connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow); + } + + else { + disconnect(peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow); + } } @@ -300,9 +293,8 @@ void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessag \sa setActivationWindow() */ -QWidget* QtSingleApplication::activationWindow() const -{ - return actWin; +QWidget* QtSingleApplication::activationWindow() const { + return actWin; } @@ -320,18 +312,17 @@ QWidget* QtSingleApplication::activationWindow() const \sa setActivationWindow(), messageReceived(), initialize() */ -void QtSingleApplication::activateWindow() -{ - if (actWin) { - actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); - actWin->raise(); - actWin->activateWindow(); - } +void QtSingleApplication::activateWindow() { + if (actWin) { + actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); + actWin->raise(); + actWin->activateWindow(); + } } -void QtSingleApplication::finish() -{ - delete peer; peer = 0; +void QtSingleApplication::finish() { + delete peer; + peer = 0; } /*! diff --git a/src/qtsingleapplication/qtsingleapplication.h b/src/qtsingleapplication/qtsingleapplication.h old mode 100644 new mode 100755 index 80181740b..383c97821 --- a/src/qtsingleapplication/qtsingleapplication.h +++ b/src/qtsingleapplication/qtsingleapplication.h @@ -61,46 +61,47 @@ class QtLocalPeer; # define QT_QTSINGLEAPPLICATION_EXPORT #endif -class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication -{ - Q_OBJECT +class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication { + Q_OBJECT -public: - QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); - QtSingleApplication(const QString &id, int &argc, char **argv); + public: + QtSingleApplication(int& argc, char** argv, bool GUIenabled = true); + QtSingleApplication(const QString& id, int& argc, char** argv); #if QT_VERSION < 0x050000 - QtSingleApplication(int &argc, char **argv, Type type); + QtSingleApplication(int& argc, char** argv, Type type); # if defined(Q_WS_X11) - QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); - QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); - QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); + QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); + QtSingleApplication(Display* dpy, int& argc, char** argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0); + QtSingleApplication(Display* dpy, const QString& appId, int argc, char** argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); # endif // Q_WS_X11 #endif // QT_VERSION < 0x050000 - bool isRunning(); - QString id() const; + bool isRunning(); + QString id() const; - void setActivationWindow(QWidget* aw, bool activateOnMessage = true); - QWidget* activationWindow() const; + void setActivationWindow(QWidget* aw, bool activateOnMessage = true); + QWidget* activationWindow() const; - // Obsolete: - void initialize(bool dummy = true) - { isRunning(); Q_UNUSED(dummy) } + // Obsolete: + void initialize(bool dummy = true) { + isRunning(); + Q_UNUSED(dummy) + } -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); - void activateWindow(); - void finish(); + public Q_SLOTS: + bool sendMessage(const QString& message, int timeout = 5000); + void activateWindow(); + void finish(); -Q_SIGNALS: - void messageReceived(const QString &message); + Q_SIGNALS: + void messageReceived(const QString& message); -private: - void sysInit(const QString &appId = QString()); - QtLocalPeer *peer; - QWidget *actWin; + private: + void sysInit(const QString& appId = QString()); + QtLocalPeer* peer; + QWidget* actWin; }; #endif // QTSINGLEAPPLICATION_H diff --git a/src/qtsingleapplication/qtsinglecoreapplication.cpp b/src/qtsingleapplication/qtsinglecoreapplication.cpp index d1440e2d1..7f31002b8 100755 --- a/src/qtsingleapplication/qtsinglecoreapplication.cpp +++ b/src/qtsingleapplication/qtsinglecoreapplication.cpp @@ -70,11 +70,10 @@ argv are passed on to the QCoreAppliation constructor. */ -QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this); - connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleCoreApplication::messageReceived); +QtSingleCoreApplication::QtSingleCoreApplication(int& argc, char** argv) + : QCoreApplication(argc, argv) { + peer = new QtLocalPeer(this); + connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleCoreApplication::messageReceived); } @@ -83,11 +82,10 @@ QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) identifier \a appId. \a argc and \a argv are passed on to the QCoreAppliation constructor. */ -QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this, appId); - connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleCoreApplication::messageReceived); +QtSingleCoreApplication::QtSingleCoreApplication(const QString& appId, int& argc, char** argv) + : QCoreApplication(argc, argv) { + peer = new QtLocalPeer(this, appId); + connect(peer, &QtLocalPeer::messageReceived, this, &QtSingleCoreApplication::messageReceived); } @@ -102,9 +100,8 @@ QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc \sa sendMessage() */ -bool QtSingleCoreApplication::isRunning() -{ - return peer->isClient(); +bool QtSingleCoreApplication::isRunning() { + return peer->isClient(); } @@ -122,9 +119,8 @@ bool QtSingleCoreApplication::isRunning() \sa isRunning(), messageReceived() */ -bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); +bool QtSingleCoreApplication::sendMessage(const QString& message, int timeout) { + return peer->sendMessage(message, timeout); } @@ -133,9 +129,8 @@ bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) identifier will be regarded as instances of the same application. */ -QString QtSingleCoreApplication::id() const -{ - return peer->applicationId(); +QString QtSingleCoreApplication::id() const { + return peer->applicationId(); } diff --git a/src/qtsingleapplication/qtsinglecoreapplication.h b/src/qtsingleapplication/qtsinglecoreapplication.h old mode 100644 new mode 100755 index b87fffe41..9b6d69f04 --- a/src/qtsingleapplication/qtsinglecoreapplication.h +++ b/src/qtsingleapplication/qtsinglecoreapplication.h @@ -45,27 +45,26 @@ class QtLocalPeer; -class QtSingleCoreApplication : public QCoreApplication -{ - Q_OBJECT +class QtSingleCoreApplication : public QCoreApplication { + Q_OBJECT -public: - QtSingleCoreApplication(int &argc, char **argv); - QtSingleCoreApplication(const QString &id, int &argc, char **argv); + public: + QtSingleCoreApplication(int& argc, char** argv); + QtSingleCoreApplication(const QString& id, int& argc, char** argv); - bool isRunning(); - QString id() const; + bool isRunning(); + QString id() const; -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); + public Q_SLOTS: + bool sendMessage(const QString& message, int timeout = 5000); -Q_SIGNALS: - void messageReceived(const QString &message); + Q_SIGNALS: + void messageReceived(const QString& message); -private: - QtLocalPeer* peer; + private: + QtLocalPeer* peer; }; #endif // QTSINGLECOREAPPLICATION_H diff --git a/src/services/abstract/accountcheckmodel.cpp b/src/services/abstract/accountcheckmodel.cpp index a78b886c8..1b8b94d00 100755 --- a/src/services/abstract/accountcheckmodel.cpp +++ b/src/services/abstract/accountcheckmodel.cpp @@ -22,261 +22,270 @@ #include "miscellaneous/iconfactory.h" -AccountCheckModel::AccountCheckModel(QObject *parent) - : QAbstractItemModel(parent), m_rootItem(nullptr), m_checkStates(QHash()), m_recursiveChange(false) { +AccountCheckModel::AccountCheckModel(QObject* parent) + : QAbstractItemModel(parent), m_rootItem(nullptr), m_checkStates(QHash()), m_recursiveChange(false) { } AccountCheckModel::~AccountCheckModel() { } -RootItem *AccountCheckModel::itemForIndex(const QModelIndex &index) const { - if (index.isValid() && index.model() == this) { - return static_cast(index.internalPointer()); - } - else { - return m_rootItem; - } +RootItem* AccountCheckModel::itemForIndex(const QModelIndex& index) const { + if (index.isValid() && index.model() == this) { + return static_cast(index.internalPointer()); + } + + else { + return m_rootItem; + } } -RootItem *AccountCheckModel::rootItem() const { - return m_rootItem; +RootItem* AccountCheckModel::rootItem() const { + return m_rootItem; } -void AccountCheckModel::setRootItem(RootItem *root_item) { - if (m_rootItem != nullptr) { - delete m_rootItem; - } +void AccountCheckModel::setRootItem(RootItem* root_item) { + if (m_rootItem != nullptr) { + delete m_rootItem; + } - m_rootItem = root_item; + m_rootItem = root_item; } void AccountCheckModel::checkAllItems() { - if (m_rootItem != nullptr) { - foreach (RootItem *root_child, m_rootItem->childItems()) { - if (root_child->kind() == RootItemKind::Feed || root_child->kind() == RootItemKind::Category) { - setItemChecked(root_child, Qt::Checked); - } - } - } + if (m_rootItem != nullptr) { + foreach (RootItem* root_child, m_rootItem->childItems()) { + if (root_child->kind() == RootItemKind::Feed || root_child->kind() == RootItemKind::Category) { + setItemChecked(root_child, Qt::Checked); + } + } + } } void AccountCheckModel::uncheckAllItems() { - if (m_rootItem != nullptr) { - foreach (RootItem *root_child, m_rootItem->childItems()) { - if (root_child->kind() == RootItemKind::Feed || root_child->kind() == RootItemKind::Category) { - setData(indexForItem(root_child), Qt::Unchecked, Qt::CheckStateRole); - } - } - } + if (m_rootItem != nullptr) { + foreach (RootItem* root_child, m_rootItem->childItems()) { + if (root_child->kind() == RootItemKind::Feed || root_child->kind() == RootItemKind::Category) { + setData(indexForItem(root_child), Qt::Unchecked, Qt::CheckStateRole); + } + } + } } -QModelIndex AccountCheckModel::index(int row, int column, const QModelIndex &parent) const { - if (!hasIndex(row, column, parent)) { - return QModelIndex(); - } +QModelIndex AccountCheckModel::index(int row, int column, const QModelIndex& parent) const { + if (!hasIndex(row, column, parent)) { + return QModelIndex(); + } - RootItem *parent_item = itemForIndex(parent); - RootItem *child_item = parent_item->child(row); + RootItem* parent_item = itemForIndex(parent); + RootItem* child_item = parent_item->child(row); - if (child_item) { - return createIndex(row, column, child_item); - } - else { - return QModelIndex(); - } + if (child_item) { + return createIndex(row, column, child_item); + } + + else { + return QModelIndex(); + } } -QModelIndex AccountCheckModel::indexForItem(RootItem *item) const { - if (item == nullptr || item->kind() == RootItemKind::ServiceRoot || item->kind() == RootItemKind::Root) { - // Root item lies on invalid index. - return QModelIndex(); - } +QModelIndex AccountCheckModel::indexForItem(RootItem* item) const { + if (item == nullptr || item->kind() == RootItemKind::ServiceRoot || item->kind() == RootItemKind::Root) { + // Root item lies on invalid index. + return QModelIndex(); + } - QList parents; + QList parents; + // Start with root item (which obviously has invalid index). + parents << indexForItem(m_rootItem); - // Start with root item (which obviously has invalid index). - parents << indexForItem(m_rootItem); + while (!parents.isEmpty()) { + QModelIndex active_index = parents.takeFirst(); + int row_count = rowCount(active_index); - while (!parents.isEmpty()) { - QModelIndex active_index = parents.takeFirst(); - int row_count = rowCount(active_index); + if (row_count > 0) { + // This index has children. + // Lets take a look if our target item is among them. + RootItem* active_item = itemForIndex(active_index); + int candidate_index = active_item->childItems().indexOf(item); - if (row_count > 0) { - // This index has children. - // Lets take a look if our target item is among them. - RootItem *active_item = itemForIndex(active_index); - int candidate_index = active_item->childItems().indexOf(item); + if (candidate_index >= 0) { + // We found our item. + return index(candidate_index, 0, active_index); + } - if (candidate_index >= 0) { - // We found our item. - return index(candidate_index, 0, active_index); - } - else { - // Item is not found, add all "categories" from active_item. - for (int i = 0; i < row_count; i++) { - RootItem *possible_category = active_item->child(i); + else { + // Item is not found, add all "categories" from active_item. + for (int i = 0; i < row_count; i++) { + RootItem* possible_category = active_item->child(i); - if (possible_category->kind() == RootItemKind::Category) { - parents << index(i, 0, active_index); - } - } - } - } - } + if (possible_category->kind() == RootItemKind::Category) { + parents << index(i, 0, active_index); + } + } + } + } + } - return QModelIndex(); + return QModelIndex(); } -QModelIndex AccountCheckModel::parent(const QModelIndex &child) const { - if (!child.isValid()) { - return QModelIndex(); - } +QModelIndex AccountCheckModel::parent(const QModelIndex& child) const { + if (!child.isValid()) { + return QModelIndex(); + } - RootItem *child_item = itemForIndex(child); - RootItem *parent_item = child_item->parent(); + RootItem* child_item = itemForIndex(child); + RootItem* parent_item = child_item->parent(); - if (parent_item == m_rootItem) { - return QModelIndex(); - } - else { - return createIndex(parent_item->row(), 0, parent_item); - } + if (parent_item == m_rootItem) { + return QModelIndex(); + } + + else { + return createIndex(parent_item->row(), 0, parent_item); + } } -int AccountCheckModel::rowCount(const QModelIndex &parent) const { - if (parent.column() > 0) { - return 0; - } - else { - RootItem *item = itemForIndex(parent); +int AccountCheckModel::rowCount(const QModelIndex& parent) const { + if (parent.column() > 0) { + return 0; + } - if (item != nullptr) { - return item->childCount(); - } - else { - return 0; - } - } + else { + RootItem* item = itemForIndex(parent); + + if (item != nullptr) { + return item->childCount(); + } + + else { + return 0; + } + } } -int AccountCheckModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent) - return 1; +int AccountCheckModel::columnCount(const QModelIndex& parent) const { + Q_UNUSED(parent) + return 1; } -QVariant AccountCheckModel::data(const QModelIndex &index, int role) const { - if (index.column() != 0) { - return QVariant(); - } +QVariant AccountCheckModel::data(const QModelIndex& index, int role) const { + if (index.column() != 0) { + return QVariant(); + } - RootItem *item = itemForIndex(index); + RootItem* item = itemForIndex(index); - if (role == Qt::CheckStateRole) { - if (m_checkStates.contains(item)) { - return m_checkStates.value(item); - } - else { - return static_cast(Qt::Unchecked); - } - } - else if (role == Qt::DecorationRole) { - switch (item->kind()) { - case RootItemKind::Category: - case RootItemKind::Bin: - case RootItemKind::Feed: - return item->icon(); + if (role == Qt::CheckStateRole) { + if (m_checkStates.contains(item)) { + return m_checkStates.value(item); + } - default: - return QVariant(); - } - } - else if (role == Qt::DisplayRole) { - switch (item->kind()) { - case RootItemKind::Category: - return QVariant(item->data(index.column(), role).toString() + tr(" (category)")); + else { + return static_cast(Qt::Unchecked); + } + } - case RootItemKind::Feed: - return QVariant(item->data(index.column(), role).toString() + tr(" (feed)")); + else if (role == Qt::DecorationRole) { + switch (item->kind()) { + case RootItemKind::Category: + case RootItemKind::Bin: + case RootItemKind::Feed: + return item->icon(); - default: - return item->title(); - } - } - else { - return QVariant(); - } + default: + return QVariant(); + } + } + + else if (role == Qt::DisplayRole) { + switch (item->kind()) { + case RootItemKind::Category: + return QVariant(item->data(index.column(), role).toString() + tr(" (category)")); + + case RootItemKind::Feed: + return QVariant(item->data(index.column(), role).toString() + tr(" (feed)")); + + default: + return item->title(); + } + } + + else { + return QVariant(); + } } -bool AccountCheckModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (index.isValid() && index.column() == 0 && role == Qt::CheckStateRole) { - RootItem *item = itemForIndex(index); +bool AccountCheckModel::setData(const QModelIndex& index, const QVariant& value, int role) { + if (index.isValid() && index.column() == 0 && role == Qt::CheckStateRole) { + RootItem* item = itemForIndex(index); - if (item == m_rootItem) { - // Cannot set data on root item. - return false; - } + if (item == m_rootItem) { + // Cannot set data on root item. + return false; + } - // Change data for the actual item. - m_checkStates[item] = static_cast(value.toInt()); - emit dataChanged(index, index); + // Change data for the actual item. + m_checkStates[item] = static_cast(value.toInt()); + emit dataChanged(index, index); - if (m_recursiveChange) { - return true; - } + if (m_recursiveChange) { + return true; + } - // Set new data for all descendants of this actual item. - foreach(RootItem *child, item->childItems()) { - setData(indexForItem(child), value, Qt::CheckStateRole); - } + // Set new data for all descendants of this actual item. + foreach (RootItem* child, item->childItems()) { + setData(indexForItem(child), value, Qt::CheckStateRole); + } - // Now we need to change new data to all parents. - QModelIndex parent_index = index; - m_recursiveChange = true; + // Now we need to change new data to all parents. + QModelIndex parent_index = index; + m_recursiveChange = true; - // Iterate all valid parents. - while ((parent_index = parent_index.parent()).isValid()) { - // We now have parent index. Get parent item too. - item = item->parent(); + // Iterate all valid parents. + while ((parent_index = parent_index.parent()).isValid()) { + // We now have parent index. Get parent item too. + item = item->parent(); + // Check children of this new parent item. + Qt::CheckState parent_state = Qt::Unchecked; - // Check children of this new parent item. - Qt::CheckState parent_state = Qt::Unchecked; - foreach (RootItem *child_of_parent, item->childItems()) { - if (m_checkStates.contains(child_of_parent) && m_checkStates[child_of_parent] == Qt::Checked) { - // We found out, that some child of this item is checked, - // therefore this item must be checked too. - parent_state = Qt::Checked; - break; - } - } + foreach (RootItem* child_of_parent, item->childItems()) { + if (m_checkStates.contains(child_of_parent) && m_checkStates[child_of_parent] == Qt::Checked) { + // We found out, that some child of this item is checked, + // therefore this item must be checked too. + parent_state = Qt::Checked; + break; + } + } - setData(parent_index, parent_state, Qt::CheckStateRole); - } + setData(parent_index, parent_state, Qt::CheckStateRole); + } - m_recursiveChange = false; - return true; - } + m_recursiveChange = false; + return true; + } - return false; + return false; } -Qt::ItemFlags AccountCheckModel::flags(const QModelIndex &index) const { - if (!index.isValid() || itemForIndex(index)->kind() == RootItemKind::Bin) { - return Qt::NoItemFlags; - } +Qt::ItemFlags AccountCheckModel::flags(const QModelIndex& index) const { + if (!index.isValid() || itemForIndex(index)->kind() == RootItemKind::Bin) { + return Qt::NoItemFlags; + } - Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; - if ( index.column() == 0 ) { - flags |= Qt::ItemIsUserCheckable; - } + if (index.column() == 0) { + flags |= Qt::ItemIsUserCheckable; + } - return flags; + return flags; } -bool AccountCheckModel::isItemChecked(RootItem *item) { - return m_checkStates.contains(item) && m_checkStates.value(item, Qt::Unchecked); +bool AccountCheckModel::isItemChecked(RootItem* item) { + return m_checkStates.contains(item) && m_checkStates.value(item, Qt::Unchecked); } -bool AccountCheckModel::setItemChecked(RootItem *item, Qt::CheckState check) { - return setData(indexForItem(item), check, Qt::CheckStateRole); +bool AccountCheckModel::setItemChecked(RootItem* item, Qt::CheckState check) { + return setData(indexForItem(item), check, Qt::CheckStateRole); } diff --git a/src/services/abstract/accountcheckmodel.h b/src/services/abstract/accountcheckmodel.h old mode 100644 new mode 100755 index daa27405e..4ab088e7f --- a/src/services/abstract/accountcheckmodel.h +++ b/src/services/abstract/accountcheckmodel.h @@ -24,45 +24,45 @@ class AccountCheckModel : public QAbstractItemModel { - Q_OBJECT + Q_OBJECT - public: - // Constructors and destructors. - explicit AccountCheckModel(QObject *parent = 0); - virtual ~AccountCheckModel(); + public: + // Constructors and destructors. + explicit AccountCheckModel(QObject* parent = 0); + virtual ~AccountCheckModel(); - QModelIndex index(int row, int column, const QModelIndex &parent) const; - QModelIndex parent(const QModelIndex &child) const; - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - bool setData(const QModelIndex &index, const QVariant &value, int role); - Qt::ItemFlags flags(const QModelIndex &index) const; + QModelIndex index(int row, int column, const QModelIndex& parent) const; + QModelIndex parent(const QModelIndex& child) const; + int rowCount(const QModelIndex& parent) const; + int columnCount(const QModelIndex& parent) const; + QVariant data(const QModelIndex& index, int role) const; + bool setData(const QModelIndex& index, const QVariant& value, int role); + Qt::ItemFlags flags(const QModelIndex& index) const; - bool isItemChecked(RootItem *item); - bool setItemChecked(RootItem *item, Qt::CheckState check); + bool isItemChecked(RootItem* item); + bool setItemChecked(RootItem* item, Qt::CheckState check); - // Returns feed/category which lies at the specified index or - // root item if index is invalid. - RootItem *itemForIndex(const QModelIndex &index) const; + // Returns feed/category which lies at the specified index or + // root item if index is invalid. + RootItem* itemForIndex(const QModelIndex& index) const; - // Returns source QModelIndex on which lies given item. - QModelIndex indexForItem(RootItem *item) const; + // Returns source QModelIndex on which lies given item. + QModelIndex indexForItem(RootItem* item) const; - // Root item manipulators. - RootItem *rootItem() const; - void setRootItem(RootItem *root_item); + // Root item manipulators. + RootItem* rootItem() const; + void setRootItem(RootItem* root_item); - public slots: - void checkAllItems(); - void uncheckAllItems(); + public slots: + void checkAllItems(); + void uncheckAllItems(); - protected: - RootItem *m_rootItem; + protected: + RootItem* m_rootItem; - private: - QHash m_checkStates; - bool m_recursiveChange; + private: + QHash m_checkStates; + bool m_recursiveChange; }; #endif // ACCOUNTCHECKMODEL_H diff --git a/src/services/abstract/cacheforserviceroot.cpp b/src/services/abstract/cacheforserviceroot.cpp index a54c456ab..69e4be18f 100755 --- a/src/services/abstract/cacheforserviceroot.cpp +++ b/src/services/abstract/cacheforserviceroot.cpp @@ -1,98 +1,86 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/abstract/cacheforserviceroot.h" - -#include "miscellaneous/mutex.h" - -#include - - -CacheForServiceRoot::CacheForServiceRoot() : m_cacheSaveMutex(new Mutex(QMutex::NonRecursive, nullptr)), - m_cachedStatesRead(QMap()), - m_cachedStatesImportant(QMap>()) { -} - -CacheForServiceRoot::~CacheForServiceRoot() { - m_cacheSaveMutex->deleteLater(); -} - -void CacheForServiceRoot::addMessageStatesToCache(const QList &ids_of_messages, RootItem::Importance importance) { - m_cacheSaveMutex->lock(); - - QList &list_act = m_cachedStatesImportant[importance]; - QList &list_other = m_cachedStatesImportant[importance == RootItem::Important ? RootItem::NotImportant : RootItem::Important]; - - // Store changes, they will be sent to server later. - list_act.append(ids_of_messages); - - QSet set_act = list_act.toSet(); - QSet set_other = list_other.toSet(); - - // Now, we want to remove all IDS from list_other, which are contained in list. - set_other -= set_act; - - list_act.clear(); list_act.append(set_act.toList()); - list_other.clear(); list_other.append(set_other.toList()); - - m_cacheSaveMutex->unlock(); -} - -void CacheForServiceRoot::addMessageStatesToCache(const QStringList &ids_of_messages, RootItem::ReadStatus read) { - m_cacheSaveMutex->lock(); - - QStringList &list_act = m_cachedStatesRead[read]; - QStringList &list_other = m_cachedStatesRead[read == RootItem::Read ? RootItem::Unread : RootItem::Read]; - - // Store changes, they will be sent to server later. - list_act.append(ids_of_messages); - - QSet set_act = list_act.toSet(); - QSet set_other = list_other.toSet(); - - // Now, we want to remove all IDS from list_other, which are contained in list. - set_other -= set_act; - - list_act.clear(); list_act.append(set_act.toList()); - list_other.clear(); list_other.append(set_other.toList()); - - m_cacheSaveMutex->unlock(); -} - -QPair, QMap> > CacheForServiceRoot::takeMessageCache() { - m_cacheSaveMutex->lock(); - - if (m_cachedStatesRead.isEmpty() && m_cachedStatesImportant.isEmpty()) { - // No cached changes. - m_cacheSaveMutex->unlock(); - return QPair, QMap> >(); - } - - // Make copy of changes. - QMap cached_data_read = m_cachedStatesRead; - cached_data_read.detach(); - - QMap> cached_data_imp = m_cachedStatesImportant; - cached_data_imp.detach(); - - m_cachedStatesRead.clear(); - m_cachedStatesImportant.clear(); - - m_cacheSaveMutex->unlock(); - - return QPair, QMap> >(cached_data_read, cached_data_imp); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/abstract/cacheforserviceroot.h" + +#include "miscellaneous/mutex.h" + +#include + + +CacheForServiceRoot::CacheForServiceRoot() : m_cacheSaveMutex(new Mutex(QMutex::NonRecursive, nullptr)), + m_cachedStatesRead(QMap()), + m_cachedStatesImportant(QMap>()) { +} + +CacheForServiceRoot::~CacheForServiceRoot() { + m_cacheSaveMutex->deleteLater(); +} + +void CacheForServiceRoot::addMessageStatesToCache(const QList& ids_of_messages, RootItem::Importance importance) { + m_cacheSaveMutex->lock(); + QList& list_act = m_cachedStatesImportant[importance]; + QList& list_other = m_cachedStatesImportant[importance == RootItem::Important ? RootItem::NotImportant : RootItem::Important]; + // Store changes, they will be sent to server later. + list_act.append(ids_of_messages); + QSet set_act = list_act.toSet(); + QSet set_other = list_other.toSet(); + // Now, we want to remove all IDS from list_other, which are contained in list. + set_other -= set_act; + list_act.clear(); + list_act.append(set_act.toList()); + list_other.clear(); + list_other.append(set_other.toList()); + m_cacheSaveMutex->unlock(); +} + +void CacheForServiceRoot::addMessageStatesToCache(const QStringList& ids_of_messages, RootItem::ReadStatus read) { + m_cacheSaveMutex->lock(); + QStringList& list_act = m_cachedStatesRead[read]; + QStringList& list_other = m_cachedStatesRead[read == RootItem::Read ? RootItem::Unread : RootItem::Read]; + // Store changes, they will be sent to server later. + list_act.append(ids_of_messages); + QSet set_act = list_act.toSet(); + QSet set_other = list_other.toSet(); + // Now, we want to remove all IDS from list_other, which are contained in list. + set_other -= set_act; + list_act.clear(); + list_act.append(set_act.toList()); + list_other.clear(); + list_other.append(set_other.toList()); + m_cacheSaveMutex->unlock(); +} + +QPair, QMap>> CacheForServiceRoot::takeMessageCache() { + m_cacheSaveMutex->lock(); + + if (m_cachedStatesRead.isEmpty() && m_cachedStatesImportant.isEmpty()) { + // No cached changes. + m_cacheSaveMutex->unlock(); + return QPair, QMap>>(); + } + + // Make copy of changes. + QMap cached_data_read = m_cachedStatesRead; + cached_data_read.detach(); + QMap> cached_data_imp = m_cachedStatesImportant; + cached_data_imp.detach(); + m_cachedStatesRead.clear(); + m_cachedStatesImportant.clear(); + m_cacheSaveMutex->unlock(); + return QPair, QMap>>(cached_data_read, cached_data_imp); +} diff --git a/src/services/abstract/cacheforserviceroot.h b/src/services/abstract/cacheforserviceroot.h index a62a73d27..4c867f098 100755 --- a/src/services/abstract/cacheforserviceroot.h +++ b/src/services/abstract/cacheforserviceroot.h @@ -1,46 +1,46 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef CACHEFORSERVICEROOT_H -#define CACHEFORSERVICEROOT_H - -#include "services/abstract/serviceroot.h" - -#include -#include -#include - - -class Mutex; - -class CacheForServiceRoot { - public: - explicit CacheForServiceRoot(); - virtual ~CacheForServiceRoot(); - - void addMessageStatesToCache(const QList &ids_of_messages, RootItem::Importance importance); - void addMessageStatesToCache(const QStringList &ids_of_messages, RootItem::ReadStatus read); - - protected: - QPair, QMap > > takeMessageCache(); - - Mutex *m_cacheSaveMutex; - QMap m_cachedStatesRead; - QMap> m_cachedStatesImportant; -}; - -#endif // CACHEFORSERVICEROOT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef CACHEFORSERVICEROOT_H +#define CACHEFORSERVICEROOT_H + +#include "services/abstract/serviceroot.h" + +#include +#include +#include + + +class Mutex; + +class CacheForServiceRoot { + public: + explicit CacheForServiceRoot(); + virtual ~CacheForServiceRoot(); + + void addMessageStatesToCache(const QList& ids_of_messages, RootItem::Importance importance); + void addMessageStatesToCache(const QStringList& ids_of_messages, RootItem::ReadStatus read); + + protected: + QPair, QMap>> takeMessageCache(); + + Mutex* m_cacheSaveMutex; + QMap m_cachedStatesRead; + QMap> m_cachedStatesImportant; +}; + +#endif // CACHEFORSERVICEROOT_H diff --git a/src/services/abstract/category.cpp b/src/services/abstract/category.cpp index 7e34a2557..d1e4d628d 100755 --- a/src/services/abstract/category.cpp +++ b/src/services/abstract/category.cpp @@ -1,65 +1,66 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/abstract/category.h" - -#include "miscellaneous/application.h" -#include "miscellaneous/databasequeries.h" -#include "services/abstract/serviceroot.h" -#include "services/abstract/feed.h" - - -Category::Category(RootItem *parent) : RootItem(parent) { - setKind(RootItemKind::Category); -} - -Category::~Category() { -} - -void Category::updateCounts(bool including_total_count) { - QList feeds; - - foreach (RootItem *child, getSubTree()) { - if (child->kind() == RootItemKind::Feed) { - feeds.append(child->toFeed()); - } - else if (child->kind() != RootItemKind::Category && child->kind() != RootItemKind::ServiceRoot) { - child->updateCounts(including_total_count); - } - } - - if (feeds.isEmpty()) { - return; - } - - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - bool ok; - QMap > counts = DatabaseQueries::getMessageCountsForCategory(database, customId(), getParentServiceRoot()->accountId(), - including_total_count, &ok); - - if (ok) { - foreach (Feed *feed, feeds) { - if (counts.contains(feed->customId())) { - feed->setCountOfUnreadMessages(counts.value(feed->customId()).first); - - if (including_total_count) { - feed->setCountOfAllMessages(counts.value(feed->customId()).second); - } - } - } - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/abstract/category.h" + +#include "miscellaneous/application.h" +#include "miscellaneous/databasequeries.h" +#include "services/abstract/serviceroot.h" +#include "services/abstract/feed.h" + + +Category::Category(RootItem* parent) : RootItem(parent) { + setKind(RootItemKind::Category); +} + +Category::~Category() { +} + +void Category::updateCounts(bool including_total_count) { + QList feeds; + + foreach (RootItem* child, getSubTree()) { + if (child->kind() == RootItemKind::Feed) { + feeds.append(child->toFeed()); + } + + else if (child->kind() != RootItemKind::Category && child->kind() != RootItemKind::ServiceRoot) { + child->updateCounts(including_total_count); + } + } + + if (feeds.isEmpty()) { + return; + } + + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + bool ok; + QMap> counts = DatabaseQueries::getMessageCountsForCategory(database, customId(), getParentServiceRoot()->accountId(), + including_total_count, &ok); + + if (ok) { + foreach (Feed* feed, feeds) { + if (counts.contains(feed->customId())) { + feed->setCountOfUnreadMessages(counts.value(feed->customId()).first); + + if (including_total_count) { + feed->setCountOfAllMessages(counts.value(feed->customId()).second); + } + } + } + } +} diff --git a/src/services/abstract/category.h b/src/services/abstract/category.h index b76410831..b9d646da7 100755 --- a/src/services/abstract/category.h +++ b/src/services/abstract/category.h @@ -1,34 +1,34 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef CATEGORY_H -#define CATEGORY_H - -#include "services/abstract/rootitem.h" - - -class Category : public RootItem { - Q_OBJECT - - public: - explicit Category(RootItem *parent = nullptr); - virtual ~Category(); - - void updateCounts(bool including_total_count); -}; - -#endif // CATEGORY_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef CATEGORY_H +#define CATEGORY_H + +#include "services/abstract/rootitem.h" + + +class Category : public RootItem { + Q_OBJECT + + public: + explicit Category(RootItem* parent = nullptr); + virtual ~Category(); + + void updateCounts(bool including_total_count); +}; + +#endif // CATEGORY_H diff --git a/src/services/abstract/feed.cpp b/src/services/abstract/feed.cpp index d78accedc..9d0c8bb4b 100755 --- a/src/services/abstract/feed.cpp +++ b/src/services/abstract/feed.cpp @@ -1,249 +1,244 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/abstract/feed.h" - -#include "definitions/definitions.h" -#include "miscellaneous/application.h" -#include "miscellaneous/mutex.h" -#include "miscellaneous/databasequeries.h" -#include "miscellaneous/feedreader.h" -#include "services/abstract/recyclebin.h" -#include "services/abstract/serviceroot.h" - -#include - - -Feed::Feed(RootItem *parent) - : RootItem(parent), m_url(QString()), m_status(Normal), m_autoUpdateType(DefaultAutoUpdate), - m_autoUpdateInitialInterval(DEFAULT_AUTO_UPDATE_INTERVAL), m_autoUpdateRemainingInterval(DEFAULT_AUTO_UPDATE_INTERVAL), - m_totalCount(0), m_unreadCount(0) { - setKind(RootItemKind::Feed); - setAutoDelete(false); -} - -Feed::~Feed() { -} - -QList Feed::undeletedMessages() const { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - return DatabaseQueries::getUndeletedMessagesForFeed(database, customId(), getParentServiceRoot()->accountId()); -} - -QVariant Feed::data(int column, int role) const { - switch (role) { - case Qt::ToolTipRole: - if (column == FDS_MODEL_TITLE_INDEX) { - //: Tooltip for feed. - return tr("%1" - "%2\n\n" - "Auto-update status: %3").arg(title(), - description().isEmpty() ? QString() : QString('\n') + description(), - getAutoUpdateStatusDescription()); - } - else { - return RootItem::data(column, role); - } - - case Qt::ForegroundRole: - switch (status()) { - case NewMessages: - return QColor(Qt::blue); - - case NetworkError: - case ParsingError: - case OtherError: - return QColor(Qt::red); - - default: - return QVariant(); - } - - default: - return RootItem::data(column, role); - } -} - -int Feed::autoUpdateInitialInterval() const { - return m_autoUpdateInitialInterval; -} - -int Feed::countOfAllMessages() const { - return m_totalCount; -} - -int Feed::countOfUnreadMessages() const { - return m_unreadCount; -} - -void Feed::setCountOfAllMessages(int count_all_messages) { - m_totalCount = count_all_messages; -} - -void Feed::setCountOfUnreadMessages(int count_unread_messages) { - if (status() == NewMessages && count_unread_messages < countOfUnreadMessages()) { - setStatus(Normal); - } - - m_unreadCount = count_unread_messages; -} - -void Feed::setAutoUpdateInitialInterval(int auto_update_interval) { - // If new initial auto-update interval is set, then - // we should reset time that remains to the next auto-update. - m_autoUpdateInitialInterval = auto_update_interval; - m_autoUpdateRemainingInterval = auto_update_interval; -} - -Feed::AutoUpdateType Feed::autoUpdateType() const { - return m_autoUpdateType; -} - -void Feed::setAutoUpdateType(Feed::AutoUpdateType auto_update_type) { - m_autoUpdateType = auto_update_type; -} - -int Feed::autoUpdateRemainingInterval() const { - return m_autoUpdateRemainingInterval; -} - -void Feed::setAutoUpdateRemainingInterval(int auto_update_remaining_interval) { - m_autoUpdateRemainingInterval = auto_update_remaining_interval; -} - -Feed::Status Feed::status() const { - return m_status; -} - -void Feed::setStatus(const Feed::Status &status) { - m_status = status; -} - -QString Feed::url() const { - return m_url; -} - -void Feed::setUrl(const QString &url) { - m_url = url; -} - -void Feed::updateCounts(bool including_total_count) { - bool is_main_thread = QThread::currentThread() == qApp->thread(); - QSqlDatabase database = is_main_thread ? - qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings) : - qApp->database()->connection(QSL("feed_upd"), DatabaseFactory::FromSettings); - int account_id = getParentServiceRoot()->accountId(); - - if (including_total_count) { - setCountOfAllMessages(DatabaseQueries::getMessageCountsForFeed(database, customId(), account_id, true)); - } - - setCountOfUnreadMessages(DatabaseQueries::getMessageCountsForFeed(database, customId(), account_id, false)); -} - -void Feed::run() { - qDebug().nospace() << "Downloading new messages for feed " - << customId() << " in thread: \'" - << QThread::currentThreadId() << "\'."; - - // Save all cached data first. - getParentServiceRoot()->saveAllCachedData(); - - bool error_during_obtaining; - QList msgs = obtainNewMessages(&error_during_obtaining); - - qDebug().nospace() << "Downloaded " << msgs.size() << " messages for feed " - << customId() << " in thread: \'" - << QThread::currentThreadId() << "\'."; - - // Now, do some general operations on messages (tweak encoding etc.). - for (int i = 0; i < msgs.size(); i++) { - // Also, make sure that HTML encoding, encoding of special characters, etc., is fixed. - msgs[i].m_contents = QUrl::fromPercentEncoding(msgs[i].m_contents.toUtf8()); - msgs[i].m_author = msgs[i].m_author.toUtf8(); - - // Sanitize title. Remove newlines etc. - msgs[i].m_title = QUrl::fromPercentEncoding(msgs[i].m_title.toUtf8()) - // Replace all continuous white space. - .replace(QRegExp(QSL("[\\s]{2,}")), QSL(" ")) - // Remove all newlines and leading white space. - .remove(QRegExp(QSL("([\\n\\r])|(^\\s)"))); - } - - emit messagesObtained(msgs, error_during_obtaining); -} - -int Feed::updateMessages(const QList &messages, bool error_during_obtaining) { - QList items_to_update; - int updated_messages = 0; - bool is_main_thread = QThread::currentThread() == qApp->thread(); - - qDebug("Updating messages in DB. Main thread: '%s'.", qPrintable(is_main_thread ? "true" : "false")); - - if (!error_during_obtaining) { - bool anything_updated = false; - bool ok = true; - - if (!messages.isEmpty()) { - int custom_id = customId(); - int account_id = getParentServiceRoot()->accountId(); - QSqlDatabase database = is_main_thread ? - qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings) : - qApp->database()->connection(QSL("feed_upd"), DatabaseFactory::FromSettings); - updated_messages = DatabaseQueries::updateMessages(database, messages, custom_id, account_id, url(), &anything_updated, &ok); - } - - if (ok) { - setStatus(updated_messages > 0 ? NewMessages : Normal); - updateCounts(true); - - if (getParentServiceRoot()->recycleBin() != nullptr && anything_updated) { - getParentServiceRoot()->recycleBin()->updateCounts(true); - items_to_update.append(getParentServiceRoot()->recycleBin()); - } - } - } - - items_to_update.append(this); - getParentServiceRoot()->itemChanged(items_to_update); - - return updated_messages; -} - -QString Feed::getAutoUpdateStatusDescription() const { - QString auto_update_string; - - switch (autoUpdateType()) { - case DontAutoUpdate: - //: Describes feed auto-update status. - auto_update_string = tr("does not use auto-update"); - break; - - case DefaultAutoUpdate: - //: Describes feed auto-update status. - auto_update_string = tr("uses global settings (%n minute(s) to next auto-update)", 0, qApp->feedReader()->autoUpdateRemainingInterval()); - break; - - case SpecificAutoUpdate: - default: - //: Describes feed auto-update status. - auto_update_string = tr("uses specific settings (%n minute(s) to next auto-update)", 0, autoUpdateRemainingInterval()); - break; - } - - return auto_update_string; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/abstract/feed.h" + +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "miscellaneous/mutex.h" +#include "miscellaneous/databasequeries.h" +#include "miscellaneous/feedreader.h" +#include "services/abstract/recyclebin.h" +#include "services/abstract/serviceroot.h" + +#include + + +Feed::Feed(RootItem* parent) + : RootItem(parent), m_url(QString()), m_status(Normal), m_autoUpdateType(DefaultAutoUpdate), + m_autoUpdateInitialInterval(DEFAULT_AUTO_UPDATE_INTERVAL), m_autoUpdateRemainingInterval(DEFAULT_AUTO_UPDATE_INTERVAL), + m_totalCount(0), m_unreadCount(0) { + setKind(RootItemKind::Feed); + setAutoDelete(false); +} + +Feed::~Feed() { +} + +QList Feed::undeletedMessages() const { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + return DatabaseQueries::getUndeletedMessagesForFeed(database, customId(), getParentServiceRoot()->accountId()); +} + +QVariant Feed::data(int column, int role) const { + switch (role) { + case Qt::ToolTipRole: + if (column == FDS_MODEL_TITLE_INDEX) { + //: Tooltip for feed. + return tr("%1" + "%2\n\n" + "Auto-update status: %3").arg(title(), + description().isEmpty() ? QString() : QString('\n') + description(), + getAutoUpdateStatusDescription()); + } + + else { + return RootItem::data(column, role); + } + + case Qt::ForegroundRole: + switch (status()) { + case NewMessages: + return QColor(Qt::blue); + + case NetworkError: + case ParsingError: + case OtherError: + return QColor(Qt::red); + + default: + return QVariant(); + } + + default: + return RootItem::data(column, role); + } +} + +int Feed::autoUpdateInitialInterval() const { + return m_autoUpdateInitialInterval; +} + +int Feed::countOfAllMessages() const { + return m_totalCount; +} + +int Feed::countOfUnreadMessages() const { + return m_unreadCount; +} + +void Feed::setCountOfAllMessages(int count_all_messages) { + m_totalCount = count_all_messages; +} + +void Feed::setCountOfUnreadMessages(int count_unread_messages) { + if (status() == NewMessages && count_unread_messages < countOfUnreadMessages()) { + setStatus(Normal); + } + + m_unreadCount = count_unread_messages; +} + +void Feed::setAutoUpdateInitialInterval(int auto_update_interval) { + // If new initial auto-update interval is set, then + // we should reset time that remains to the next auto-update. + m_autoUpdateInitialInterval = auto_update_interval; + m_autoUpdateRemainingInterval = auto_update_interval; +} + +Feed::AutoUpdateType Feed::autoUpdateType() const { + return m_autoUpdateType; +} + +void Feed::setAutoUpdateType(Feed::AutoUpdateType auto_update_type) { + m_autoUpdateType = auto_update_type; +} + +int Feed::autoUpdateRemainingInterval() const { + return m_autoUpdateRemainingInterval; +} + +void Feed::setAutoUpdateRemainingInterval(int auto_update_remaining_interval) { + m_autoUpdateRemainingInterval = auto_update_remaining_interval; +} + +Feed::Status Feed::status() const { + return m_status; +} + +void Feed::setStatus(const Feed::Status& status) { + m_status = status; +} + +QString Feed::url() const { + return m_url; +} + +void Feed::setUrl(const QString& url) { + m_url = url; +} + +void Feed::updateCounts(bool including_total_count) { + bool is_main_thread = QThread::currentThread() == qApp->thread(); + QSqlDatabase database = is_main_thread ? + qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings) : + qApp->database()->connection(QSL("feed_upd"), DatabaseFactory::FromSettings); + int account_id = getParentServiceRoot()->accountId(); + + if (including_total_count) { + setCountOfAllMessages(DatabaseQueries::getMessageCountsForFeed(database, customId(), account_id, true)); + } + + setCountOfUnreadMessages(DatabaseQueries::getMessageCountsForFeed(database, customId(), account_id, false)); +} + +void Feed::run() { + qDebug().nospace() << "Downloading new messages for feed " + << customId() << " in thread: \'" + << QThread::currentThreadId() << "\'."; + // Save all cached data first. + getParentServiceRoot()->saveAllCachedData(); + bool error_during_obtaining; + QList msgs = obtainNewMessages(&error_during_obtaining); + qDebug().nospace() << "Downloaded " << msgs.size() << " messages for feed " + << customId() << " in thread: \'" + << QThread::currentThreadId() << "\'."; + + // Now, do some general operations on messages (tweak encoding etc.). + for (int i = 0; i < msgs.size(); i++) { + // Also, make sure that HTML encoding, encoding of special characters, etc., is fixed. + msgs[i].m_contents = QUrl::fromPercentEncoding(msgs[i].m_contents.toUtf8()); + msgs[i].m_author = msgs[i].m_author.toUtf8(); + // Sanitize title. Remove newlines etc. + msgs[i].m_title = QUrl::fromPercentEncoding(msgs[i].m_title.toUtf8()) + // Replace all continuous white space. + .replace(QRegExp(QSL("[\\s]{2,}")), QSL(" ")) + // Remove all newlines and leading white space. + .remove(QRegExp(QSL("([\\n\\r])|(^\\s)"))); + } + + emit messagesObtained(msgs, error_during_obtaining); +} + +int Feed::updateMessages(const QList& messages, bool error_during_obtaining) { + QList items_to_update; + int updated_messages = 0; + bool is_main_thread = QThread::currentThread() == qApp->thread(); + qDebug("Updating messages in DB. Main thread: '%s'.", qPrintable(is_main_thread ? "true" : "false")); + + if (!error_during_obtaining) { + bool anything_updated = false; + bool ok = true; + + if (!messages.isEmpty()) { + int custom_id = customId(); + int account_id = getParentServiceRoot()->accountId(); + QSqlDatabase database = is_main_thread ? + qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings) : + qApp->database()->connection(QSL("feed_upd"), DatabaseFactory::FromSettings); + updated_messages = DatabaseQueries::updateMessages(database, messages, custom_id, account_id, url(), &anything_updated, &ok); + } + + if (ok) { + setStatus(updated_messages > 0 ? NewMessages : Normal); + updateCounts(true); + + if (getParentServiceRoot()->recycleBin() != nullptr && anything_updated) { + getParentServiceRoot()->recycleBin()->updateCounts(true); + items_to_update.append(getParentServiceRoot()->recycleBin()); + } + } + } + + items_to_update.append(this); + getParentServiceRoot()->itemChanged(items_to_update); + return updated_messages; +} + +QString Feed::getAutoUpdateStatusDescription() const { + QString auto_update_string; + + switch (autoUpdateType()) { + case DontAutoUpdate: + //: Describes feed auto-update status. + auto_update_string = tr("does not use auto-update"); + break; + + case DefaultAutoUpdate: + //: Describes feed auto-update status. + auto_update_string = tr("uses global settings (%n minute(s) to next auto-update)", 0, qApp->feedReader()->autoUpdateRemainingInterval()); + break; + + case SpecificAutoUpdate: + default: + //: Describes feed auto-update status. + auto_update_string = tr("uses specific settings (%n minute(s) to next auto-update)", 0, autoUpdateRemainingInterval()); + break; + } + + return auto_update_string; +} diff --git a/src/services/abstract/feed.h b/src/services/abstract/feed.h index ef18aa25f..a30d7456b 100755 --- a/src/services/abstract/feed.h +++ b/src/services/abstract/feed.h @@ -1,110 +1,110 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FEED_H -#define FEED_H - -#include "services/abstract/rootitem.h" - -#include "core/message.h" - -#include -#include - - -// Base class for "feed" nodes. -class Feed : public RootItem, public QRunnable { - Q_OBJECT - - public: - // Specifies the auto-update strategy for the feed. - enum AutoUpdateType { - DontAutoUpdate = 0, - DefaultAutoUpdate = 1, - SpecificAutoUpdate = 2 - }; - - // Specifies the actual "status" of the feed. - // For example if it has new messages, error - // occurred, and so on. - enum Status { - Normal = 0, - NewMessages = 1, - NetworkError = 2, - ParsingError = 3, - OtherError = 4 - }; - - // Constructors. - explicit Feed(RootItem *parent = nullptr); - virtual ~Feed(); - - QList undeletedMessages() const; - - int countOfAllMessages() const; - int countOfUnreadMessages() const; - - void setCountOfAllMessages(int count_all_messages); - void setCountOfUnreadMessages(int count_unread_messages); - - QVariant data(int column, int role) const; - - int autoUpdateInitialInterval() const; - void setAutoUpdateInitialInterval(int auto_update_interval); - - AutoUpdateType autoUpdateType() const; - void setAutoUpdateType(AutoUpdateType auto_update_type); - - int autoUpdateRemainingInterval() const; - void setAutoUpdateRemainingInterval(int auto_update_remaining_interval); - - Status status() const; - void setStatus(const Status &status); - - QString url() const; - void setUrl(const QString &url); - - // Runs update in thread (thread pooled). - void run(); - - public slots: - void updateCounts(bool including_total_count); - int updateMessages(const QList &messages, bool error_during_obtaining); - - protected: - QString getAutoUpdateStatusDescription() const; - - signals: - void messagesObtained(QList messages, bool error_during_obtaining); - - private: - // Performs synchronous obtaining of new messages for this feed. - virtual QList obtainNewMessages(bool *error_during_obtaining) = 0; - - private: - QString m_url; - Status m_status; - AutoUpdateType m_autoUpdateType; - int m_autoUpdateInitialInterval; - int m_autoUpdateRemainingInterval; - int m_totalCount; - int m_unreadCount; -}; - -Q_DECLARE_METATYPE(Feed::AutoUpdateType) - -#endif // FEED_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FEED_H +#define FEED_H + +#include "services/abstract/rootitem.h" + +#include "core/message.h" + +#include +#include + + +// Base class for "feed" nodes. +class Feed : public RootItem, public QRunnable { + Q_OBJECT + + public: + // Specifies the auto-update strategy for the feed. + enum AutoUpdateType { + DontAutoUpdate = 0, + DefaultAutoUpdate = 1, + SpecificAutoUpdate = 2 + }; + + // Specifies the actual "status" of the feed. + // For example if it has new messages, error + // occurred, and so on. + enum Status { + Normal = 0, + NewMessages = 1, + NetworkError = 2, + ParsingError = 3, + OtherError = 4 + }; + + // Constructors. + explicit Feed(RootItem* parent = nullptr); + virtual ~Feed(); + + QList undeletedMessages() const; + + int countOfAllMessages() const; + int countOfUnreadMessages() const; + + void setCountOfAllMessages(int count_all_messages); + void setCountOfUnreadMessages(int count_unread_messages); + + QVariant data(int column, int role) const; + + int autoUpdateInitialInterval() const; + void setAutoUpdateInitialInterval(int auto_update_interval); + + AutoUpdateType autoUpdateType() const; + void setAutoUpdateType(AutoUpdateType auto_update_type); + + int autoUpdateRemainingInterval() const; + void setAutoUpdateRemainingInterval(int auto_update_remaining_interval); + + Status status() const; + void setStatus(const Status& status); + + QString url() const; + void setUrl(const QString& url); + + // Runs update in thread (thread pooled). + void run(); + + public slots: + void updateCounts(bool including_total_count); + int updateMessages(const QList& messages, bool error_during_obtaining); + + protected: + QString getAutoUpdateStatusDescription() const; + + signals: + void messagesObtained(QList messages, bool error_during_obtaining); + + private: + // Performs synchronous obtaining of new messages for this feed. + virtual QList obtainNewMessages(bool* error_during_obtaining) = 0; + + private: + QString m_url; + Status m_status; + AutoUpdateType m_autoUpdateType; + int m_autoUpdateInitialInterval; + int m_autoUpdateRemainingInterval; + int m_totalCount; + int m_unreadCount; +}; + +Q_DECLARE_METATYPE(Feed::AutoUpdateType) + +#endif // FEED_H diff --git a/src/services/abstract/gui/formfeeddetails.cpp b/src/services/abstract/gui/formfeeddetails.cpp index c79dfdaf7..aa85e4304 100755 --- a/src/services/abstract/gui/formfeeddetails.cpp +++ b/src/services/abstract/gui/formfeeddetails.cpp @@ -1,426 +1,411 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/abstract/gui/formfeeddetails.h" - -#include "definitions/definitions.h" -#include "core/feedsmodel.h" -#include "services/abstract/rootitem.h" -#include "services/abstract/category.h" -#include "services/standard/standardserviceroot.h" -#include "services/standard/standardfeed.h" -#include "miscellaneous/textfactory.h" -#include "miscellaneous/iconfactory.h" -#include "network-web/networkfactory.h" -#include "gui/baselineedit.h" -#include "gui/messagebox.h" -#include "gui/systemtrayicon.h" - -#include -#include -#include -#include -#include -#include -#include -#include - - -FormFeedDetails::FormFeedDetails(ServiceRoot *service_root, QWidget *parent) - : QDialog(parent), - m_editableFeed(nullptr), - m_serviceRoot(service_root) { - initialize(); - createConnections(); - - // Initialize that shit. - onTitleChanged(QString()); - onDescriptionChanged(QString()); - onUrlChanged(QString()); - onUsernameChanged(QString()); - onPasswordChanged(QString()); -} - -FormFeedDetails::~FormFeedDetails() { -} - -int FormFeedDetails::addEditFeed(Feed *input_feed, RootItem *parent_to_select, const QString &url) { - // Load categories. - loadCategories(m_serviceRoot->getSubTreeCategories(), m_serviceRoot); - - if (input_feed == nullptr) { - // User is adding new category. - setWindowTitle(tr("Add new feed")); - - // Make sure that "default" icon is used as the default option for new - // feed. - m_actionUseDefaultIcon->trigger(); - - int default_encoding_index = m_ui->m_cmbEncoding->findText(DEFAULT_FEED_ENCODING); - - if (default_encoding_index >= 0) { - m_ui->m_cmbEncoding->setCurrentIndex(default_encoding_index); - } - - if (parent_to_select != nullptr) { - if (parent_to_select->kind() == RootItemKind::Category) { - m_ui->m_cmbParentCategory->setCurrentIndex(m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) parent_to_select))); - } - else if (parent_to_select->kind() == RootItemKind::Feed) { - int target_item = m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) parent_to_select->parent())); - - if (target_item >= 0) { - m_ui->m_cmbParentCategory->setCurrentIndex(target_item); - } - } - } - - if (!url.isEmpty()) { - m_ui->m_txtUrl->lineEdit()->setText(url); - } - else if (Application::clipboard()->mimeData()->hasText()) { - m_ui->m_txtUrl->lineEdit()->setText(Application::clipboard()->text()); - } - } - else { - // User is editing existing category. - setWindowTitle(tr("Edit feed '%1'").arg(input_feed->title())); - setEditableFeed(input_feed); - } - - // Run the dialog. - return QDialog::exec(); -} - -void FormFeedDetails::onTitleChanged(const QString &new_title){ - if (new_title.simplified().size() >= MIN_CATEGORY_NAME_LENGTH) { - m_ui->m_txtTitle->setStatus(LineEditWithStatus::Ok, tr("Feed name is ok.")); - } - else { - m_ui->m_txtTitle->setStatus(LineEditWithStatus::Error, tr("Feed name is too short.")); - } -} - -void FormFeedDetails::onDescriptionChanged(const QString &new_description) { - if (new_description.simplified().isEmpty()) { - m_ui->m_txtDescription->setStatus(LineEditWithStatus::Warning, tr("Description is empty.")); - } - else { - m_ui->m_txtDescription->setStatus(LineEditWithStatus::Ok, tr("The description is ok.")); - } -} - -void FormFeedDetails::onUrlChanged(const QString &new_url) { - if (QRegExp(URL_REGEXP).exactMatch(new_url)) { - // New url is well-formed. - m_ui->m_txtUrl->setStatus(LineEditWithStatus::Ok, tr("The URL is ok.")); - } - else if (!new_url.simplified().isEmpty()) { - // New url is not well-formed but is not empty on the other hand. - m_ui->m_txtUrl->setStatus(LineEditWithStatus::Warning, tr("The URL does not meet standard pattern. Does your URL start with \"http://\" or \"https://\" prefix.")); - } - else { - // New url is empty. - m_ui->m_txtUrl->setStatus(LineEditWithStatus::Error, tr("The URL is empty.")); - } -} - -void FormFeedDetails::onUsernameChanged(const QString &new_username) { - bool is_username_ok = !m_ui->m_gbAuthentication->isChecked() || !new_username.simplified().isEmpty(); - - m_ui->m_txtUsername->setStatus(is_username_ok ? - LineEditWithStatus::Ok : - LineEditWithStatus::Warning, - is_username_ok ? - tr("Username is ok or it is not needed.") : - tr("Username is empty.")); -} - -void FormFeedDetails::onPasswordChanged(const QString &new_password) { - bool is_password_ok = !m_ui->m_gbAuthentication->isChecked() || !new_password.simplified().isEmpty(); - - m_ui->m_txtPassword->setStatus(is_password_ok ? - LineEditWithStatus::Ok : - LineEditWithStatus::Warning, - is_password_ok ? - tr("Password is ok or it is not needed.") : - tr("Password is empty.")); -} - -void FormFeedDetails::onAuthenticationSwitched() { - onUsernameChanged(m_ui->m_txtUsername->lineEdit()->text()); - onPasswordChanged(m_ui->m_txtPassword->lineEdit()->text()); -} - -void FormFeedDetails::onAutoUpdateTypeChanged(int new_index) { - Feed::AutoUpdateType auto_update_type = static_cast(m_ui->m_cmbAutoUpdateType->itemData(new_index).toInt()); - - switch (auto_update_type) { - case Feed::DontAutoUpdate: - case Feed::DefaultAutoUpdate: - m_ui->m_spinAutoUpdateInterval->setEnabled(false); - break; - - case Feed::SpecificAutoUpdate: - default: - m_ui->m_spinAutoUpdateInterval->setEnabled(true); - } -} - -void FormFeedDetails::onNoIconSelected() { - m_ui->m_btnIcon->setIcon(QIcon()); -} - -void FormFeedDetails::onLoadIconFromFile() { - QFileDialog dialog(this, tr("Select icon file for the feed"), - qApp->getHomeFolderPath(), tr("Images (*.bmp *.jpg *.jpeg *.png *.svg *.tga)")); - dialog.setFileMode(QFileDialog::ExistingFile); - dialog.setWindowIcon(qApp->icons()->fromTheme(QSL("image-x-generic"))); - dialog.setOptions(QFileDialog::DontUseNativeDialog | QFileDialog::ReadOnly); - dialog.setViewMode(QFileDialog::Detail); - dialog.setLabelText(QFileDialog::Accept, tr("Select icon")); - dialog.setLabelText(QFileDialog::Reject, tr("Cancel")); - //: Label for field with icon file name textbox for selection dialog. - dialog.setLabelText(QFileDialog::LookIn, tr("Look in:")); - dialog.setLabelText(QFileDialog::FileName, tr("Icon name:")); - dialog.setLabelText(QFileDialog::FileType, tr("Icon type:")); - - if (dialog.exec() == QDialog::Accepted) { - m_ui->m_btnIcon->setIcon(QIcon(dialog.selectedFiles().value(0))); - } -} - -void FormFeedDetails::onUseDefaultIcon() { - m_ui->m_btnIcon->setIcon(qApp->icons()->fromTheme(QSL("application-rss+xml"))); -} - -void FormFeedDetails::apply() { - -} - -void FormFeedDetails::guessFeed() { - QPair result = StandardFeed::guessFeed(m_ui->m_txtUrl->lineEdit()->text(), - m_ui->m_txtUsername->lineEdit()->text(), - m_ui->m_txtPassword->lineEdit()->text()); - - if (result.first != nullptr) { - // Icon or whole feed was guessed. - m_ui->m_btnIcon->setIcon(result.first->icon()); - m_ui->m_txtTitle->lineEdit()->setText(result.first->title()); - m_ui->m_txtDescription->lineEdit()->setText(result.first->description()); - m_ui->m_cmbType->setCurrentIndex(m_ui->m_cmbType->findData(QVariant::fromValue((int) result.first->type()))); - - int encoding_index = m_ui->m_cmbEncoding->findText(result.first->encoding(), Qt::MatchFixedString); - - if (encoding_index >= 0) { - m_ui->m_cmbEncoding->setCurrentIndex(encoding_index); - } - else { - m_ui->m_cmbEncoding->setCurrentIndex(m_ui->m_cmbEncoding->findText(DEFAULT_FEED_ENCODING, - Qt::MatchFixedString)); - } - - if (result.second == QNetworkReply::NoError) { - m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Ok, - tr("All metadata fetched successfully."), - tr("Feed and icon metadata fetched.")); - } - else { - m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Warning, - tr("Result: %1.").arg(NetworkFactory::networkErrorText(result.second)), - tr("Feed or icon metadata not fetched.")); - } - - // Remove temporary feed object. - delete result.first; - } - else { - // No feed guessed, even no icon available. - m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Error, - tr("Error: %1.").arg(NetworkFactory::networkErrorText(result.second)), - tr("No metadata fetched.")); - } -} - -void FormFeedDetails::guessIconOnly() { - QPair result = StandardFeed::guessFeed(m_ui->m_txtUrl->lineEdit()->text(), - m_ui->m_txtUsername->lineEdit()->text(), - m_ui->m_txtPassword->lineEdit()->text()); - - if (result.first != nullptr) { - // Icon or whole feed was guessed. - m_ui->m_btnIcon->setIcon(result.first->icon()); - - if (result.second == QNetworkReply::NoError) { - m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Ok, - tr("Icon fetched successfully."), - tr("Icon metadata fetched.")); - } - else { - m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Warning, - tr("Result: %1.").arg(NetworkFactory::networkErrorText(result.second)), - tr("Icon metadata not fetched.")); - } - - // Remove temporary feed object. - delete result.first; - } - else { - // No feed guessed, even no icon available. - m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Error, - tr("Error: %1.").arg(NetworkFactory::networkErrorText(result.second)), - tr("No icon fetched.")); - } -} - -void FormFeedDetails::createConnections() { - // General connections. - connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &FormFeedDetails::apply); - connect(m_ui->m_txtTitle->lineEdit(), &BaseLineEdit::textChanged, this, &FormFeedDetails::onTitleChanged); - connect(m_ui->m_txtDescription->lineEdit(), &BaseLineEdit::textChanged, this, &FormFeedDetails::onDescriptionChanged); - connect(m_ui->m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &FormFeedDetails::onUrlChanged); - connect(m_ui->m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &FormFeedDetails::onUsernameChanged); - connect(m_ui->m_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &FormFeedDetails::onPasswordChanged); - connect(m_ui->m_gbAuthentication, &QGroupBox::toggled, this, &FormFeedDetails::onAuthenticationSwitched); - connect(m_ui->m_cmbAutoUpdateType, static_cast(&QComboBox::currentIndexChanged), this, &FormFeedDetails::onAutoUpdateTypeChanged); - connect(m_ui->m_btnFetchMetadata, &QPushButton::clicked, this, &FormFeedDetails::guessFeed); - - // Icon connections. - connect(m_actionFetchIcon, &QAction::triggered, this, &FormFeedDetails::guessIconOnly); - connect(m_actionLoadIconFromFile, &QAction::triggered, this, &FormFeedDetails::onLoadIconFromFile); - connect(m_actionNoIcon, &QAction::triggered, this, &FormFeedDetails::onNoIconSelected); - connect(m_actionUseDefaultIcon, &QAction::triggered, this, &FormFeedDetails::onUseDefaultIcon); -} - -void FormFeedDetails::setEditableFeed(Feed *editable_feed) { - m_editableFeed = editable_feed; - - m_ui->m_cmbParentCategory->setCurrentIndex(m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) editable_feed->parent()))); - m_ui->m_txtTitle->lineEdit()->setText(editable_feed->title()); - m_ui->m_txtDescription->lineEdit()->setText(editable_feed->description()); - m_ui->m_btnIcon->setIcon(editable_feed->icon()); - m_ui->m_txtUrl->lineEdit()->setText(editable_feed->url()); - m_ui->m_cmbAutoUpdateType->setCurrentIndex(m_ui->m_cmbAutoUpdateType->findData(QVariant::fromValue((int) editable_feed->autoUpdateType()))); - m_ui->m_spinAutoUpdateInterval->setValue(editable_feed->autoUpdateInitialInterval()); -} - -void FormFeedDetails::initialize() { - m_ui.reset(new Ui::FormFeedDetails()); - m_ui->setupUi(this); - - // Set flags and attributes. - setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint | Qt::WindowTitleHint); - setWindowIcon(qApp->icons()->fromTheme(QSL("application-rss+xml"))); - - // Set text boxes. - m_ui->m_txtTitle->lineEdit()->setPlaceholderText(tr("Feed title")); - m_ui->m_txtTitle->lineEdit()->setToolTip(tr("Set title for your feed.")); - - m_ui->m_txtDescription->lineEdit()->setPlaceholderText(tr("Feed description")); - m_ui->m_txtDescription->lineEdit()->setToolTip(tr("Set description for your feed.")); - - m_ui->m_txtUrl->lineEdit()->setPlaceholderText(tr("Full feed url including scheme")); - m_ui->m_txtUrl->lineEdit()->setToolTip(tr("Set url for your feed.")); - - m_ui->m_txtUsername->lineEdit()->setPlaceholderText(tr("Username")); - m_ui->m_txtUsername->lineEdit()->setToolTip(tr("Set username to access the feed.")); - - m_ui->m_txtPassword->lineEdit()->setPlaceholderText(tr("Password")); - m_ui->m_txtPassword->lineEdit()->setToolTip(tr("Set password to access the feed.")); - - // Add standard feed types. - m_ui->m_cmbType->addItem(StandardFeed::typeToString(StandardFeed::Atom10), QVariant::fromValue((int) StandardFeed::Atom10)); - m_ui->m_cmbType->addItem(StandardFeed::typeToString(StandardFeed::Rdf), QVariant::fromValue((int) StandardFeed::Rdf)); - m_ui->m_cmbType->addItem(StandardFeed::typeToString(StandardFeed::Rss0X), QVariant::fromValue((int) StandardFeed::Rss0X)); - m_ui->m_cmbType->addItem(StandardFeed::typeToString(StandardFeed::Rss2X), QVariant::fromValue((int) StandardFeed::Rss2X)); - - // Load available encodings. - const QList encodings = QTextCodec::availableCodecs(); - QStringList encoded_encodings; - - foreach (const QByteArray &encoding, encodings) { - encoded_encodings.append(encoding); - } - - // Sort encodings and add them. - qSort(encoded_encodings.begin(), encoded_encodings.end(), TextFactory::isCaseInsensitiveLessThan); - m_ui->m_cmbEncoding->addItems(encoded_encodings); - - // Setup menu & actions for icon selection. - m_iconMenu = new QMenu(tr("Icon selection"), this); - m_actionLoadIconFromFile = new QAction(qApp->icons()->fromTheme(QSL("image-x-generic")), - tr("Load icon from file..."), - this); - m_actionNoIcon = new QAction(qApp->icons()->fromTheme(QSL("dialog-error")), - tr("Do not use icon"), - this); - m_actionUseDefaultIcon = new QAction(qApp->icons()->fromTheme(QSL("application-rss+xml")), - tr("Use default icon"), - this); - m_actionFetchIcon = new QAction(qApp->icons()->fromTheme(QSL("emblem-downloads")), - tr("Fetch icon from feed"), - this); - m_iconMenu->addAction(m_actionFetchIcon); - m_iconMenu->addAction(m_actionLoadIconFromFile); - m_iconMenu->addAction(m_actionUseDefaultIcon); - m_iconMenu->addAction(m_actionNoIcon); - m_ui->m_btnIcon->setMenu(m_iconMenu); - - // Set feed metadata fetch label. - m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Information, - tr("No metadata fetched so far."), - tr("No metadata fetched so far.")); - - // Setup auto-update options. - m_ui->m_spinAutoUpdateInterval->setValue(DEFAULT_AUTO_UPDATE_INTERVAL); - m_ui->m_cmbAutoUpdateType->addItem(tr("Auto-update using global interval"), QVariant::fromValue((int) Feed::DefaultAutoUpdate)); - m_ui->m_cmbAutoUpdateType->addItem(tr("Auto-update every"), QVariant::fromValue((int) Feed::SpecificAutoUpdate)); - m_ui->m_cmbAutoUpdateType->addItem(tr("Do not auto-update at all"), QVariant::fromValue((int) Feed::DontAutoUpdate)); - - // Set tab order. - setTabOrder(m_ui->m_cmbParentCategory, m_ui->m_cmbType); - - setTabOrder(m_ui->m_cmbType, m_ui->m_cmbEncoding); - - setTabOrder(m_ui->m_cmbEncoding, m_ui->m_cmbAutoUpdateType); - setTabOrder(m_ui->m_cmbAutoUpdateType, m_ui->m_spinAutoUpdateInterval); - setTabOrder(m_ui->m_spinAutoUpdateInterval, m_ui->m_txtTitle->lineEdit()); - - - setTabOrder(m_ui->m_txtTitle->lineEdit(), m_ui->m_txtDescription->lineEdit()); - setTabOrder(m_ui->m_txtDescription->lineEdit(), m_ui->m_txtUrl->lineEdit()); - setTabOrder(m_ui->m_txtUrl->lineEdit(), m_ui->m_btnFetchMetadata); - - setTabOrder(m_ui->m_btnFetchMetadata, m_ui->m_btnIcon); - setTabOrder(m_ui->m_btnIcon, m_ui->m_gbAuthentication); - setTabOrder(m_ui->m_gbAuthentication, m_ui->m_txtUsername->lineEdit()); - setTabOrder(m_ui->m_txtUsername->lineEdit(), m_ui->m_txtPassword->lineEdit()); - - m_ui->m_txtUrl->lineEdit()->setFocus(Qt::TabFocusReason); -} - -void FormFeedDetails::loadCategories(const QList categories, RootItem *root_item) { - m_ui->m_cmbParentCategory->addItem(root_item->icon(), - root_item->title(), - QVariant::fromValue((void*) root_item)); - - foreach (Category *category, categories) { - m_ui->m_cmbParentCategory->addItem(category->icon(), - category->title(), - QVariant::fromValue((void*) category)); - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/abstract/gui/formfeeddetails.h" + +#include "definitions/definitions.h" +#include "core/feedsmodel.h" +#include "services/abstract/rootitem.h" +#include "services/abstract/category.h" +#include "services/standard/standardserviceroot.h" +#include "services/standard/standardfeed.h" +#include "miscellaneous/textfactory.h" +#include "miscellaneous/iconfactory.h" +#include "network-web/networkfactory.h" +#include "gui/baselineedit.h" +#include "gui/messagebox.h" +#include "gui/systemtrayicon.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +FormFeedDetails::FormFeedDetails(ServiceRoot* service_root, QWidget* parent) + : QDialog(parent), + m_editableFeed(nullptr), + m_serviceRoot(service_root) { + initialize(); + createConnections(); + // Initialize that shit. + onTitleChanged(QString()); + onDescriptionChanged(QString()); + onUrlChanged(QString()); + onUsernameChanged(QString()); + onPasswordChanged(QString()); +} + +FormFeedDetails::~FormFeedDetails() { +} + +int FormFeedDetails::addEditFeed(Feed* input_feed, RootItem* parent_to_select, const QString& url) { + // Load categories. + loadCategories(m_serviceRoot->getSubTreeCategories(), m_serviceRoot); + + if (input_feed == nullptr) { + // User is adding new category. + setWindowTitle(tr("Add new feed")); + // Make sure that "default" icon is used as the default option for new + // feed. + m_actionUseDefaultIcon->trigger(); + int default_encoding_index = m_ui->m_cmbEncoding->findText(DEFAULT_FEED_ENCODING); + + if (default_encoding_index >= 0) { + m_ui->m_cmbEncoding->setCurrentIndex(default_encoding_index); + } + + if (parent_to_select != nullptr) { + if (parent_to_select->kind() == RootItemKind::Category) { + m_ui->m_cmbParentCategory->setCurrentIndex(m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) parent_to_select))); + } + + else if (parent_to_select->kind() == RootItemKind::Feed) { + int target_item = m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) parent_to_select->parent())); + + if (target_item >= 0) { + m_ui->m_cmbParentCategory->setCurrentIndex(target_item); + } + } + } + + if (!url.isEmpty()) { + m_ui->m_txtUrl->lineEdit()->setText(url); + } + + else if (Application::clipboard()->mimeData()->hasText()) { + m_ui->m_txtUrl->lineEdit()->setText(Application::clipboard()->text()); + } + } + + else { + // User is editing existing category. + setWindowTitle(tr("Edit feed '%1'").arg(input_feed->title())); + setEditableFeed(input_feed); + } + + // Run the dialog. + return QDialog::exec(); +} + +void FormFeedDetails::onTitleChanged(const QString& new_title) { + if (new_title.simplified().size() >= MIN_CATEGORY_NAME_LENGTH) { + m_ui->m_txtTitle->setStatus(LineEditWithStatus::Ok, tr("Feed name is ok.")); + } + + else { + m_ui->m_txtTitle->setStatus(LineEditWithStatus::Error, tr("Feed name is too short.")); + } +} + +void FormFeedDetails::onDescriptionChanged(const QString& new_description) { + if (new_description.simplified().isEmpty()) { + m_ui->m_txtDescription->setStatus(LineEditWithStatus::Warning, tr("Description is empty.")); + } + + else { + m_ui->m_txtDescription->setStatus(LineEditWithStatus::Ok, tr("The description is ok.")); + } +} + +void FormFeedDetails::onUrlChanged(const QString& new_url) { + if (QRegExp(URL_REGEXP).exactMatch(new_url)) { + // New url is well-formed. + m_ui->m_txtUrl->setStatus(LineEditWithStatus::Ok, tr("The URL is ok.")); + } + + else if (!new_url.simplified().isEmpty()) { + // New url is not well-formed but is not empty on the other hand. + m_ui->m_txtUrl->setStatus(LineEditWithStatus::Warning, tr("The URL does not meet standard pattern. Does your URL start with \"http://\" or \"https://\" prefix.")); + } + + else { + // New url is empty. + m_ui->m_txtUrl->setStatus(LineEditWithStatus::Error, tr("The URL is empty.")); + } +} + +void FormFeedDetails::onUsernameChanged(const QString& new_username) { + bool is_username_ok = !m_ui->m_gbAuthentication->isChecked() || !new_username.simplified().isEmpty(); + m_ui->m_txtUsername->setStatus(is_username_ok ? + LineEditWithStatus::Ok : + LineEditWithStatus::Warning, + is_username_ok ? + tr("Username is ok or it is not needed.") : + tr("Username is empty.")); +} + +void FormFeedDetails::onPasswordChanged(const QString& new_password) { + bool is_password_ok = !m_ui->m_gbAuthentication->isChecked() || !new_password.simplified().isEmpty(); + m_ui->m_txtPassword->setStatus(is_password_ok ? + LineEditWithStatus::Ok : + LineEditWithStatus::Warning, + is_password_ok ? + tr("Password is ok or it is not needed.") : + tr("Password is empty.")); +} + +void FormFeedDetails::onAuthenticationSwitched() { + onUsernameChanged(m_ui->m_txtUsername->lineEdit()->text()); + onPasswordChanged(m_ui->m_txtPassword->lineEdit()->text()); +} + +void FormFeedDetails::onAutoUpdateTypeChanged(int new_index) { + Feed::AutoUpdateType auto_update_type = static_cast(m_ui->m_cmbAutoUpdateType->itemData(new_index).toInt()); + + switch (auto_update_type) { + case Feed::DontAutoUpdate: + case Feed::DefaultAutoUpdate: + m_ui->m_spinAutoUpdateInterval->setEnabled(false); + break; + + case Feed::SpecificAutoUpdate: + default: + m_ui->m_spinAutoUpdateInterval->setEnabled(true); + } +} + +void FormFeedDetails::onNoIconSelected() { + m_ui->m_btnIcon->setIcon(QIcon()); +} + +void FormFeedDetails::onLoadIconFromFile() { + QFileDialog dialog(this, tr("Select icon file for the feed"), + qApp->getHomeFolderPath(), tr("Images (*.bmp *.jpg *.jpeg *.png *.svg *.tga)")); + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.setWindowIcon(qApp->icons()->fromTheme(QSL("image-x-generic"))); + dialog.setOptions(QFileDialog::DontUseNativeDialog | QFileDialog::ReadOnly); + dialog.setViewMode(QFileDialog::Detail); + dialog.setLabelText(QFileDialog::Accept, tr("Select icon")); + dialog.setLabelText(QFileDialog::Reject, tr("Cancel")); + //: Label for field with icon file name textbox for selection dialog. + dialog.setLabelText(QFileDialog::LookIn, tr("Look in:")); + dialog.setLabelText(QFileDialog::FileName, tr("Icon name:")); + dialog.setLabelText(QFileDialog::FileType, tr("Icon type:")); + + if (dialog.exec() == QDialog::Accepted) { + m_ui->m_btnIcon->setIcon(QIcon(dialog.selectedFiles().value(0))); + } +} + +void FormFeedDetails::onUseDefaultIcon() { + m_ui->m_btnIcon->setIcon(qApp->icons()->fromTheme(QSL("application-rss+xml"))); +} + +void FormFeedDetails::apply() { +} + +void FormFeedDetails::guessFeed() { + QPair result = StandardFeed::guessFeed(m_ui->m_txtUrl->lineEdit()->text(), + m_ui->m_txtUsername->lineEdit()->text(), + m_ui->m_txtPassword->lineEdit()->text()); + + if (result.first != nullptr) { + // Icon or whole feed was guessed. + m_ui->m_btnIcon->setIcon(result.first->icon()); + m_ui->m_txtTitle->lineEdit()->setText(result.first->title()); + m_ui->m_txtDescription->lineEdit()->setText(result.first->description()); + m_ui->m_cmbType->setCurrentIndex(m_ui->m_cmbType->findData(QVariant::fromValue((int) result.first->type()))); + int encoding_index = m_ui->m_cmbEncoding->findText(result.first->encoding(), Qt::MatchFixedString); + + if (encoding_index >= 0) { + m_ui->m_cmbEncoding->setCurrentIndex(encoding_index); + } + + else { + m_ui->m_cmbEncoding->setCurrentIndex(m_ui->m_cmbEncoding->findText(DEFAULT_FEED_ENCODING, + Qt::MatchFixedString)); + } + + if (result.second == QNetworkReply::NoError) { + m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Ok, + tr("All metadata fetched successfully."), + tr("Feed and icon metadata fetched.")); + } + + else { + m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Warning, + tr("Result: %1.").arg(NetworkFactory::networkErrorText(result.second)), + tr("Feed or icon metadata not fetched.")); + } + + // Remove temporary feed object. + delete result.first; + } + + else { + // No feed guessed, even no icon available. + m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Error, + tr("Error: %1.").arg(NetworkFactory::networkErrorText(result.second)), + tr("No metadata fetched.")); + } +} + +void FormFeedDetails::guessIconOnly() { + QPair result = StandardFeed::guessFeed(m_ui->m_txtUrl->lineEdit()->text(), + m_ui->m_txtUsername->lineEdit()->text(), + m_ui->m_txtPassword->lineEdit()->text()); + + if (result.first != nullptr) { + // Icon or whole feed was guessed. + m_ui->m_btnIcon->setIcon(result.first->icon()); + + if (result.second == QNetworkReply::NoError) { + m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Ok, + tr("Icon fetched successfully."), + tr("Icon metadata fetched.")); + } + + else { + m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Warning, + tr("Result: %1.").arg(NetworkFactory::networkErrorText(result.second)), + tr("Icon metadata not fetched.")); + } + + // Remove temporary feed object. + delete result.first; + } + + else { + // No feed guessed, even no icon available. + m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Error, + tr("Error: %1.").arg(NetworkFactory::networkErrorText(result.second)), + tr("No icon fetched.")); + } +} + +void FormFeedDetails::createConnections() { + // General connections. + connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &FormFeedDetails::apply); + connect(m_ui->m_txtTitle->lineEdit(), &BaseLineEdit::textChanged, this, &FormFeedDetails::onTitleChanged); + connect(m_ui->m_txtDescription->lineEdit(), &BaseLineEdit::textChanged, this, &FormFeedDetails::onDescriptionChanged); + connect(m_ui->m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &FormFeedDetails::onUrlChanged); + connect(m_ui->m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &FormFeedDetails::onUsernameChanged); + connect(m_ui->m_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &FormFeedDetails::onPasswordChanged); + connect(m_ui->m_gbAuthentication, &QGroupBox::toggled, this, &FormFeedDetails::onAuthenticationSwitched); + connect(m_ui->m_cmbAutoUpdateType, static_cast(&QComboBox::currentIndexChanged), this, &FormFeedDetails::onAutoUpdateTypeChanged); + connect(m_ui->m_btnFetchMetadata, &QPushButton::clicked, this, &FormFeedDetails::guessFeed); + // Icon connections. + connect(m_actionFetchIcon, &QAction::triggered, this, &FormFeedDetails::guessIconOnly); + connect(m_actionLoadIconFromFile, &QAction::triggered, this, &FormFeedDetails::onLoadIconFromFile); + connect(m_actionNoIcon, &QAction::triggered, this, &FormFeedDetails::onNoIconSelected); + connect(m_actionUseDefaultIcon, &QAction::triggered, this, &FormFeedDetails::onUseDefaultIcon); +} + +void FormFeedDetails::setEditableFeed(Feed* editable_feed) { + m_editableFeed = editable_feed; + m_ui->m_cmbParentCategory->setCurrentIndex(m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) editable_feed->parent()))); + m_ui->m_txtTitle->lineEdit()->setText(editable_feed->title()); + m_ui->m_txtDescription->lineEdit()->setText(editable_feed->description()); + m_ui->m_btnIcon->setIcon(editable_feed->icon()); + m_ui->m_txtUrl->lineEdit()->setText(editable_feed->url()); + m_ui->m_cmbAutoUpdateType->setCurrentIndex(m_ui->m_cmbAutoUpdateType->findData(QVariant::fromValue((int) editable_feed->autoUpdateType()))); + m_ui->m_spinAutoUpdateInterval->setValue(editable_feed->autoUpdateInitialInterval()); +} + +void FormFeedDetails::initialize() { + m_ui.reset(new Ui::FormFeedDetails()); + m_ui->setupUi(this); + // Set flags and attributes. + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint | Qt::WindowTitleHint); + setWindowIcon(qApp->icons()->fromTheme(QSL("application-rss+xml"))); + // Set text boxes. + m_ui->m_txtTitle->lineEdit()->setPlaceholderText(tr("Feed title")); + m_ui->m_txtTitle->lineEdit()->setToolTip(tr("Set title for your feed.")); + m_ui->m_txtDescription->lineEdit()->setPlaceholderText(tr("Feed description")); + m_ui->m_txtDescription->lineEdit()->setToolTip(tr("Set description for your feed.")); + m_ui->m_txtUrl->lineEdit()->setPlaceholderText(tr("Full feed url including scheme")); + m_ui->m_txtUrl->lineEdit()->setToolTip(tr("Set url for your feed.")); + m_ui->m_txtUsername->lineEdit()->setPlaceholderText(tr("Username")); + m_ui->m_txtUsername->lineEdit()->setToolTip(tr("Set username to access the feed.")); + m_ui->m_txtPassword->lineEdit()->setPlaceholderText(tr("Password")); + m_ui->m_txtPassword->lineEdit()->setToolTip(tr("Set password to access the feed.")); + // Add standard feed types. + m_ui->m_cmbType->addItem(StandardFeed::typeToString(StandardFeed::Atom10), QVariant::fromValue((int) StandardFeed::Atom10)); + m_ui->m_cmbType->addItem(StandardFeed::typeToString(StandardFeed::Rdf), QVariant::fromValue((int) StandardFeed::Rdf)); + m_ui->m_cmbType->addItem(StandardFeed::typeToString(StandardFeed::Rss0X), QVariant::fromValue((int) StandardFeed::Rss0X)); + m_ui->m_cmbType->addItem(StandardFeed::typeToString(StandardFeed::Rss2X), QVariant::fromValue((int) StandardFeed::Rss2X)); + // Load available encodings. + const QList encodings = QTextCodec::availableCodecs(); + QStringList encoded_encodings; + + foreach (const QByteArray& encoding, encodings) { + encoded_encodings.append(encoding); + } + + // Sort encodings and add them. + qSort(encoded_encodings.begin(), encoded_encodings.end(), TextFactory::isCaseInsensitiveLessThan); + m_ui->m_cmbEncoding->addItems(encoded_encodings); + // Setup menu & actions for icon selection. + m_iconMenu = new QMenu(tr("Icon selection"), this); + m_actionLoadIconFromFile = new QAction(qApp->icons()->fromTheme(QSL("image-x-generic")), + tr("Load icon from file..."), + this); + m_actionNoIcon = new QAction(qApp->icons()->fromTheme(QSL("dialog-error")), + tr("Do not use icon"), + this); + m_actionUseDefaultIcon = new QAction(qApp->icons()->fromTheme(QSL("application-rss+xml")), + tr("Use default icon"), + this); + m_actionFetchIcon = new QAction(qApp->icons()->fromTheme(QSL("emblem-downloads")), + tr("Fetch icon from feed"), + this); + m_iconMenu->addAction(m_actionFetchIcon); + m_iconMenu->addAction(m_actionLoadIconFromFile); + m_iconMenu->addAction(m_actionUseDefaultIcon); + m_iconMenu->addAction(m_actionNoIcon); + m_ui->m_btnIcon->setMenu(m_iconMenu); + // Set feed metadata fetch label. + m_ui->m_lblFetchMetadata->setStatus(WidgetWithStatus::Information, + tr("No metadata fetched so far."), + tr("No metadata fetched so far.")); + // Setup auto-update options. + m_ui->m_spinAutoUpdateInterval->setValue(DEFAULT_AUTO_UPDATE_INTERVAL); + m_ui->m_cmbAutoUpdateType->addItem(tr("Auto-update using global interval"), QVariant::fromValue((int) Feed::DefaultAutoUpdate)); + m_ui->m_cmbAutoUpdateType->addItem(tr("Auto-update every"), QVariant::fromValue((int) Feed::SpecificAutoUpdate)); + m_ui->m_cmbAutoUpdateType->addItem(tr("Do not auto-update at all"), QVariant::fromValue((int) Feed::DontAutoUpdate)); + // Set tab order. + setTabOrder(m_ui->m_cmbParentCategory, m_ui->m_cmbType); + setTabOrder(m_ui->m_cmbType, m_ui->m_cmbEncoding); + setTabOrder(m_ui->m_cmbEncoding, m_ui->m_cmbAutoUpdateType); + setTabOrder(m_ui->m_cmbAutoUpdateType, m_ui->m_spinAutoUpdateInterval); + setTabOrder(m_ui->m_spinAutoUpdateInterval, m_ui->m_txtTitle->lineEdit()); + setTabOrder(m_ui->m_txtTitle->lineEdit(), m_ui->m_txtDescription->lineEdit()); + setTabOrder(m_ui->m_txtDescription->lineEdit(), m_ui->m_txtUrl->lineEdit()); + setTabOrder(m_ui->m_txtUrl->lineEdit(), m_ui->m_btnFetchMetadata); + setTabOrder(m_ui->m_btnFetchMetadata, m_ui->m_btnIcon); + setTabOrder(m_ui->m_btnIcon, m_ui->m_gbAuthentication); + setTabOrder(m_ui->m_gbAuthentication, m_ui->m_txtUsername->lineEdit()); + setTabOrder(m_ui->m_txtUsername->lineEdit(), m_ui->m_txtPassword->lineEdit()); + m_ui->m_txtUrl->lineEdit()->setFocus(Qt::TabFocusReason); +} + +void FormFeedDetails::loadCategories(const QList categories, RootItem* root_item) { + m_ui->m_cmbParentCategory->addItem(root_item->icon(), + root_item->title(), + QVariant::fromValue((void*) root_item)); + + foreach (Category* category, categories) { + m_ui->m_cmbParentCategory->addItem(category->icon(), + category->title(), + QVariant::fromValue((void*) category)); + } +} diff --git a/src/services/abstract/gui/formfeeddetails.h b/src/services/abstract/gui/formfeeddetails.h old mode 100644 new mode 100755 index 340707378..fae253785 --- a/src/services/abstract/gui/formfeeddetails.h +++ b/src/services/abstract/gui/formfeeddetails.h @@ -1,97 +1,97 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FORMFEEDDETAILS_H -#define FORMFEEDDETAILS_H - -#include - -#include "ui_formfeeddetails.h" - - -namespace Ui { - class FormFeedDetails; -} - -class ServiceRoot; -class Feed; -class Category; -class RootItem; - -class FormFeedDetails : public QDialog { - Q_OBJECT - - public: - // Constructors and destructors. - explicit FormFeedDetails(ServiceRoot *service_root, QWidget *parent = 0); - virtual ~FormFeedDetails(); - - public slots: - // Executes add/edit standard feed dialog. - int addEditFeed(Feed *input_feed, RootItem *parent_to_select, const QString &url = QString()); - - protected slots: - // Applies changes. - // NOTE: This must be reimplemented in subclasses. Also this - // base implementation must be called first. - virtual void apply() = 0; - - void guessFeed(); - void guessIconOnly(); - - // Trigerred when title/description/url/username/password changes. - void onTitleChanged(const QString &new_title); - void onDescriptionChanged(const QString &new_description); - void onUrlChanged(const QString &new_url); - void onUsernameChanged(const QString &new_username); - void onPasswordChanged(const QString &new_password); - void onAuthenticationSwitched(); - void onAutoUpdateTypeChanged(int new_index); - - // Icon selectors. - void onNoIconSelected(); - void onLoadIconFromFile(); - void onUseDefaultIcon(); - - protected: - // Sets the feed which will be edited. - // NOTE: This must be reimplemented in subclasses. Also this - // base implementation must be called first. - void virtual setEditableFeed(Feed *editable_feed); - - // Creates needed connections. - void createConnections(); - - // Initializes the dialog. - void initialize(); - - // Loads categories into the dialog from the model. - void loadCategories(const QList categories, RootItem *root_item); - - protected: - QScopedPointer m_ui; - Feed *m_editableFeed; - ServiceRoot *m_serviceRoot; - - QMenu *m_iconMenu; - QAction *m_actionLoadIconFromFile; - QAction *m_actionUseDefaultIcon; - QAction *m_actionFetchIcon; - QAction *m_actionNoIcon; -}; - -#endif // FORMFEEDDETAILS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FORMFEEDDETAILS_H +#define FORMFEEDDETAILS_H + +#include + +#include "ui_formfeeddetails.h" + + +namespace Ui { + class FormFeedDetails; +} + +class ServiceRoot; +class Feed; +class Category; +class RootItem; + +class FormFeedDetails : public QDialog { + Q_OBJECT + + public: + // Constructors and destructors. + explicit FormFeedDetails(ServiceRoot* service_root, QWidget* parent = 0); + virtual ~FormFeedDetails(); + + public slots: + // Executes add/edit standard feed dialog. + int addEditFeed(Feed* input_feed, RootItem* parent_to_select, const QString& url = QString()); + + protected slots: + // Applies changes. + // NOTE: This must be reimplemented in subclasses. Also this + // base implementation must be called first. + virtual void apply() = 0; + + void guessFeed(); + void guessIconOnly(); + + // Trigerred when title/description/url/username/password changes. + void onTitleChanged(const QString& new_title); + void onDescriptionChanged(const QString& new_description); + void onUrlChanged(const QString& new_url); + void onUsernameChanged(const QString& new_username); + void onPasswordChanged(const QString& new_password); + void onAuthenticationSwitched(); + void onAutoUpdateTypeChanged(int new_index); + + // Icon selectors. + void onNoIconSelected(); + void onLoadIconFromFile(); + void onUseDefaultIcon(); + + protected: + // Sets the feed which will be edited. + // NOTE: This must be reimplemented in subclasses. Also this + // base implementation must be called first. + void virtual setEditableFeed(Feed* editable_feed); + + // Creates needed connections. + void createConnections(); + + // Initializes the dialog. + void initialize(); + + // Loads categories into the dialog from the model. + void loadCategories(const QList categories, RootItem* root_item); + + protected: + QScopedPointer m_ui; + Feed* m_editableFeed; + ServiceRoot* m_serviceRoot; + + QMenu* m_iconMenu; + QAction* m_actionLoadIconFromFile; + QAction* m_actionUseDefaultIcon; + QAction* m_actionFetchIcon; + QAction* m_actionNoIcon; +}; + +#endif // FORMFEEDDETAILS_H diff --git a/src/services/abstract/label.cpp b/src/services/abstract/label.cpp index a831d3bd9..ed2f8f6b6 100755 --- a/src/services/abstract/label.cpp +++ b/src/services/abstract/label.cpp @@ -1,22 +1,22 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/abstract/label.h" - - -Label::Label(RootItem *parent_item) : RootItem(parent_item) { -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/abstract/label.h" + + +Label::Label(RootItem* parent_item) : RootItem(parent_item) { +} diff --git a/src/services/abstract/label.h b/src/services/abstract/label.h index 853f4888a..15cab172a 100755 --- a/src/services/abstract/label.h +++ b/src/services/abstract/label.h @@ -1,29 +1,29 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef LABEL_H -#define LABEL_H - -#include "services/abstract/rootitem.h" - - -class Label : public RootItem { - public: - explicit Label(RootItem *parent_item = nullptr); -}; - -#endif // LABEL_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef LABEL_H +#define LABEL_H + +#include "services/abstract/rootitem.h" + + +class Label : public RootItem { + public: + explicit Label(RootItem* parent_item = nullptr); +}; + +#endif // LABEL_H diff --git a/src/services/abstract/labelsrootitem.cpp b/src/services/abstract/labelsrootitem.cpp index 471d9133e..fa1b24c48 100755 --- a/src/services/abstract/labelsrootitem.cpp +++ b/src/services/abstract/labelsrootitem.cpp @@ -1,28 +1,27 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/abstract/labelsrootitem.h" - -#include "miscellaneous/iconfactory.h" - - -LabelsRootItem::LabelsRootItem(RootItem *parent_item) : RootItem(parent_item) { - setTitle(tr("Labels")); - setIcon(qApp->icons()->fromTheme(QSL("stock_bookmark"))); - - setKind(RootItemKind::LabelsRoot); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/abstract/labelsrootitem.h" + +#include "miscellaneous/iconfactory.h" + + +LabelsRootItem::LabelsRootItem(RootItem* parent_item) : RootItem(parent_item) { + setTitle(tr("Labels")); + setIcon(qApp->icons()->fromTheme(QSL("stock_bookmark"))); + setKind(RootItemKind::LabelsRoot); +} diff --git a/src/services/abstract/labelsrootitem.h b/src/services/abstract/labelsrootitem.h index 1031864b3..c267dbdea 100755 --- a/src/services/abstract/labelsrootitem.h +++ b/src/services/abstract/labelsrootitem.h @@ -1,29 +1,29 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef LABELSROOTITEM_H -#define LABELSROOTITEM_H - -#include "services/abstract/rootitem.h" - - -class LabelsRootItem : public RootItem { - public: - explicit LabelsRootItem(RootItem *parent_item = nullptr); -}; - -#endif // LABELSROOTITEM_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef LABELSROOTITEM_H +#define LABELSROOTITEM_H + +#include "services/abstract/rootitem.h" + + +class LabelsRootItem : public RootItem { + public: + explicit LabelsRootItem(RootItem* parent_item = nullptr); +}; + +#endif // LABELSROOTITEM_H diff --git a/src/services/abstract/recyclebin.cpp b/src/services/abstract/recyclebin.cpp index 4e33efad4..a7465ee20 100755 --- a/src/services/abstract/recyclebin.cpp +++ b/src/services/abstract/recyclebin.cpp @@ -26,121 +26,120 @@ #include -RecycleBin::RecycleBin(RootItem *parent_item) : RootItem(parent_item), m_totalCount(0), - m_unreadCount(0), m_contextMenu(QList()) { - setKind(RootItemKind::Bin); - setId(ID_RECYCLE_BIN); - setIcon(qApp->icons()->fromTheme(QSL("user-trash"))); - setTitle(tr("Recycle bin")); - setDescription(tr("Recycle bin contains all deleted messages from all feeds.")); - setCreationDate(QDateTime::currentDateTime()); +RecycleBin::RecycleBin(RootItem* parent_item) : RootItem(parent_item), m_totalCount(0), + m_unreadCount(0), m_contextMenu(QList()) { + setKind(RootItemKind::Bin); + setId(ID_RECYCLE_BIN); + setIcon(qApp->icons()->fromTheme(QSL("user-trash"))); + setTitle(tr("Recycle bin")); + setDescription(tr("Recycle bin contains all deleted messages from all feeds.")); + setCreationDate(QDateTime::currentDateTime()); } RecycleBin::~RecycleBin() { } int RecycleBin::countOfUnreadMessages() const { - return m_unreadCount; + return m_unreadCount; } int RecycleBin::countOfAllMessages() const { - return m_totalCount; + return m_totalCount; } void RecycleBin::updateCounts(bool update_total_count) { - bool is_main_thread = QThread::currentThread() == qApp->thread(); - QSqlDatabase database = is_main_thread ? - qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings) : - qApp->database()->connection(QSL("feed_upd"), DatabaseFactory::FromSettings); + bool is_main_thread = QThread::currentThread() == qApp->thread(); + QSqlDatabase database = is_main_thread ? + qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings) : + qApp->database()->connection(QSL("feed_upd"), DatabaseFactory::FromSettings); + m_unreadCount = DatabaseQueries::getMessageCountsForBin(database, getParentServiceRoot()->accountId(), false); - m_unreadCount = DatabaseQueries::getMessageCountsForBin(database, getParentServiceRoot()->accountId(), false); - - if (update_total_count) { - m_totalCount = DatabaseQueries::getMessageCountsForBin(database, getParentServiceRoot()->accountId(), true); - } + if (update_total_count) { + m_totalCount = DatabaseQueries::getMessageCountsForBin(database, getParentServiceRoot()->accountId(), true); + } } QVariant RecycleBin::data(int column, int role) const { - switch (role) { - case Qt::ToolTipRole: - return tr("Recycle bin\n\n%1").arg(tr("%n deleted message(s).", 0, countOfAllMessages())); + switch (role) { + case Qt::ToolTipRole: + return tr("Recycle bin\n\n%1").arg(tr("%n deleted message(s).", 0, countOfAllMessages())); - default: - return RootItem::data(column, role); - } + default: + return RootItem::data(column, role); + } } QList RecycleBin::contextMenu() { - if (m_contextMenu.isEmpty()) { - QAction *restore_action = new QAction(qApp->icons()->fromTheme(QSL("recycle-bin-restore-all")), - tr("Restore recycle bin"), - this); - QAction *empty_action = new QAction(qApp->icons()->fromTheme(QSL("recycle-bin-empty")), - tr("Empty recycle bin"), - this); + if (m_contextMenu.isEmpty()) { + QAction* restore_action = new QAction(qApp->icons()->fromTheme(QSL("recycle-bin-restore-all")), + tr("Restore recycle bin"), + this); + QAction* empty_action = new QAction(qApp->icons()->fromTheme(QSL("recycle-bin-empty")), + tr("Empty recycle bin"), + this); + connect(restore_action, &QAction::triggered, this, &RecycleBin::restore); + connect(empty_action, &QAction::triggered, this, &RecycleBin::empty); + m_contextMenu.append(restore_action); + m_contextMenu.append(empty_action); + } - connect(restore_action, &QAction::triggered, this, &RecycleBin::restore); - connect(empty_action, &QAction::triggered, this, &RecycleBin::empty); - - m_contextMenu.append(restore_action); - m_contextMenu.append(empty_action); - } - - return m_contextMenu; + return m_contextMenu; } QList RecycleBin::undeletedMessages() const { - const int account_id = getParentServiceRoot()->accountId(); - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - return DatabaseQueries::getUndeletedMessagesForBin(database, account_id); + const int account_id = getParentServiceRoot()->accountId(); + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + return DatabaseQueries::getUndeletedMessagesForBin(database, account_id); } bool RecycleBin::markAsReadUnread(RootItem::ReadStatus status) { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - ServiceRoot *parent_root = getParentServiceRoot(); + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + ServiceRoot* parent_root = getParentServiceRoot(); - if (DatabaseQueries::markBinReadUnread(database, parent_root->accountId(), status)) { - updateCounts(false); - parent_root->itemChanged(QList() << this); - parent_root->requestReloadMessageList(status == RootItem::Read); - return true; - } - else { - return false; - } + if (DatabaseQueries::markBinReadUnread(database, parent_root->accountId(), status)) { + updateCounts(false); + parent_root->itemChanged(QList() << this); + parent_root->requestReloadMessageList(status == RootItem::Read); + return true; + } + + else { + return false; + } } bool RecycleBin::cleanMessages(bool clear_only_read) { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - ServiceRoot *parent_root = getParentServiceRoot(); + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + ServiceRoot* parent_root = getParentServiceRoot(); - if (DatabaseQueries::purgeMessagesFromBin(database, clear_only_read, parent_root->accountId())) { - updateCounts(true); - parent_root->itemChanged(QList() << this); - parent_root->requestReloadMessageList(true); - return true;; - } - else { - return false; - } + if (DatabaseQueries::purgeMessagesFromBin(database, clear_only_read, parent_root->accountId())) { + updateCounts(true); + parent_root->itemChanged(QList() << this); + parent_root->requestReloadMessageList(true); + return true;; + } + + else { + return false; + } } bool RecycleBin::empty() { - return cleanMessages(false); + return cleanMessages(false); } bool RecycleBin::restore() { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - ServiceRoot *parent_root = getParentServiceRoot(); + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + ServiceRoot* parent_root = getParentServiceRoot(); - if (DatabaseQueries::restoreBin(database, parent_root->accountId())) { - parent_root->updateCounts(true); - parent_root->itemChanged(parent_root->getSubTree()); - parent_root->requestReloadMessageList(true); - return true; - } - else { - return false; - } + if (DatabaseQueries::restoreBin(database, parent_root->accountId())) { + parent_root->updateCounts(true); + parent_root->itemChanged(parent_root->getSubTree()); + parent_root->requestReloadMessageList(true); + return true; + } + + else { + return false; + } } diff --git a/src/services/abstract/recyclebin.h b/src/services/abstract/recyclebin.h index b2cd35869..d8cd692c1 100755 --- a/src/services/abstract/recyclebin.h +++ b/src/services/abstract/recyclebin.h @@ -22,47 +22,47 @@ class RecycleBin : public RootItem { - Q_OBJECT + Q_OBJECT - public: - explicit RecycleBin(RootItem *parent_item = nullptr); - virtual ~RecycleBin(); + public: + explicit RecycleBin(RootItem* parent_item = nullptr); + virtual ~RecycleBin(); - QVariant data(int column, int role) const; + QVariant data(int column, int role) const; - QList contextMenu(); - QList undeletedMessages() const; + QList contextMenu(); + QList undeletedMessages() const; - bool markAsReadUnread(ReadStatus status); - bool cleanMessages(bool clear_only_read); + bool markAsReadUnread(ReadStatus status); + bool cleanMessages(bool clear_only_read); - int countOfUnreadMessages() const; - int countOfAllMessages() const; + int countOfUnreadMessages() const; + int countOfAllMessages() const; - void updateCounts(bool update_total_count); + void updateCounts(bool update_total_count); - public slots: - ///////////////////////////////////////// - // /* Members to override. - ///////////////////////////////////////// + public slots: + ///////////////////////////////////////// + // /* Members to override. + ///////////////////////////////////////// - // Empties the bin - removes all messages from it (does not remove - // them from DB, just permanently hide them, so that they are not - // re-downloaded). - virtual bool empty(); + // Empties the bin - removes all messages from it (does not remove + // them from DB, just permanently hide them, so that they are not + // re-downloaded). + virtual bool empty(); - // Performs complete restoration of all messages contained in the bin - virtual bool restore(); + // Performs complete restoration of all messages contained in the bin + virtual bool restore(); - ///////////////////////////////////////// - // Members to override. */ - ///////////////////////////////////////// + ///////////////////////////////////////// + // Members to override. */ + ///////////////////////////////////////// - private: - int m_totalCount; - int m_unreadCount; + private: + int m_totalCount; + int m_unreadCount; - QList m_contextMenu; + QList m_contextMenu; }; #endif // RECYCLEBIN_H diff --git a/src/services/abstract/rootitem.cpp b/src/services/abstract/rootitem.cpp index 422b781b0..c2a0fb88e 100755 --- a/src/services/abstract/rootitem.cpp +++ b/src/services/abstract/rootitem.cpp @@ -1,485 +1,488 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/abstract/rootitem.h" - -#include "services/abstract/serviceroot.h" -#include "services/abstract/feed.h" -#include "services/abstract/category.h" -#include "services/abstract/recyclebin.h" -#include "miscellaneous/application.h" - -#include - - -RootItem::RootItem(RootItem *parent_item) - : QObject(nullptr), - m_kind(RootItemKind::Root), - m_id(NO_PARENT_CATEGORY), - m_customId(NO_PARENT_CATEGORY), - m_title(QString()), - m_description(QString()), - m_icon(QIcon()), - m_creationDate(QDateTime()), - m_childItems(QList()), - m_parentItem(parent_item) { - setupFonts(); -} - -RootItem::~RootItem() { - qDeleteAll(m_childItems); -} - -QString RootItem::hashCode() const { - ServiceRoot *root = getParentServiceRoot(); - int acc_id = root == nullptr ? 0 : root->accountId(); - - return - QString::number(acc_id) + QL1S("-") + - QString::number(kind()) + QL1S("-") + - QString::number(id()); -} - -QList RootItem::contextMenu() { - return QList(); -} - -bool RootItem::canBeEdited() const { - return false; -} - -bool RootItem::editViaGui() { - return false; -} - -bool RootItem::canBeDeleted() const { - return false; -} - -bool RootItem::deleteViaGui() { - return false; -} - -bool RootItem::markAsReadUnread(ReadStatus status) { - bool result = true; - - foreach (RootItem *child, m_childItems) { - result &= child->markAsReadUnread(status); - } - - return result; -} - -QList RootItem::undeletedMessages() const { - QList messages; - - foreach (RootItem *child, m_childItems) { - messages.append(child->undeletedMessages()); - } - - return messages; -} - -bool RootItem::cleanMessages(bool clear_only_read) { - bool result = true; - - foreach (RootItem *child, m_childItems) { - if (child->kind() != RootItemKind::Bin) { - result &= child->cleanMessages(clear_only_read); - } - } - - return result; -} - -void RootItem::updateCounts(bool including_total_count) { - foreach (RootItem *child, m_childItems) { - child->updateCounts(including_total_count); - } -} - -void RootItem::setupFonts() { - m_normalFont = Application::font("FeedsView"); - m_boldFont = m_normalFont; - m_boldFont.setBold(true); -} - -int RootItem::row() const { - if (m_parentItem) { - return m_parentItem->m_childItems.indexOf(const_cast(this)); - } - else { - // This item has no parent. Therefore, its row index is 0. - return 0; - } -} - -QVariant RootItem::data(int column, int role) const { - Q_UNUSED(column) - Q_UNUSED(role) - - switch (role) { - case Qt::ToolTipRole: - if (column == FDS_MODEL_TITLE_INDEX) { - return m_title; - } - else if (column == FDS_MODEL_COUNTS_INDEX) { - //: Tooltip for "unread" column of feed list. - return tr("%n unread message(s).", 0, countOfUnreadMessages()); - } - else { - return QVariant(); - } - - case Qt::EditRole: - if (column == FDS_MODEL_TITLE_INDEX) { - return m_title; - } - else if (column == FDS_MODEL_COUNTS_INDEX) { - return countOfUnreadMessages(); - } - else { - return QVariant(); - } - - case Qt::FontRole: - return countOfUnreadMessages() > 0 ? m_boldFont : m_normalFont; - - case Qt::DisplayRole: - if (column == FDS_MODEL_TITLE_INDEX) { - return m_title; - } - else if (column == FDS_MODEL_COUNTS_INDEX) { - int count_all = countOfAllMessages(); - int count_unread = countOfUnreadMessages(); - - return qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::CountFormat)).toString() - .replace(PLACEHOLDER_UNREAD_COUNTS, count_unread < 0 ? QSL("-") : QString::number(count_unread)) - .replace(PLACEHOLDER_ALL_COUNTS, count_all < 0 ? QSL("-") : QString::number(count_all)); - } - else { - return QVariant(); - } - - case Qt::DecorationRole: - if (column == FDS_MODEL_TITLE_INDEX) { - return icon(); - } - else { - return QVariant(); - } - - case Qt::TextAlignmentRole: - if (column == FDS_MODEL_COUNTS_INDEX) { - return Qt::AlignCenter; - } - else { - return QVariant(); - } - - default: - return QVariant(); - } -} - -Qt::ItemFlags RootItem::additionalFlags() const { - return Qt::NoItemFlags; -} - -bool RootItem::performDragDropChange(RootItem *target_item) { - Q_UNUSED(target_item) - - return false; -} - -int RootItem::countOfAllMessages() const { - int total_count = 0; - - foreach (RootItem *child_item, m_childItems) { - total_count += child_item->countOfAllMessages(); - } - - return total_count; -} - -bool RootItem::isChildOf(const RootItem *root) const { - if (root == nullptr) { - return false; - } - - const RootItem *this_item = this; - - while (this_item->kind() != RootItemKind::Root) { - if (root->childItems().contains(const_cast(this_item))) { - return true; - } - else { - this_item = this_item->parent(); - } - } - - return false; -} - -bool RootItem::isParentOf(const RootItem *child) const { - if (child == nullptr) { - return false; - } - else { - return child->isChildOf(this); - } -} - -QList RootItem::getSubTree() const { - QList children; - QList traversable_items; - - traversable_items.append(const_cast(this)); - - // Iterate all nested items. - while (!traversable_items.isEmpty()) { - RootItem *active_item = traversable_items.takeFirst(); - - children.append(active_item); - traversable_items.append(active_item->childItems()); - } - - return children; -} - -QList RootItem::getSubTree(RootItemKind::Kind kind_of_item) const { - QList children; - QList traversable_items; - - traversable_items.append(const_cast(this)); - - // Iterate all nested items. - while (!traversable_items.isEmpty()) { - RootItem *active_item = traversable_items.takeFirst(); - - if ((active_item->kind() & kind_of_item) > 0) { - children.append(active_item); - } - - traversable_items.append(active_item->childItems()); - } - - return children; -} - -QList RootItem::getSubTreeCategories() const { - QList children; - QList traversable_items; - - traversable_items.append(const_cast(this)); - - // Iterate all nested items. - while (!traversable_items.isEmpty()) { - RootItem *active_item = traversable_items.takeFirst(); - - if (active_item->kind() == RootItemKind::Category) { - children.append(active_item->toCategory()); - } - - traversable_items.append(active_item->childItems()); - } - - return children; -} - -QHash RootItem::getHashedSubTreeCategories() const { - QHash children; - QList traversable_items; - - traversable_items.append(const_cast(this)); - - // Iterate all nested items. - while (!traversable_items.isEmpty()) { - RootItem *active_item = traversable_items.takeFirst(); - - if (active_item->kind() == RootItemKind::Category && !children.contains(active_item->customId())) { - children.insert(active_item->customId(), active_item->toCategory()); - } - - traversable_items.append(active_item->childItems()); - } - - return children; -} - -QHash RootItem::getHashedSubTreeFeeds() const { - QHash children; - QList traversable_items; - - traversable_items.append(const_cast(this)); - - // Iterate all nested items. - while (!traversable_items.isEmpty()) { - RootItem *active_item = traversable_items.takeFirst(); - - if (active_item->kind() == RootItemKind::Feed && !children.contains(active_item->customId())) { - children.insert(active_item->customId(), active_item->toFeed()); - } - - traversable_items.append(active_item->childItems()); - } - - return children; -} - -QList RootItem::getSubTreeFeeds() const { - QList children; - QList traversable_items; - - traversable_items.append(const_cast(this)); - - // Iterate all nested items. - while (!traversable_items.isEmpty()) { - RootItem *active_item = traversable_items.takeFirst(); - - if (active_item->kind() == RootItemKind::Feed) { - children.append(active_item->toFeed()); - } - - traversable_items.append(active_item->childItems()); - } - - return children; -} - -ServiceRoot *RootItem::getParentServiceRoot() const { - const RootItem *working_parent = this; - - while (working_parent->kind() != RootItemKind::Root) { - if (working_parent->kind() == RootItemKind::ServiceRoot) { - return working_parent->toServiceRoot(); - } - else { - working_parent = working_parent->parent(); - } - } - - return nullptr; -} - -RootItemKind::Kind RootItem::kind() const { - return m_kind; -} - -void RootItem::setKind(RootItemKind::Kind kind) { - m_kind = kind; -} - -QIcon RootItem::icon() const { - return m_icon; -} - -void RootItem::setIcon(const QIcon &icon) { - m_icon = icon; -} - -int RootItem::id() const { - return m_id; -} - -void RootItem::setId(int id) { - m_id = id; -} - -QString RootItem::title() const { - return m_title; -} - -void RootItem::setTitle(const QString &title) { - m_title = title; -} - -QDateTime RootItem::creationDate() const { - return m_creationDate; -} - -void RootItem::setCreationDate(const QDateTime &creation_date) { - m_creationDate = creation_date; -} - -QString RootItem::description() const { - return m_description; -} - -void RootItem::setDescription(const QString &description) { - m_description = description; -} - -QFont RootItem::normalFont() const { - return m_normalFont; -} - -void RootItem::setNormalFont(const QFont &normal_font) { - m_normalFont = normal_font; -} - -QFont RootItem::boldFont() const { - return m_boldFont; -} - -void RootItem::setBoldFont(const QFont &bold_font) { - m_boldFont = bold_font; -} - -bool RootItem::removeChild(RootItem *child) { - return m_childItems.removeOne(child); -} - -int RootItem::customId() const { - return m_customId; -} - -void RootItem::setCustomId(int custom_id) { - m_customId = custom_id; -} - -Category *RootItem::toCategory() const { - return static_cast(const_cast(this)); -} - -Feed *RootItem::toFeed() const { - return static_cast(const_cast(this)); -} - -ServiceRoot *RootItem::toServiceRoot() const { - return static_cast(const_cast(this)); -} - -int RootItem::countOfUnreadMessages() const { - int total_count = 0; - - foreach (RootItem *child_item, m_childItems) { - total_count += child_item->countOfUnreadMessages(); - } - - return total_count; -} - -bool RootItem::removeChild(int index) { - if (index >= 0 && index < m_childItems.size()) { - m_childItems.removeAt(index); - return true; - } - else { - return false; - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/abstract/rootitem.h" + +#include "services/abstract/serviceroot.h" +#include "services/abstract/feed.h" +#include "services/abstract/category.h" +#include "services/abstract/recyclebin.h" +#include "miscellaneous/application.h" + +#include + + +RootItem::RootItem(RootItem* parent_item) + : QObject(nullptr), + m_kind(RootItemKind::Root), + m_id(NO_PARENT_CATEGORY), + m_customId(NO_PARENT_CATEGORY), + m_title(QString()), + m_description(QString()), + m_icon(QIcon()), + m_creationDate(QDateTime()), + m_childItems(QList()), + m_parentItem(parent_item) { + setupFonts(); +} + +RootItem::~RootItem() { + qDeleteAll(m_childItems); +} + +QString RootItem::hashCode() const { + ServiceRoot* root = getParentServiceRoot(); + int acc_id = root == nullptr ? 0 : root->accountId(); + return + QString::number(acc_id) + QL1S("-") + + QString::number(kind()) + QL1S("-") + + QString::number(id()); +} + +QList RootItem::contextMenu() { + return QList(); +} + +bool RootItem::canBeEdited() const { + return false; +} + +bool RootItem::editViaGui() { + return false; +} + +bool RootItem::canBeDeleted() const { + return false; +} + +bool RootItem::deleteViaGui() { + return false; +} + +bool RootItem::markAsReadUnread(ReadStatus status) { + bool result = true; + + foreach (RootItem* child, m_childItems) { + result &= child->markAsReadUnread(status); + } + + return result; +} + +QList RootItem::undeletedMessages() const { + QList messages; + + foreach (RootItem* child, m_childItems) { + messages.append(child->undeletedMessages()); + } + + return messages; +} + +bool RootItem::cleanMessages(bool clear_only_read) { + bool result = true; + + foreach (RootItem* child, m_childItems) { + if (child->kind() != RootItemKind::Bin) { + result &= child->cleanMessages(clear_only_read); + } + } + + return result; +} + +void RootItem::updateCounts(bool including_total_count) { + foreach (RootItem* child, m_childItems) { + child->updateCounts(including_total_count); + } +} + +void RootItem::setupFonts() { + m_normalFont = Application::font("FeedsView"); + m_boldFont = m_normalFont; + m_boldFont.setBold(true); +} + +int RootItem::row() const { + if (m_parentItem) { + return m_parentItem->m_childItems.indexOf(const_cast(this)); + } + + else { + // This item has no parent. Therefore, its row index is 0. + return 0; + } +} + +QVariant RootItem::data(int column, int role) const { + Q_UNUSED(column) + Q_UNUSED(role) + + switch (role) { + case Qt::ToolTipRole: + if (column == FDS_MODEL_TITLE_INDEX) { + return m_title; + } + + else if (column == FDS_MODEL_COUNTS_INDEX) { + //: Tooltip for "unread" column of feed list. + return tr("%n unread message(s).", 0, countOfUnreadMessages()); + } + + else { + return QVariant(); + } + + case Qt::EditRole: + if (column == FDS_MODEL_TITLE_INDEX) { + return m_title; + } + + else if (column == FDS_MODEL_COUNTS_INDEX) { + return countOfUnreadMessages(); + } + + else { + return QVariant(); + } + + case Qt::FontRole: + return countOfUnreadMessages() > 0 ? m_boldFont : m_normalFont; + + case Qt::DisplayRole: + if (column == FDS_MODEL_TITLE_INDEX) { + return m_title; + } + + else if (column == FDS_MODEL_COUNTS_INDEX) { + int count_all = countOfAllMessages(); + int count_unread = countOfUnreadMessages(); + return qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::CountFormat)).toString() + .replace(PLACEHOLDER_UNREAD_COUNTS, count_unread < 0 ? QSL("-") : QString::number(count_unread)) + .replace(PLACEHOLDER_ALL_COUNTS, count_all < 0 ? QSL("-") : QString::number(count_all)); + } + + else { + return QVariant(); + } + + case Qt::DecorationRole: + if (column == FDS_MODEL_TITLE_INDEX) { + return icon(); + } + + else { + return QVariant(); + } + + case Qt::TextAlignmentRole: + if (column == FDS_MODEL_COUNTS_INDEX) { + return Qt::AlignCenter; + } + + else { + return QVariant(); + } + + default: + return QVariant(); + } +} + +Qt::ItemFlags RootItem::additionalFlags() const { + return Qt::NoItemFlags; +} + +bool RootItem::performDragDropChange(RootItem* target_item) { + Q_UNUSED(target_item) + return false; +} + +int RootItem::countOfAllMessages() const { + int total_count = 0; + + foreach (RootItem* child_item, m_childItems) { + total_count += child_item->countOfAllMessages(); + } + + return total_count; +} + +bool RootItem::isChildOf(const RootItem* root) const { + if (root == nullptr) { + return false; + } + + const RootItem* this_item = this; + + while (this_item->kind() != RootItemKind::Root) { + if (root->childItems().contains(const_cast(this_item))) { + return true; + } + + else { + this_item = this_item->parent(); + } + } + + return false; +} + +bool RootItem::isParentOf(const RootItem* child) const { + if (child == nullptr) { + return false; + } + + else { + return child->isChildOf(this); + } +} + +QList RootItem::getSubTree() const { + QList children; + QList traversable_items; + traversable_items.append(const_cast(this)); + + // Iterate all nested items. + while (!traversable_items.isEmpty()) { + RootItem* active_item = traversable_items.takeFirst(); + children.append(active_item); + traversable_items.append(active_item->childItems()); + } + + return children; +} + +QList RootItem::getSubTree(RootItemKind::Kind kind_of_item) const { + QList children; + QList traversable_items; + traversable_items.append(const_cast(this)); + + // Iterate all nested items. + while (!traversable_items.isEmpty()) { + RootItem* active_item = traversable_items.takeFirst(); + + if ((active_item->kind() & kind_of_item) > 0) { + children.append(active_item); + } + + traversable_items.append(active_item->childItems()); + } + + return children; +} + +QList RootItem::getSubTreeCategories() const { + QList children; + QList traversable_items; + traversable_items.append(const_cast(this)); + + // Iterate all nested items. + while (!traversable_items.isEmpty()) { + RootItem* active_item = traversable_items.takeFirst(); + + if (active_item->kind() == RootItemKind::Category) { + children.append(active_item->toCategory()); + } + + traversable_items.append(active_item->childItems()); + } + + return children; +} + +QHash RootItem::getHashedSubTreeCategories() const { + QHash children; + QList traversable_items; + traversable_items.append(const_cast(this)); + + // Iterate all nested items. + while (!traversable_items.isEmpty()) { + RootItem* active_item = traversable_items.takeFirst(); + + if (active_item->kind() == RootItemKind::Category && !children.contains(active_item->customId())) { + children.insert(active_item->customId(), active_item->toCategory()); + } + + traversable_items.append(active_item->childItems()); + } + + return children; +} + +QHash RootItem::getHashedSubTreeFeeds() const { + QHash children; + QList traversable_items; + traversable_items.append(const_cast(this)); + + // Iterate all nested items. + while (!traversable_items.isEmpty()) { + RootItem* active_item = traversable_items.takeFirst(); + + if (active_item->kind() == RootItemKind::Feed && !children.contains(active_item->customId())) { + children.insert(active_item->customId(), active_item->toFeed()); + } + + traversable_items.append(active_item->childItems()); + } + + return children; +} + +QList RootItem::getSubTreeFeeds() const { + QList children; + QList traversable_items; + traversable_items.append(const_cast(this)); + + // Iterate all nested items. + while (!traversable_items.isEmpty()) { + RootItem* active_item = traversable_items.takeFirst(); + + if (active_item->kind() == RootItemKind::Feed) { + children.append(active_item->toFeed()); + } + + traversable_items.append(active_item->childItems()); + } + + return children; +} + +ServiceRoot* RootItem::getParentServiceRoot() const { + const RootItem* working_parent = this; + + while (working_parent->kind() != RootItemKind::Root) { + if (working_parent->kind() == RootItemKind::ServiceRoot) { + return working_parent->toServiceRoot(); + } + + else { + working_parent = working_parent->parent(); + } + } + + return nullptr; +} + +RootItemKind::Kind RootItem::kind() const { + return m_kind; +} + +void RootItem::setKind(RootItemKind::Kind kind) { + m_kind = kind; +} + +QIcon RootItem::icon() const { + return m_icon; +} + +void RootItem::setIcon(const QIcon& icon) { + m_icon = icon; +} + +int RootItem::id() const { + return m_id; +} + +void RootItem::setId(int id) { + m_id = id; +} + +QString RootItem::title() const { + return m_title; +} + +void RootItem::setTitle(const QString& title) { + m_title = title; +} + +QDateTime RootItem::creationDate() const { + return m_creationDate; +} + +void RootItem::setCreationDate(const QDateTime& creation_date) { + m_creationDate = creation_date; +} + +QString RootItem::description() const { + return m_description; +} + +void RootItem::setDescription(const QString& description) { + m_description = description; +} + +QFont RootItem::normalFont() const { + return m_normalFont; +} + +void RootItem::setNormalFont(const QFont& normal_font) { + m_normalFont = normal_font; +} + +QFont RootItem::boldFont() const { + return m_boldFont; +} + +void RootItem::setBoldFont(const QFont& bold_font) { + m_boldFont = bold_font; +} + +bool RootItem::removeChild(RootItem* child) { + return m_childItems.removeOne(child); +} + +int RootItem::customId() const { + return m_customId; +} + +void RootItem::setCustomId(int custom_id) { + m_customId = custom_id; +} + +Category* RootItem::toCategory() const { + return static_cast(const_cast(this)); +} + +Feed* RootItem::toFeed() const { + return static_cast(const_cast(this)); +} + +ServiceRoot* RootItem::toServiceRoot() const { + return static_cast(const_cast(this)); +} + +int RootItem::countOfUnreadMessages() const { + int total_count = 0; + + foreach (RootItem* child_item, m_childItems) { + total_count += child_item->countOfUnreadMessages(); + } + + return total_count; +} + +bool RootItem::removeChild(int index) { + if (index >= 0 && index < m_childItems.size()) { + m_childItems.removeAt(index); + return true; + } + + else { + return false; + } +} diff --git a/src/services/abstract/rootitem.h b/src/services/abstract/rootitem.h index 1af188cc7..0a6532df8 100755 --- a/src/services/abstract/rootitem.h +++ b/src/services/abstract/rootitem.h @@ -1,239 +1,239 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef ROOTITEM_H -#define ROOTITEM_H - -#include "core/message.h" - -#include -#include -#include - - -class Category; -class Feed; -class ServiceRoot; -class QAction; - -namespace RootItemKind { - // Describes the kind of the item. - enum Kind { - Root = 1, - Bin = 2, - Feed = 4, - Category = 8, - ServiceRoot = 16, - LabelsRoot = 32 - }; - - inline Kind operator|(Kind a, Kind b) { - return static_cast(static_cast(a) | static_cast(b)); - } -} - -// Represents ROOT item of FeedsModel. -// NOTE: This class is derived to add functionality for -// all other non-root items of FeedsModel. -class RootItem : public QObject { - Q_OBJECT - - public: - // Holds statuses for feeds/messages - // to be marked read/unread. - enum ReadStatus { - Unread = 0, - Read = 1 - }; - - // Holds statuses for messages - // to be switched importance (starred). - enum Importance { - NotImportant = 0, - Important = 1 - }; - - // Constructors and destructors. - explicit RootItem(RootItem *parent_item = nullptr); - virtual ~RootItem(); - - virtual QString hashCode() const; - - // Returns list of specific actions which can be done with the item. - // Do not include general actions here like actions: Mark as read, Update, ... - // NOTE: Ownership of returned actions is not switched to caller, free them when needed. - virtual QList contextMenu(); - - // Can properties of this item be edited? - virtual bool canBeEdited() const; - - // Performs editing of properties of this item (probably via dialog) - // and returns result status. - virtual bool editViaGui(); - - // Can the item be deleted? - virtual bool canBeDeleted() const; - - // Performs deletion of the item, this - // method should NOT display any additional dialogs. - // Returns result status. - virtual bool deleteViaGui(); - - // Performs all needed steps (DB update, remote server update) - // to mark this item as read/unread. - virtual bool markAsReadUnread(ReadStatus status); - - // Get ALL undeleted messages from this item in one single list. - // This is currently used for displaying items in "newspaper mode". - virtual QList undeletedMessages() const; - - // This method should "clean" all messages it contains. - // What "clean" means? It means delete messages -> move them to recycle bin - // or eventually remove them completely if there is no recycle bin functionality. - // If this method is called on "recycle bin" instance of your - // service account, it should "empty" the recycle bin. - virtual bool cleanMessages(bool clear_only_read); - - // Updates counts of all/unread messages for this feed. - virtual void updateCounts(bool including_total_count); - - virtual int row() const; - virtual QVariant data(int column, int role) const; - virtual Qt::ItemFlags additionalFlags() const; - virtual bool performDragDropChange(RootItem *target_item); - - // Each item offers "counts" of messages. - // Returns counts of messages of all child items summed up. - virtual int countOfUnreadMessages() const; - virtual int countOfAllMessages() const; - - inline RootItem *parent() const { - return m_parentItem; - } - - inline void setParent(RootItem *parent_item) { - m_parentItem = parent_item; - } - - inline RootItem *child(int row) { - return m_childItems.value(row); - } - - inline int childCount() const { - return m_childItems.size(); - } - - inline void appendChild(RootItem *child) { - m_childItems.append(child); - child->setParent(this); - } - - // Access to children. - inline QList childItems() const { - return m_childItems; - } - - // Removes all children from this item. - // NOTE: Children are NOT freed from the memory. - inline void clearChildren() { - m_childItems.clear(); - } - - inline void setChildItems(const QList &child_items) { - m_childItems = child_items; - } - - // Removes particular child at given index. - // NOTE: Child is NOT freed from the memory. - bool removeChild(int index); - bool removeChild(RootItem *child); - - // Checks whether "this" object is child (direct or indirect) - // of the given root. - bool isChildOf(const RootItem *root) const; - - // Is "this" item parent (direct or indirect) if given child? - bool isParentOf(const RootItem *child) const; - - // Returns flat list of all items from subtree where this item is a root. - // Returned list includes this item too. - QList getSubTree() const; - QList getSubTree(RootItemKind::Kind kind_of_item) const; - QList getSubTreeCategories() const; - QHash getHashedSubTreeCategories() const; - QHash getHashedSubTreeFeeds() const; - QList getSubTreeFeeds() const; - - // Returns the service root node which is direct or indirect parent of current item. - ServiceRoot *getParentServiceRoot() const; - - RootItemKind::Kind kind() const; - void setKind(RootItemKind::Kind kind); - - // Each item can have icon. - QIcon icon() const; - void setIcon(const QIcon &icon); - - // This ALWAYS represents primary column number/ID under which - // the item is stored in DB. - int id() const; - void setId(int id); - - // Each item has its title. - QString title() const; - void setTitle(const QString &title); - - QDateTime creationDate() const; - void setCreationDate(const QDateTime &creation_date); - - QString description() const; - void setDescription(const QString &description); - - QFont normalFont() const; - void setNormalFont(const QFont &normal_font); - - QFont boldFont() const; - void setBoldFont(const QFont &bold_font); - - // NOTE: For standard feed/category, this WILL equal to id(). - int customId() const; - void setCustomId(int custom_id); - - // Converters - Category *toCategory() const; - Feed *toFeed() const; - ServiceRoot *toServiceRoot() const; - - private: - void setupFonts(); - - RootItemKind::Kind m_kind; - int m_id; - int m_customId; - QString m_title; - QString m_description; - QIcon m_icon; - QDateTime m_creationDate; - - QFont m_normalFont; - QFont m_boldFont; - - QList m_childItems; - RootItem *m_parentItem; -}; - -#endif // ROOTITEM_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef ROOTITEM_H +#define ROOTITEM_H + +#include "core/message.h" + +#include +#include +#include + + +class Category; +class Feed; +class ServiceRoot; +class QAction; + +namespace RootItemKind { + // Describes the kind of the item. + enum Kind { + Root = 1, + Bin = 2, + Feed = 4, + Category = 8, + ServiceRoot = 16, + LabelsRoot = 32 + }; + + inline Kind operator|(Kind a, Kind b) { + return static_cast(static_cast(a) | static_cast(b)); + } +} + +// Represents ROOT item of FeedsModel. +// NOTE: This class is derived to add functionality for +// all other non-root items of FeedsModel. +class RootItem : public QObject { + Q_OBJECT + + public: + // Holds statuses for feeds/messages + // to be marked read/unread. + enum ReadStatus { + Unread = 0, + Read = 1 + }; + + // Holds statuses for messages + // to be switched importance (starred). + enum Importance { + NotImportant = 0, + Important = 1 + }; + + // Constructors and destructors. + explicit RootItem(RootItem* parent_item = nullptr); + virtual ~RootItem(); + + virtual QString hashCode() const; + + // Returns list of specific actions which can be done with the item. + // Do not include general actions here like actions: Mark as read, Update, ... + // NOTE: Ownership of returned actions is not switched to caller, free them when needed. + virtual QList contextMenu(); + + // Can properties of this item be edited? + virtual bool canBeEdited() const; + + // Performs editing of properties of this item (probably via dialog) + // and returns result status. + virtual bool editViaGui(); + + // Can the item be deleted? + virtual bool canBeDeleted() const; + + // Performs deletion of the item, this + // method should NOT display any additional dialogs. + // Returns result status. + virtual bool deleteViaGui(); + + // Performs all needed steps (DB update, remote server update) + // to mark this item as read/unread. + virtual bool markAsReadUnread(ReadStatus status); + + // Get ALL undeleted messages from this item in one single list. + // This is currently used for displaying items in "newspaper mode". + virtual QList undeletedMessages() const; + + // This method should "clean" all messages it contains. + // What "clean" means? It means delete messages -> move them to recycle bin + // or eventually remove them completely if there is no recycle bin functionality. + // If this method is called on "recycle bin" instance of your + // service account, it should "empty" the recycle bin. + virtual bool cleanMessages(bool clear_only_read); + + // Updates counts of all/unread messages for this feed. + virtual void updateCounts(bool including_total_count); + + virtual int row() const; + virtual QVariant data(int column, int role) const; + virtual Qt::ItemFlags additionalFlags() const; + virtual bool performDragDropChange(RootItem* target_item); + + // Each item offers "counts" of messages. + // Returns counts of messages of all child items summed up. + virtual int countOfUnreadMessages() const; + virtual int countOfAllMessages() const; + + inline RootItem* parent() const { + return m_parentItem; + } + + inline void setParent(RootItem* parent_item) { + m_parentItem = parent_item; + } + + inline RootItem* child(int row) { + return m_childItems.value(row); + } + + inline int childCount() const { + return m_childItems.size(); + } + + inline void appendChild(RootItem* child) { + m_childItems.append(child); + child->setParent(this); + } + + // Access to children. + inline QList childItems() const { + return m_childItems; + } + + // Removes all children from this item. + // NOTE: Children are NOT freed from the memory. + inline void clearChildren() { + m_childItems.clear(); + } + + inline void setChildItems(const QList& child_items) { + m_childItems = child_items; + } + + // Removes particular child at given index. + // NOTE: Child is NOT freed from the memory. + bool removeChild(int index); + bool removeChild(RootItem* child); + + // Checks whether "this" object is child (direct or indirect) + // of the given root. + bool isChildOf(const RootItem* root) const; + + // Is "this" item parent (direct or indirect) if given child? + bool isParentOf(const RootItem* child) const; + + // Returns flat list of all items from subtree where this item is a root. + // Returned list includes this item too. + QList getSubTree() const; + QList getSubTree(RootItemKind::Kind kind_of_item) const; + QList getSubTreeCategories() const; + QHash getHashedSubTreeCategories() const; + QHash getHashedSubTreeFeeds() const; + QList getSubTreeFeeds() const; + + // Returns the service root node which is direct or indirect parent of current item. + ServiceRoot* getParentServiceRoot() const; + + RootItemKind::Kind kind() const; + void setKind(RootItemKind::Kind kind); + + // Each item can have icon. + QIcon icon() const; + void setIcon(const QIcon& icon); + + // This ALWAYS represents primary column number/ID under which + // the item is stored in DB. + int id() const; + void setId(int id); + + // Each item has its title. + QString title() const; + void setTitle(const QString& title); + + QDateTime creationDate() const; + void setCreationDate(const QDateTime& creation_date); + + QString description() const; + void setDescription(const QString& description); + + QFont normalFont() const; + void setNormalFont(const QFont& normal_font); + + QFont boldFont() const; + void setBoldFont(const QFont& bold_font); + + // NOTE: For standard feed/category, this WILL equal to id(). + int customId() const; + void setCustomId(int custom_id); + + // Converters + Category* toCategory() const; + Feed* toFeed() const; + ServiceRoot* toServiceRoot() const; + + private: + void setupFonts(); + + RootItemKind::Kind m_kind; + int m_id; + int m_customId; + QString m_title; + QString m_description; + QIcon m_icon; + QDateTime m_creationDate; + + QFont m_normalFont; + QFont m_boldFont; + + QList m_childItems; + RootItem* m_parentItem; +}; + +#endif // ROOTITEM_H diff --git a/src/services/abstract/serviceentrypoint.cpp b/src/services/abstract/serviceentrypoint.cpp index b6c781435..2d220433f 100755 --- a/src/services/abstract/serviceentrypoint.cpp +++ b/src/services/abstract/serviceentrypoint.cpp @@ -1,22 +1,22 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/abstract/serviceentrypoint.h" - - -ServiceEntryPoint::~ServiceEntryPoint() { -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/abstract/serviceentrypoint.h" + + +ServiceEntryPoint::~ServiceEntryPoint() { +} diff --git a/src/services/abstract/serviceentrypoint.h b/src/services/abstract/serviceentrypoint.h index 8848895f6..4cd2ef818 100755 --- a/src/services/abstract/serviceentrypoint.h +++ b/src/services/abstract/serviceentrypoint.h @@ -1,81 +1,81 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SERVICE_H -#define SERVICE_H - -#include -#include -#include - - -class ServiceRoot; -class FeedsModel; - -// TOP LEVEL class which provides basic information about the "service" -class ServiceEntryPoint { - public: - // Constructors. - virtual ~ServiceEntryPoint(); - - ///////////////////////////////////////// - // /* Members to override. - ///////////////////////////////////////// - - // Creates new service root item, which is ready to be added - // into the model. This method can for example display - // some kind of first-time configuration dialog inside itself - // before returning the root item. - // Returns NULL if initialization of new root cannot be done. - virtual ServiceRoot *createNewRoot() const = 0; - - // Performs initialization of all service accounts created using this entry - // point from persistent DB. - // Returns list of root nodes which will be afterwards added - // to the global feed model. - virtual QList initializeSubtree() const = 0; - - // Can this service account be added just once? - // NOTE: This is true particularly for "standard" service - // which operates with normal RSS/ATOM feeds. - virtual bool isSingleInstanceService() const = 0; - - // Human readable service name, for example "TT-RSS". - virtual QString name() const = 0; - - // Some arbitrary string. - // NOTE: Keep in sync with ServiceRoot::code(). - virtual QString code() const = 0; - - // Human readable service description, for example "Services which offers TT-RSS integration.". - virtual QString description() const = 0; - - // Version of the service, using of semantic versioning is recommended. - virtual QString version() const = 0; - - // Author of the service. - virtual QString author() const = 0; - - // Icon of the service. - virtual QIcon icon() const = 0; - - ///////////////////////////////////////// - // Members to override. */ - ///////////////////////////////////////// -}; - -#endif // SERVICE_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SERVICE_H +#define SERVICE_H + +#include +#include +#include + + +class ServiceRoot; +class FeedsModel; + +// TOP LEVEL class which provides basic information about the "service" +class ServiceEntryPoint { + public: + // Constructors. + virtual ~ServiceEntryPoint(); + + ///////////////////////////////////////// + // /* Members to override. + ///////////////////////////////////////// + + // Creates new service root item, which is ready to be added + // into the model. This method can for example display + // some kind of first-time configuration dialog inside itself + // before returning the root item. + // Returns NULL if initialization of new root cannot be done. + virtual ServiceRoot* createNewRoot() const = 0; + + // Performs initialization of all service accounts created using this entry + // point from persistent DB. + // Returns list of root nodes which will be afterwards added + // to the global feed model. + virtual QList initializeSubtree() const = 0; + + // Can this service account be added just once? + // NOTE: This is true particularly for "standard" service + // which operates with normal RSS/ATOM feeds. + virtual bool isSingleInstanceService() const = 0; + + // Human readable service name, for example "TT-RSS". + virtual QString name() const = 0; + + // Some arbitrary string. + // NOTE: Keep in sync with ServiceRoot::code(). + virtual QString code() const = 0; + + // Human readable service description, for example "Services which offers TT-RSS integration.". + virtual QString description() const = 0; + + // Version of the service, using of semantic versioning is recommended. + virtual QString version() const = 0; + + // Author of the service. + virtual QString author() const = 0; + + // Icon of the service. + virtual QIcon icon() const = 0; + + ///////////////////////////////////////// + // Members to override. */ + ///////////////////////////////////////// +}; + +#endif // SERVICE_H diff --git a/src/services/abstract/serviceroot.cpp b/src/services/abstract/serviceroot.cpp index 9d6d7f36f..39e31b649 100755 --- a/src/services/abstract/serviceroot.cpp +++ b/src/services/abstract/serviceroot.cpp @@ -1,523 +1,509 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/abstract/serviceroot.h" - -#include "core/feedsmodel.h" -#include "core/messagesmodel.h" -#include "miscellaneous/application.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/textfactory.h" -#include "miscellaneous/databasequeries.h" -#include "services/abstract/category.h" -#include "services/abstract/feed.h" -#include "services/abstract/recyclebin.h" - - -ServiceRoot::ServiceRoot(RootItem *parent) : RootItem(parent), m_accountId(NO_PARENT_CATEGORY) { - setKind(RootItemKind::ServiceRoot); - setCreationDate(QDateTime::currentDateTime()); -} - -ServiceRoot::~ServiceRoot() { -} - -bool ServiceRoot::deleteViaGui() { - QSqlDatabase database= qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - if (DatabaseQueries::deleteAccount(database, accountId())) { - requestItemRemoval(this); - return true; - } - else { - return false; - } -} - -bool ServiceRoot::markAsReadUnread(RootItem::ReadStatus status) { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - if (DatabaseQueries::markAccountReadUnread(database, accountId(), status)) { - updateCounts(false); - itemChanged(getSubTree()); - requestReloadMessageList(status == RootItem::Read); - return true; - } - else { - return false; - } -} - -QList ServiceRoot::addItemMenu() { - return QList(); -} - -QList ServiceRoot::contextMenu() { - return serviceMenu(); -} - -QList ServiceRoot::serviceMenu() { - return QList(); -} - -void ServiceRoot::updateCounts(bool including_total_count) { - QList feeds; - - foreach (RootItem *child, getSubTree()) { - if (child->kind() == RootItemKind::Feed) { - feeds.append(child->toFeed()); - } - else if (child->kind() != RootItemKind::Category && child->kind() != RootItemKind::ServiceRoot) { - child->updateCounts(including_total_count); - } - } - - if (feeds.isEmpty()) { - return; - } - - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - bool ok; - QMap > counts = DatabaseQueries::getMessageCountsForAccount(database, accountId(), including_total_count, &ok); - - if (ok) { - foreach (Feed *feed, feeds) { - if (counts.contains(feed->customId())) { - feed->setCountOfUnreadMessages(counts.value(feed->customId()).first); - - if (including_total_count) { - feed->setCountOfAllMessages(counts.value(feed->customId()).second); - } - } - else { - feed->setCountOfUnreadMessages(0); - - if (including_total_count) { - feed->setCountOfAllMessages(0); - } - } - } - } -} - -void ServiceRoot::completelyRemoveAllData() { - // Purge old data from SQL and clean all model items. - removeOldFeedTree(true); - cleanAllItems(); - updateCounts(true); - itemChanged(QList() << this); - requestReloadMessageList(true); -} - -void ServiceRoot::removeOldFeedTree(bool including_messages) { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - DatabaseQueries::deleteAccountData(database, accountId(), including_messages); -} - -void ServiceRoot::cleanAllItems() { - foreach (RootItem *top_level_item, childItems()) { - if (top_level_item->kind() != RootItemKind::Bin) { - requestItemRemoval(top_level_item); - } - } -} - -bool ServiceRoot::cleanFeeds(QList items, bool clean_read_only) { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - if (DatabaseQueries::cleanFeeds(database, textualFeedIds(items), clean_read_only, accountId())) { - // Messages are cleared, now inform model about need to reload data. - QList itemss; - - foreach (Feed *feed, items) { - feed->updateCounts(true); - itemss.append(feed); - } - - RecycleBin *bin = recycleBin(); - - if (bin != nullptr) { - bin->updateCounts(true); - itemss.append(bin); - } - - itemChanged(itemss); - requestReloadMessageList(true); - return true; - } - else { - return false; - } -} - -void ServiceRoot::storeNewFeedTree(RootItem *root) { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - if (DatabaseQueries::storeAccountTree(database, root, accountId())) { - RecycleBin *bin = recycleBin(); - - if (bin != nullptr && !childItems().contains(bin)) { - // As the last item, add recycle bin, which is needed. - appendChild(bin); - bin->updateCounts(true); - } - } -} - -void ServiceRoot::removeLeftOverMessages() { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - DatabaseQueries::purgeLeftoverMessages(database, accountId()); -} - -QList ServiceRoot::undeletedMessages() const { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - return DatabaseQueries::getUndeletedMessagesForAccount(database, accountId()); -} - -void ServiceRoot::saveAllCachedData() { -} - -void ServiceRoot::itemChanged(const QList &items) { - emit dataChanged(items); -} - -void ServiceRoot::requestReloadMessageList(bool mark_selected_messages_read) { - emit reloadMessageListRequested(mark_selected_messages_read); -} - -void ServiceRoot::requestItemExpand(const QList &items, bool expand) { - emit itemExpandRequested(items, expand); -} - -void ServiceRoot::requestItemExpandStateSave(RootItem *subtree_root) { - emit itemExpandStateSaveRequested(subtree_root); -} - -void ServiceRoot::requestItemReassignment(RootItem *item, RootItem *new_parent) { - emit itemReassignmentRequested(item, new_parent); -} - -void ServiceRoot::requestItemRemoval(RootItem *item) { - emit itemRemovalRequested(item); -} - -void ServiceRoot::syncIn() { - QIcon original_icon = icon(); - - setIcon(qApp->icons()->fromTheme(QSL("view-refresh"))); - itemChanged(QList() << this); - - RootItem *new_tree = obtainNewTreeForSyncIn(); - - if (new_tree != nullptr) { - // Purge old data from SQL and clean all model items. - requestItemExpandStateSave(this); - - QMap feed_custom_data = storeCustomFeedsData(); - - removeOldFeedTree(false); - cleanAllItems(); - restoreCustomFeedsData(feed_custom_data, new_tree->getHashedSubTreeFeeds()); - - // Model is clean, now store new tree into DB and - // set primary IDs of the items. - storeNewFeedTree(new_tree); - - // We have new feed, some feeds were maybe removed, - // so remove left over messages. - removeLeftOverMessages(); - - foreach (RootItem *top_level_item, new_tree->childItems()) { - top_level_item->setParent(nullptr); - requestItemReassignment(top_level_item, this); - } - - updateCounts(true); - - new_tree->clearChildren(); - new_tree->deleteLater(); - - QList all_items = getSubTree(); - - itemChanged(all_items); - requestReloadMessageList(true); - - // Now we must refresh expand states. - QList items_to_expand; - - foreach (RootItem *item, all_items) { - if (qApp->settings()->value(GROUP(CategoriesExpandStates), item->hashCode(), item->childCount() > 0).toBool()) { - items_to_expand.append(item); - } - } - - if (!items_to_expand.contains(this)) { - items_to_expand.prepend(this); - } - - requestItemExpand(items_to_expand, true); - } - - setIcon(original_icon); - itemChanged(QList() << this); -} - -RootItem *ServiceRoot::obtainNewTreeForSyncIn() const { - return nullptr; -} - -QStringList ServiceRoot::customIDSOfMessagesForItem(RootItem *item) { - if (item->getParentServiceRoot() != this) { - // Not item from this account. - return QStringList(); - } - else { - QStringList list; - - switch (item->kind()) { - case RootItemKind::Category: { - foreach (RootItem *child, item->childItems()) { - list.append(customIDSOfMessagesForItem(child)); - } - - return list; - } - - case RootItemKind::ServiceRoot: { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - list = DatabaseQueries::customIdsOfMessagesFromAccount(database, accountId()); - break; - } - - case RootItemKind::Bin: { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - list = DatabaseQueries::customIdsOfMessagesFromBin(database, accountId()); - break; - } - - case RootItemKind::Feed: { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - list = DatabaseQueries::customIdsOfMessagesFromFeed(database, item->customId(), accountId()); - break; - } - - default: - break; - } - - qDebug() << "Custom IDs of messages for some operation are:" << list; - - return list; - } -} - -bool ServiceRoot::markFeedsReadUnread(QList items, RootItem::ReadStatus read) { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - if (DatabaseQueries::markFeedsReadUnread(database, textualFeedIds(items), accountId(), read)) { - QList itemss; - - foreach (Feed *feed, items) { - feed->updateCounts(false); - itemss.append(feed); - } - - itemChanged(itemss); - requestReloadMessageList(read == RootItem::Read); - return true; - } - else { - return false; - } -} - -QStringList ServiceRoot::textualFeedIds(const QList &feeds) const { - QStringList stringy_ids; - stringy_ids.reserve(feeds.size()); - - foreach (const Feed *feed, feeds) { - stringy_ids.append(QString("'%1'").arg(QString::number(feed->customId()))); - } - - return stringy_ids; -} - -QStringList ServiceRoot::customIDsOfMessages(const QList &changes) { - QStringList list; - - for (int i = 0; i < changes.size(); i++) { - list.append(changes.at(i).first.m_customId); - } - - return list; -} - -QStringList ServiceRoot::customIDsOfMessages(const QList &messages) { - QStringList list; - - foreach (const Message &message, messages) { - list.append(message.m_customId); - } - - return list; -} - -int ServiceRoot::accountId() const { - return m_accountId; -} - -void ServiceRoot::setAccountId(int account_id) { - m_accountId = account_id; -} - -bool ServiceRoot::loadMessagesForItem(RootItem *item, MessagesModel *model) { - if (item->kind() == RootItemKind::Bin) { - model->setFilter(QString("Messages.is_deleted = 1 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1").arg(QString::number(accountId()))); - } - else { - QList children = item->getSubTreeFeeds(); - QString filter_clause = textualFeedIds(children).join(QSL(", ")); - - model->setFilter(QString("Feeds.custom_id IN (%1) AND Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %2").arg(filter_clause, - QString::number(accountId()))); - qDebug("Loading messages from feeds: %s.", qPrintable(filter_clause)); - } - - return true; -} - -bool ServiceRoot::onBeforeSetMessagesRead(RootItem *selected_item, const QList &messages, RootItem::ReadStatus read) { - Q_UNUSED(messages) - Q_UNUSED(read) - Q_UNUSED(selected_item) - - return true; -} - -bool ServiceRoot::onAfterSetMessagesRead(RootItem *selected_item, const QList &messages, RootItem::ReadStatus read) { - Q_UNUSED(messages) - Q_UNUSED(read) - - selected_item->updateCounts(false); - itemChanged(QList() << selected_item); - return true; -} - -bool ServiceRoot::onBeforeSwitchMessageImportance(RootItem *selected_item, const QList &changes) { - Q_UNUSED(selected_item) - Q_UNUSED(changes) - - return true; -} - -bool ServiceRoot::onAfterSwitchMessageImportance(RootItem *selected_item, const QList &changes) { - Q_UNUSED(selected_item) - Q_UNUSED(changes) - - return true; -} - -bool ServiceRoot::onBeforeMessagesDelete(RootItem *selected_item, const QList &messages) { - Q_UNUSED(selected_item) - Q_UNUSED(messages) - - return true; -} - -bool ServiceRoot::onAfterMessagesDelete(RootItem *selected_item, const QList &messages) { - Q_UNUSED(messages) - - // User deleted some messages he selected in message list. - selected_item->updateCounts(true); - - RecycleBin *bin = recycleBin(); - - if (selected_item->kind() == RootItemKind::Bin) { - itemChanged(QList() << bin); - } - else { - if (bin != nullptr) { - bin->updateCounts(true); - itemChanged(QList() << selected_item << bin); - } - else { - itemChanged(QList() << selected_item); - } - } - - return true; -} - -bool ServiceRoot::onBeforeMessagesRestoredFromBin(RootItem *selected_item, const QList &messages) { - Q_UNUSED(selected_item) - Q_UNUSED(messages) - - return true; -} - -bool ServiceRoot::onAfterMessagesRestoredFromBin(RootItem *selected_item, const QList &messages) { - Q_UNUSED(selected_item) - Q_UNUSED(messages) - - updateCounts(true); - itemChanged(getSubTree()); - return true; -} - -void ServiceRoot::assembleFeeds(Assignment feeds) { - QHash categories = getHashedSubTreeCategories(); - - foreach (const AssignmentItem &feed, feeds) { - if (feed.first == NO_PARENT_CATEGORY) { - // This is top-level feed, add it to the root item. - appendChild(feed.second); - } - else if (categories.contains(feed.first)) { - // This feed belongs to this category. - categories.value(feed.first)->appendChild(feed.second); - } - else { - qWarning("Feed '%s' is loose, skipping it.", qPrintable(feed.second->title())); - } - } -} - -void ServiceRoot::assembleCategories(Assignment categories) { - QHash assignments; - assignments.insert(NO_PARENT_CATEGORY, this); - - // Add top-level categories. - while (!categories.isEmpty()) { - for (int i = 0; i < categories.size(); i++) { - if (assignments.contains(categories.at(i).first)) { - // Parent category of this category is already added. - assignments.value(categories.at(i).first)->appendChild(categories.at(i).second); - - // Now, added category can be parent for another categories, add it. - assignments.insert(categories.at(i).second->id(), categories.at(i).second); - - // Remove the category from the list, because it was - // added to the final collection. - categories.removeAt(i); - i--; - } - } - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/abstract/serviceroot.h" + +#include "core/feedsmodel.h" +#include "core/messagesmodel.h" +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/textfactory.h" +#include "miscellaneous/databasequeries.h" +#include "services/abstract/category.h" +#include "services/abstract/feed.h" +#include "services/abstract/recyclebin.h" + + +ServiceRoot::ServiceRoot(RootItem* parent) : RootItem(parent), m_accountId(NO_PARENT_CATEGORY) { + setKind(RootItemKind::ServiceRoot); + setCreationDate(QDateTime::currentDateTime()); +} + +ServiceRoot::~ServiceRoot() { +} + +bool ServiceRoot::deleteViaGui() { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + + if (DatabaseQueries::deleteAccount(database, accountId())) { + requestItemRemoval(this); + return true; + } + + else { + return false; + } +} + +bool ServiceRoot::markAsReadUnread(RootItem::ReadStatus status) { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + + if (DatabaseQueries::markAccountReadUnread(database, accountId(), status)) { + updateCounts(false); + itemChanged(getSubTree()); + requestReloadMessageList(status == RootItem::Read); + return true; + } + + else { + return false; + } +} + +QList ServiceRoot::addItemMenu() { + return QList(); +} + +QList ServiceRoot::contextMenu() { + return serviceMenu(); +} + +QList ServiceRoot::serviceMenu() { + return QList(); +} + +void ServiceRoot::updateCounts(bool including_total_count) { + QList feeds; + + foreach (RootItem* child, getSubTree()) { + if (child->kind() == RootItemKind::Feed) { + feeds.append(child->toFeed()); + } + + else if (child->kind() != RootItemKind::Category && child->kind() != RootItemKind::ServiceRoot) { + child->updateCounts(including_total_count); + } + } + + if (feeds.isEmpty()) { + return; + } + + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + bool ok; + QMap> counts = DatabaseQueries::getMessageCountsForAccount(database, accountId(), including_total_count, &ok); + + if (ok) { + foreach (Feed* feed, feeds) { + if (counts.contains(feed->customId())) { + feed->setCountOfUnreadMessages(counts.value(feed->customId()).first); + + if (including_total_count) { + feed->setCountOfAllMessages(counts.value(feed->customId()).second); + } + } + + else { + feed->setCountOfUnreadMessages(0); + + if (including_total_count) { + feed->setCountOfAllMessages(0); + } + } + } + } +} + +void ServiceRoot::completelyRemoveAllData() { + // Purge old data from SQL and clean all model items. + removeOldFeedTree(true); + cleanAllItems(); + updateCounts(true); + itemChanged(QList() << this); + requestReloadMessageList(true); +} + +void ServiceRoot::removeOldFeedTree(bool including_messages) { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + DatabaseQueries::deleteAccountData(database, accountId(), including_messages); +} + +void ServiceRoot::cleanAllItems() { + foreach (RootItem* top_level_item, childItems()) { + if (top_level_item->kind() != RootItemKind::Bin) { + requestItemRemoval(top_level_item); + } + } +} + +bool ServiceRoot::cleanFeeds(QList items, bool clean_read_only) { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + + if (DatabaseQueries::cleanFeeds(database, textualFeedIds(items), clean_read_only, accountId())) { + // Messages are cleared, now inform model about need to reload data. + QList itemss; + + foreach (Feed* feed, items) { + feed->updateCounts(true); + itemss.append(feed); + } + + RecycleBin* bin = recycleBin(); + + if (bin != nullptr) { + bin->updateCounts(true); + itemss.append(bin); + } + + itemChanged(itemss); + requestReloadMessageList(true); + return true; + } + + else { + return false; + } +} + +void ServiceRoot::storeNewFeedTree(RootItem* root) { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + + if (DatabaseQueries::storeAccountTree(database, root, accountId())) { + RecycleBin* bin = recycleBin(); + + if (bin != nullptr && !childItems().contains(bin)) { + // As the last item, add recycle bin, which is needed. + appendChild(bin); + bin->updateCounts(true); + } + } +} + +void ServiceRoot::removeLeftOverMessages() { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + DatabaseQueries::purgeLeftoverMessages(database, accountId()); +} + +QList ServiceRoot::undeletedMessages() const { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + return DatabaseQueries::getUndeletedMessagesForAccount(database, accountId()); +} + +void ServiceRoot::saveAllCachedData() { +} + +void ServiceRoot::itemChanged(const QList& items) { + emit dataChanged(items); +} + +void ServiceRoot::requestReloadMessageList(bool mark_selected_messages_read) { + emit reloadMessageListRequested(mark_selected_messages_read); +} + +void ServiceRoot::requestItemExpand(const QList& items, bool expand) { + emit itemExpandRequested(items, expand); +} + +void ServiceRoot::requestItemExpandStateSave(RootItem* subtree_root) { + emit itemExpandStateSaveRequested(subtree_root); +} + +void ServiceRoot::requestItemReassignment(RootItem* item, RootItem* new_parent) { + emit itemReassignmentRequested(item, new_parent); +} + +void ServiceRoot::requestItemRemoval(RootItem* item) { + emit itemRemovalRequested(item); +} + +void ServiceRoot::syncIn() { + QIcon original_icon = icon(); + setIcon(qApp->icons()->fromTheme(QSL("view-refresh"))); + itemChanged(QList() << this); + RootItem* new_tree = obtainNewTreeForSyncIn(); + + if (new_tree != nullptr) { + // Purge old data from SQL and clean all model items. + requestItemExpandStateSave(this); + QMap feed_custom_data = storeCustomFeedsData(); + removeOldFeedTree(false); + cleanAllItems(); + restoreCustomFeedsData(feed_custom_data, new_tree->getHashedSubTreeFeeds()); + // Model is clean, now store new tree into DB and + // set primary IDs of the items. + storeNewFeedTree(new_tree); + // We have new feed, some feeds were maybe removed, + // so remove left over messages. + removeLeftOverMessages(); + + foreach (RootItem* top_level_item, new_tree->childItems()) { + top_level_item->setParent(nullptr); + requestItemReassignment(top_level_item, this); + } + + updateCounts(true); + new_tree->clearChildren(); + new_tree->deleteLater(); + QList all_items = getSubTree(); + itemChanged(all_items); + requestReloadMessageList(true); + // Now we must refresh expand states. + QList items_to_expand; + + foreach (RootItem* item, all_items) { + if (qApp->settings()->value(GROUP(CategoriesExpandStates), item->hashCode(), item->childCount() > 0).toBool()) { + items_to_expand.append(item); + } + } + + if (!items_to_expand.contains(this)) { + items_to_expand.prepend(this); + } + + requestItemExpand(items_to_expand, true); + } + + setIcon(original_icon); + itemChanged(QList() << this); +} + +RootItem* ServiceRoot::obtainNewTreeForSyncIn() const { + return nullptr; +} + +QStringList ServiceRoot::customIDSOfMessagesForItem(RootItem* item) { + if (item->getParentServiceRoot() != this) { + // Not item from this account. + return QStringList(); + } + + else { + QStringList list; + + switch (item->kind()) { + case RootItemKind::Category: { + foreach (RootItem* child, item->childItems()) { + list.append(customIDSOfMessagesForItem(child)); + } + + return list; + } + + case RootItemKind::ServiceRoot: { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + list = DatabaseQueries::customIdsOfMessagesFromAccount(database, accountId()); + break; + } + + case RootItemKind::Bin: { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + list = DatabaseQueries::customIdsOfMessagesFromBin(database, accountId()); + break; + } + + case RootItemKind::Feed: { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + list = DatabaseQueries::customIdsOfMessagesFromFeed(database, item->customId(), accountId()); + break; + } + + default: + break; + } + + qDebug() << "Custom IDs of messages for some operation are:" << list; + return list; + } +} + +bool ServiceRoot::markFeedsReadUnread(QList items, RootItem::ReadStatus read) { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + + if (DatabaseQueries::markFeedsReadUnread(database, textualFeedIds(items), accountId(), read)) { + QList itemss; + + foreach (Feed* feed, items) { + feed->updateCounts(false); + itemss.append(feed); + } + + itemChanged(itemss); + requestReloadMessageList(read == RootItem::Read); + return true; + } + + else { + return false; + } +} + +QStringList ServiceRoot::textualFeedIds(const QList& feeds) const { + QStringList stringy_ids; + stringy_ids.reserve(feeds.size()); + + foreach (const Feed* feed, feeds) { + stringy_ids.append(QString("'%1'").arg(QString::number(feed->customId()))); + } + + return stringy_ids; +} + +QStringList ServiceRoot::customIDsOfMessages(const QList& changes) { + QStringList list; + + for (int i = 0; i < changes.size(); i++) { + list.append(changes.at(i).first.m_customId); + } + + return list; +} + +QStringList ServiceRoot::customIDsOfMessages(const QList& messages) { + QStringList list; + + foreach (const Message& message, messages) { + list.append(message.m_customId); + } + + return list; +} + +int ServiceRoot::accountId() const { + return m_accountId; +} + +void ServiceRoot::setAccountId(int account_id) { + m_accountId = account_id; +} + +bool ServiceRoot::loadMessagesForItem(RootItem* item, MessagesModel* model) { + if (item->kind() == RootItemKind::Bin) { + model->setFilter(QString("Messages.is_deleted = 1 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1").arg(QString::number(accountId()))); + } + + else { + QList children = item->getSubTreeFeeds(); + QString filter_clause = textualFeedIds(children).join(QSL(", ")); + model->setFilter(QString("Feeds.custom_id IN (%1) AND Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %2").arg(filter_clause, + QString::number(accountId()))); + qDebug("Loading messages from feeds: %s.", qPrintable(filter_clause)); + } + + return true; +} + +bool ServiceRoot::onBeforeSetMessagesRead(RootItem* selected_item, const QList& messages, RootItem::ReadStatus read) { + Q_UNUSED(messages) + Q_UNUSED(read) + Q_UNUSED(selected_item) + return true; +} + +bool ServiceRoot::onAfterSetMessagesRead(RootItem* selected_item, const QList& messages, RootItem::ReadStatus read) { + Q_UNUSED(messages) + Q_UNUSED(read) + selected_item->updateCounts(false); + itemChanged(QList() << selected_item); + return true; +} + +bool ServiceRoot::onBeforeSwitchMessageImportance(RootItem* selected_item, const QList& changes) { + Q_UNUSED(selected_item) + Q_UNUSED(changes) + return true; +} + +bool ServiceRoot::onAfterSwitchMessageImportance(RootItem* selected_item, const QList& changes) { + Q_UNUSED(selected_item) + Q_UNUSED(changes) + return true; +} + +bool ServiceRoot::onBeforeMessagesDelete(RootItem* selected_item, const QList& messages) { + Q_UNUSED(selected_item) + Q_UNUSED(messages) + return true; +} + +bool ServiceRoot::onAfterMessagesDelete(RootItem* selected_item, const QList& messages) { + Q_UNUSED(messages) + // User deleted some messages he selected in message list. + selected_item->updateCounts(true); + RecycleBin* bin = recycleBin(); + + if (selected_item->kind() == RootItemKind::Bin) { + itemChanged(QList() << bin); + } + + else { + if (bin != nullptr) { + bin->updateCounts(true); + itemChanged(QList() << selected_item << bin); + } + + else { + itemChanged(QList() << selected_item); + } + } + + return true; +} + +bool ServiceRoot::onBeforeMessagesRestoredFromBin(RootItem* selected_item, const QList& messages) { + Q_UNUSED(selected_item) + Q_UNUSED(messages) + return true; +} + +bool ServiceRoot::onAfterMessagesRestoredFromBin(RootItem* selected_item, const QList& messages) { + Q_UNUSED(selected_item) + Q_UNUSED(messages) + updateCounts(true); + itemChanged(getSubTree()); + return true; +} + +void ServiceRoot::assembleFeeds(Assignment feeds) { + QHash categories = getHashedSubTreeCategories(); + + foreach (const AssignmentItem& feed, feeds) { + if (feed.first == NO_PARENT_CATEGORY) { + // This is top-level feed, add it to the root item. + appendChild(feed.second); + } + + else if (categories.contains(feed.first)) { + // This feed belongs to this category. + categories.value(feed.first)->appendChild(feed.second); + } + + else { + qWarning("Feed '%s' is loose, skipping it.", qPrintable(feed.second->title())); + } + } +} + +void ServiceRoot::assembleCategories(Assignment categories) { + QHash assignments; + assignments.insert(NO_PARENT_CATEGORY, this); + + // Add top-level categories. + while (!categories.isEmpty()) { + for (int i = 0; i < categories.size(); i++) { + if (assignments.contains(categories.at(i).first)) { + // Parent category of this category is already added. + assignments.value(categories.at(i).first)->appendChild(categories.at(i).second); + // Now, added category can be parent for another categories, add it. + assignments.insert(categories.at(i).second->id(), categories.at(i).second); + // Remove the category from the list, because it was + // added to the final collection. + categories.removeAt(i); + i--; + } + } + } +} diff --git a/src/services/abstract/serviceroot.h b/src/services/abstract/serviceroot.h index 93b49f706..cbf5818b0 100755 --- a/src/services/abstract/serviceroot.h +++ b/src/services/abstract/serviceroot.h @@ -1,217 +1,217 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef SERVICEROOT_H -#define SERVICEROOT_H - -#include "services/abstract/rootitem.h" - -#include "core/message.h" - -#include - - -class FeedsModel; -class RecycleBin; -class QAction; -class MessagesModel; - -// Car here represents ID of the item. -typedef QList > Assignment; -typedef QPair AssignmentItem; -typedef QPair ImportanceChange; - -// THIS IS the root node of the service. -// NOTE: The root usually contains some core functionality of the -// service like service account username/password etc. -class ServiceRoot : public RootItem { - Q_OBJECT - - public: - explicit ServiceRoot(RootItem *parent = nullptr); - virtual ~ServiceRoot(); - - bool deleteViaGui(); - bool markAsReadUnread(ReadStatus status); - - virtual bool supportsFeedAdding() const = 0; - virtual bool supportsCategoryAdding() const = 0; - - // Returns list of specific actions for "Add new item" main window menu. - // So typical list of returned actions could look like: - // a) Add new feed - // b) Add new category - // c) ... - // NOTE: Caller does NOT take ownership of created menu! - virtual QList addItemMenu(); - - // Returns actions to display as context menu. - QList contextMenu(); - - // Returns list of specific actions to be shown in main window menu - // bar in sections "Services -> 'this service'". - // NOTE: Caller does NOT take ownership of created menu! - virtual QList serviceMenu(); - - // Access to recycle bin of this account if there is any. - virtual RecycleBin *recycleBin() const = 0; - - void updateCounts(bool including_total_count); - - QList undeletedMessages() const; - - // Start/stop services. - // Start method is called when feed model gets initialized OR after user adds new service. - // Account should synchronously initialize its children (load them from DB is recommended - // here). - // - // Stop method is called just before application exits OR when - // user explicitly deletes existing service instance. - virtual void start(bool freshly_activated) = 0; - virtual void stop() = 0; - - virtual void saveAllCachedData(); - - // Account ID corresponds with DB attribute Accounts (id). - int accountId() const; - void setAccountId(int account_id); - - // Returns the UNIQUE code of the given service. - // NOTE: Keep in sync with ServiceEntryRoot::code(). - virtual QString code() const = 0; - - // Removes all/read only messages from given underlying feeds. - bool cleanFeeds(QList items, bool clean_read_only); - - // This method should prepare messages for given "item" (download them maybe?) - // into predefined "Messages" table - // and then use method QSqlTableModel::setFilter(....). - // NOTE: It would be more preferable if all messages are downloaded - // right when feeds are updated. - virtual bool loadMessagesForItem(RootItem *item, MessagesModel *model); - - // Called BEFORE this read status update (triggered by user in message list) is stored in DB, - // when false is returned, change is aborted. - // This is the place to make some other changes like updating - // some ONLINE service or something. - // - // "read" is status which is ABOUT TO BE SET. - virtual bool onBeforeSetMessagesRead(RootItem *selected_item, const QList &messages, ReadStatus read); - - // Called AFTER this read status update (triggered by user in message list) is stored in DB, - // when false is returned, change is aborted. - // Here service root should inform (via signals) - // which items are actually changed. - // - // "read" is status which is ABOUT TO BE SET. - virtual bool onAfterSetMessagesRead(RootItem *selected_item, const QList &messages, ReadStatus read); - - // Called BEFORE this importance switch update is stored in DB, - // when false is returned, change is aborted. - // This is the place to make some other changes like updating - // some ONLINE service or something. - // - // "changes" - list of pairs - - virtual bool onBeforeSwitchMessageImportance(RootItem *selected_item, const QList &changes); - - // Called AFTER this importance switch update is stored in DB, - // when false is returned, change is aborted. - // Here service root should inform (via signals) - // which items are actually changed. - // - // "changes" - list of pairs - - virtual bool onAfterSwitchMessageImportance(RootItem *selected_item, const QList &changes); - - // Called BEFORE the list of messages is about to be deleted - // by the user from message list. - virtual bool onBeforeMessagesDelete(RootItem *selected_item, const QList &messages); - - // Called AFTER the list of messages was deleted - // by the user from message list. - virtual bool onAfterMessagesDelete(RootItem *selected_item, const QList &messages); - - // Called BEFORE the list of messages is about to be restored from recycle bin - // by the user from message list. - // Selected item is naturally recycle bin. - virtual bool onBeforeMessagesRestoredFromBin(RootItem *selected_item, const QList &messages); - - // Called AFTER the list of messages was restored from recycle bin - // by the user from message list. - // Selected item is naturally recycle bin. - virtual bool onAfterMessagesRestoredFromBin(RootItem *selected_item, const QList &messages); - - void completelyRemoveAllData(); - QStringList customIDSOfMessagesForItem(RootItem *item); - bool markFeedsReadUnread(QList items, ReadStatus read); - - // Obvious methods to wrap signals. - void itemChanged(const QList &items); - void requestReloadMessageList(bool mark_selected_messages_read); - void requestItemExpand(const QList &items, bool expand); - void requestItemExpandStateSave(RootItem *subtree_root); - void requestItemReassignment(RootItem *item, RootItem *new_parent); - void requestItemRemoval(RootItem *item); - - public slots: - virtual void addNewFeed(const QString &url = QString()) = 0; - virtual void addNewCategory() = 0; - virtual void syncIn(); - - protected: - // This method should obtain new tree of feed/messages/etc to perform - // sync in. - virtual RootItem *obtainNewTreeForSyncIn() const; - - // Removes all messages/categories/feeds which are - // associated with this account. - void removeOldFeedTree(bool including_messages); - void storeNewFeedTree(RootItem *root); - void cleanAllItems(); - - // Removes messages which do not belong to any - // existing feed. - // - // NOTE: This situation may happen if user deletes some feed - // from another machine and then performs sync-in on this machine. - void removeLeftOverMessages(); - - QStringList textualFeedIds(const QList &feeds) const; - QStringList customIDsOfMessages(const QList &changes); - QStringList customIDsOfMessages(const QList &messages); - - // Takes lists of feeds/categories and assembles them into the tree structure. - void assembleCategories(Assignment categories); - void assembleFeeds(Assignment feeds); - - signals: - // Emitted if data in any item belonging to this root are changed. - void dataChanged(QList items); - void reloadMessageListRequested(bool mark_selected_messages_read); - void itemExpandRequested(QList items, bool expand); - void itemExpandStateSaveRequested(RootItem *subtree_root); - - void itemReassignmentRequested(RootItem *item, RootItem *new_parent); - void itemRemovalRequested(RootItem *item); - - private: - virtual QMap storeCustomFeedsData() = 0; - virtual void restoreCustomFeedsData(const QMap &data, const QHash &feeds) = 0; - - int m_accountId; -}; - -#endif // SERVICEROOT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef SERVICEROOT_H +#define SERVICEROOT_H + +#include "services/abstract/rootitem.h" + +#include "core/message.h" + +#include + + +class FeedsModel; +class RecycleBin; +class QAction; +class MessagesModel; + +// Car here represents ID of the item. +typedef QList> Assignment; +typedef QPair AssignmentItem; +typedef QPair ImportanceChange; + +// THIS IS the root node of the service. +// NOTE: The root usually contains some core functionality of the +// service like service account username/password etc. +class ServiceRoot : public RootItem { + Q_OBJECT + + public: + explicit ServiceRoot(RootItem* parent = nullptr); + virtual ~ServiceRoot(); + + bool deleteViaGui(); + bool markAsReadUnread(ReadStatus status); + + virtual bool supportsFeedAdding() const = 0; + virtual bool supportsCategoryAdding() const = 0; + + // Returns list of specific actions for "Add new item" main window menu. + // So typical list of returned actions could look like: + // a) Add new feed + // b) Add new category + // c) ... + // NOTE: Caller does NOT take ownership of created menu! + virtual QList addItemMenu(); + + // Returns actions to display as context menu. + QList contextMenu(); + + // Returns list of specific actions to be shown in main window menu + // bar in sections "Services -> 'this service'". + // NOTE: Caller does NOT take ownership of created menu! + virtual QList serviceMenu(); + + // Access to recycle bin of this account if there is any. + virtual RecycleBin* recycleBin() const = 0; + + void updateCounts(bool including_total_count); + + QList undeletedMessages() const; + + // Start/stop services. + // Start method is called when feed model gets initialized OR after user adds new service. + // Account should synchronously initialize its children (load them from DB is recommended + // here). + // + // Stop method is called just before application exits OR when + // user explicitly deletes existing service instance. + virtual void start(bool freshly_activated) = 0; + virtual void stop() = 0; + + virtual void saveAllCachedData(); + + // Account ID corresponds with DB attribute Accounts (id). + int accountId() const; + void setAccountId(int account_id); + + // Returns the UNIQUE code of the given service. + // NOTE: Keep in sync with ServiceEntryRoot::code(). + virtual QString code() const = 0; + + // Removes all/read only messages from given underlying feeds. + bool cleanFeeds(QList items, bool clean_read_only); + + // This method should prepare messages for given "item" (download them maybe?) + // into predefined "Messages" table + // and then use method QSqlTableModel::setFilter(....). + // NOTE: It would be more preferable if all messages are downloaded + // right when feeds are updated. + virtual bool loadMessagesForItem(RootItem* item, MessagesModel* model); + + // Called BEFORE this read status update (triggered by user in message list) is stored in DB, + // when false is returned, change is aborted. + // This is the place to make some other changes like updating + // some ONLINE service or something. + // + // "read" is status which is ABOUT TO BE SET. + virtual bool onBeforeSetMessagesRead(RootItem* selected_item, const QList& messages, ReadStatus read); + + // Called AFTER this read status update (triggered by user in message list) is stored in DB, + // when false is returned, change is aborted. + // Here service root should inform (via signals) + // which items are actually changed. + // + // "read" is status which is ABOUT TO BE SET. + virtual bool onAfterSetMessagesRead(RootItem* selected_item, const QList& messages, ReadStatus read); + + // Called BEFORE this importance switch update is stored in DB, + // when false is returned, change is aborted. + // This is the place to make some other changes like updating + // some ONLINE service or something. + // + // "changes" - list of pairs - + virtual bool onBeforeSwitchMessageImportance(RootItem* selected_item, const QList& changes); + + // Called AFTER this importance switch update is stored in DB, + // when false is returned, change is aborted. + // Here service root should inform (via signals) + // which items are actually changed. + // + // "changes" - list of pairs - + virtual bool onAfterSwitchMessageImportance(RootItem* selected_item, const QList& changes); + + // Called BEFORE the list of messages is about to be deleted + // by the user from message list. + virtual bool onBeforeMessagesDelete(RootItem* selected_item, const QList& messages); + + // Called AFTER the list of messages was deleted + // by the user from message list. + virtual bool onAfterMessagesDelete(RootItem* selected_item, const QList& messages); + + // Called BEFORE the list of messages is about to be restored from recycle bin + // by the user from message list. + // Selected item is naturally recycle bin. + virtual bool onBeforeMessagesRestoredFromBin(RootItem* selected_item, const QList& messages); + + // Called AFTER the list of messages was restored from recycle bin + // by the user from message list. + // Selected item is naturally recycle bin. + virtual bool onAfterMessagesRestoredFromBin(RootItem* selected_item, const QList& messages); + + void completelyRemoveAllData(); + QStringList customIDSOfMessagesForItem(RootItem* item); + bool markFeedsReadUnread(QList items, ReadStatus read); + + // Obvious methods to wrap signals. + void itemChanged(const QList& items); + void requestReloadMessageList(bool mark_selected_messages_read); + void requestItemExpand(const QList& items, bool expand); + void requestItemExpandStateSave(RootItem* subtree_root); + void requestItemReassignment(RootItem* item, RootItem* new_parent); + void requestItemRemoval(RootItem* item); + + public slots: + virtual void addNewFeed(const QString& url = QString()) = 0; + virtual void addNewCategory() = 0; + virtual void syncIn(); + + protected: + // This method should obtain new tree of feed/messages/etc to perform + // sync in. + virtual RootItem* obtainNewTreeForSyncIn() const; + + // Removes all messages/categories/feeds which are + // associated with this account. + void removeOldFeedTree(bool including_messages); + void storeNewFeedTree(RootItem* root); + void cleanAllItems(); + + // Removes messages which do not belong to any + // existing feed. + // + // NOTE: This situation may happen if user deletes some feed + // from another machine and then performs sync-in on this machine. + void removeLeftOverMessages(); + + QStringList textualFeedIds(const QList& feeds) const; + QStringList customIDsOfMessages(const QList& changes); + QStringList customIDsOfMessages(const QList& messages); + + // Takes lists of feeds/categories and assembles them into the tree structure. + void assembleCategories(Assignment categories); + void assembleFeeds(Assignment feeds); + + signals: + // Emitted if data in any item belonging to this root are changed. + void dataChanged(QList items); + void reloadMessageListRequested(bool mark_selected_messages_read); + void itemExpandRequested(QList items, bool expand); + void itemExpandStateSaveRequested(RootItem* subtree_root); + + void itemReassignmentRequested(RootItem* item, RootItem* new_parent); + void itemRemovalRequested(RootItem* item); + + private: + virtual QMap storeCustomFeedsData() = 0; + virtual void restoreCustomFeedsData(const QMap& data, const QHash& feeds) = 0; + + int m_accountId; +}; + +#endif // SERVICEROOT_H diff --git a/src/services/owncloud/definitions.h b/src/services/owncloud/definitions.h index 19492bf4d..fb19ac250 100755 --- a/src/services/owncloud/definitions.h +++ b/src/services/owncloud/definitions.h @@ -1,27 +1,27 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef OWNCLOUD_DEFINITIONS_H -#define OWNCLOUD_DEFINITIONS_H - -#define CONTENT_TYPE "application/json; charset=utf-8" -#define API_VERSION "1.2" -#define API_PATH "index.php/apps/news/api/v1-2/" -#define MINIMAL_OC_VERSION "6.0.5" - -#endif // OWNCLOUD_DEFINITIONS_H - +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef OWNCLOUD_DEFINITIONS_H +#define OWNCLOUD_DEFINITIONS_H + +#define CONTENT_TYPE "application/json; charset=utf-8" +#define API_VERSION "1.2" +#define API_PATH "index.php/apps/news/api/v1-2/" +#define MINIMAL_OC_VERSION "6.0.5" + +#endif // OWNCLOUD_DEFINITIONS_H + diff --git a/src/services/owncloud/gui/formeditowncloudaccount.cpp b/src/services/owncloud/gui/formeditowncloudaccount.cpp index 5d825478b..8f418f713 100755 --- a/src/services/owncloud/gui/formeditowncloudaccount.cpp +++ b/src/services/owncloud/gui/formeditowncloudaccount.cpp @@ -1,197 +1,193 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/owncloud/gui/formeditowncloudaccount.h" - -#include "services/owncloud/definitions.h" -#include "services/owncloud/owncloudserviceroot.h" -#include "services/owncloud/network/owncloudnetworkfactory.h" -#include "miscellaneous/iconfactory.h" -#include "network-web/networkfactory.h" - - -FormEditOwnCloudAccount::FormEditOwnCloudAccount(QWidget *parent) - : QDialog(parent), m_ui(new Ui::FormEditOwnCloudAccount), m_editableRoot(nullptr) { - m_ui->setupUi(this); - m_btnOk = m_ui->m_buttonBox->button(QDialogButtonBox::Ok); - - setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); - setWindowIcon(qApp->icons()->fromTheme(QSL("owncloud"))); - - m_ui->m_lblTestResult->label()->setWordWrap(true); - m_ui->m_lblServerSideUpdateInformation->setText(tr("Leaving this option on causes that updates " - "of feeds will be probably much slower and may time-out often.")); - m_ui->m_lblDescription->setText(tr("Note that at least version %1 is required.").arg(MINIMAL_OC_VERSION)); - m_ui->m_txtPassword->lineEdit()->setPlaceholderText(tr("Password for your ownCloud account")); - m_ui->m_txtUsername->lineEdit()->setPlaceholderText(tr("Username for your ownCloud account")); - m_ui->m_txtUrl->lineEdit()->setPlaceholderText(tr("URL of your ownCloud server, without any API path")); - m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Information, - tr("No test done yet."), - tr("Here, results of connection test are shown.")); - - setTabOrder(m_ui->m_txtUrl->lineEdit(), m_ui->m_checkServerSideUpdate); - setTabOrder(m_ui->m_checkServerSideUpdate, m_ui->m_txtUsername->lineEdit()); - setTabOrder(m_ui->m_txtUsername->lineEdit(), m_ui->m_txtPassword->lineEdit()); - setTabOrder(m_ui->m_txtPassword->lineEdit(), m_ui->m_checkShowPassword); - setTabOrder(m_ui->m_checkShowPassword, m_ui->m_btnTestSetup); - setTabOrder(m_ui->m_btnTestSetup, m_ui->m_buttonBox); - - connect(m_ui->m_checkShowPassword, &QCheckBox::toggled, this, &FormEditOwnCloudAccount::displayPassword); - connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &FormEditOwnCloudAccount::onClickedOk); - connect(m_ui->m_buttonBox, &QDialogButtonBox::rejected, this, &FormEditOwnCloudAccount::onClickedCancel); - connect(m_ui->m_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditOwnCloudAccount::onPasswordChanged); - connect(m_ui->m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditOwnCloudAccount::onUsernameChanged); - connect(m_ui->m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditOwnCloudAccount::onUrlChanged); - connect(m_ui->m_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditOwnCloudAccount::checkOkButton); - connect(m_ui->m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditOwnCloudAccount::checkOkButton); - connect(m_ui->m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditOwnCloudAccount::checkOkButton); - connect(m_ui->m_btnTestSetup, &QPushButton::clicked, this, &FormEditOwnCloudAccount::performTest); - - onPasswordChanged(); - onUsernameChanged(); - onUrlChanged(); - checkOkButton(); - displayPassword(false); -} - -FormEditOwnCloudAccount::~FormEditOwnCloudAccount() { -} - -OwnCloudServiceRoot *FormEditOwnCloudAccount::execForCreate() { - setWindowTitle(tr("Add new ownCloud News account")); - exec(); - return m_editableRoot; -} - -void FormEditOwnCloudAccount::execForEdit(OwnCloudServiceRoot *existing_root) { - setWindowTitle(tr("Edit existing ownCloud News account")); - m_editableRoot = existing_root; - - m_ui->m_txtUsername->lineEdit()->setText(existing_root->network()->authUsername()); - m_ui->m_txtPassword->lineEdit()->setText(existing_root->network()->authPassword()); - m_ui->m_txtUrl->lineEdit()->setText(existing_root->network()->url()); - m_ui->m_checkServerSideUpdate->setChecked(existing_root->network()->forceServerSideUpdate()); - - exec(); -} - -void FormEditOwnCloudAccount::displayPassword(bool display) { - m_ui->m_txtPassword->lineEdit()->setEchoMode(display ? QLineEdit::Normal : QLineEdit::Password); -} - -void FormEditOwnCloudAccount::performTest() { - OwnCloudNetworkFactory factory; - - factory.setAuthUsername(m_ui->m_txtUsername->lineEdit()->text()); - factory.setAuthPassword(m_ui->m_txtPassword->lineEdit()->text()); - factory.setUrl(m_ui->m_txtUrl->lineEdit()->text()); - factory.setForceServerSideUpdate(m_ui->m_checkServerSideUpdate->isChecked()); - - OwnCloudStatusResponse result = factory.status(); - - if (result.isLoaded()) { - if (!SystemFactory::isVersionEqualOrNewer(result.version(), MINIMAL_OC_VERSION)) { - m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, - tr("Selected ownCloud News server is running unsupported version (%1). At least version %2 is required.").arg(result.version(), - MINIMAL_OC_VERSION), - tr("Selected ownCloud News server is running unsupported version.")); - } - else { - m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Ok, - tr("ownCloud News server is okay, running with version %1, while at least version %2 is required.").arg(result.version(), - MINIMAL_OC_VERSION), - tr("ownCloud News server is okay.")); - } - } - else if (factory.lastError() != QNetworkReply::NoError) { - m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, - tr("Network error: '%1'.").arg(NetworkFactory::networkErrorText(factory.lastError())), - tr("Network error, have you entered correct ownCloud endpoint and password?")); - } - else { - m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, - tr("Unspecified error, did you enter correct URL?"), - tr("Unspecified error, did you enter correct URL?")); - } -} - -void FormEditOwnCloudAccount::onClickedOk() { - bool editing_account = true; - - if (m_editableRoot == nullptr) { - // We want to confirm newly created account. - // So save new account into DB, setup its properties. - m_editableRoot = new OwnCloudServiceRoot(); - editing_account = false; - } - - m_editableRoot->network()->setUrl(m_ui->m_txtUrl->lineEdit()->text()); - m_editableRoot->network()->setAuthUsername(m_ui->m_txtUsername->lineEdit()->text()); - m_editableRoot->network()->setAuthPassword(m_ui->m_txtPassword->lineEdit()->text()); - m_editableRoot->network()->setForceServerSideUpdate(m_ui->m_checkServerSideUpdate->isChecked()); - m_editableRoot->saveAccountDataToDatabase(); - - accept(); - - if (editing_account) { - m_editableRoot->completelyRemoveAllData(); - m_editableRoot->syncIn(); - } -} - -void FormEditOwnCloudAccount::onClickedCancel() { - reject(); -} - -void FormEditOwnCloudAccount::onUsernameChanged() { - const QString username = m_ui->m_txtUsername->lineEdit()->text(); - - if (username.isEmpty()) { - m_ui->m_txtUsername->setStatus(WidgetWithStatus::Error, tr("Username cannot be empty.")); - } - else { - m_ui->m_txtUsername->setStatus(WidgetWithStatus::Ok, tr("Username is okay.")); - } -} - -void FormEditOwnCloudAccount::onPasswordChanged() { - const QString password = m_ui->m_txtPassword->lineEdit()->text(); - - if (password.isEmpty()) { - m_ui->m_txtPassword->setStatus(WidgetWithStatus::Error, tr("Password cannot be empty.")); - } - else { - m_ui->m_txtPassword->setStatus(WidgetWithStatus::Ok, tr("Password is okay.")); - } -} - -void FormEditOwnCloudAccount::onUrlChanged() { - const QString url = m_ui->m_txtUrl->lineEdit()->text(); - - if (url.isEmpty()) { - m_ui->m_txtUrl->setStatus(WidgetWithStatus::Error, tr("URL cannot be empty.")); - } - else { - m_ui->m_txtUrl->setStatus(WidgetWithStatus::Ok, tr("URL is okay.")); - } -} - -void FormEditOwnCloudAccount::checkOkButton() { - m_btnOk->setEnabled(!m_ui->m_txtUsername->lineEdit()->text().isEmpty() && - !m_ui->m_txtPassword->lineEdit()->text().isEmpty() && - !m_ui->m_txtUrl->lineEdit()->text().isEmpty()); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/owncloud/gui/formeditowncloudaccount.h" + +#include "services/owncloud/definitions.h" +#include "services/owncloud/owncloudserviceroot.h" +#include "services/owncloud/network/owncloudnetworkfactory.h" +#include "miscellaneous/iconfactory.h" +#include "network-web/networkfactory.h" + + +FormEditOwnCloudAccount::FormEditOwnCloudAccount(QWidget* parent) + : QDialog(parent), m_ui(new Ui::FormEditOwnCloudAccount), m_editableRoot(nullptr) { + m_ui->setupUi(this); + m_btnOk = m_ui->m_buttonBox->button(QDialogButtonBox::Ok); + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); + setWindowIcon(qApp->icons()->fromTheme(QSL("owncloud"))); + m_ui->m_lblTestResult->label()->setWordWrap(true); + m_ui->m_lblServerSideUpdateInformation->setText(tr("Leaving this option on causes that updates " + "of feeds will be probably much slower and may time-out often.")); + m_ui->m_lblDescription->setText(tr("Note that at least version %1 is required.").arg(MINIMAL_OC_VERSION)); + m_ui->m_txtPassword->lineEdit()->setPlaceholderText(tr("Password for your ownCloud account")); + m_ui->m_txtUsername->lineEdit()->setPlaceholderText(tr("Username for your ownCloud account")); + m_ui->m_txtUrl->lineEdit()->setPlaceholderText(tr("URL of your ownCloud server, without any API path")); + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Information, + tr("No test done yet."), + tr("Here, results of connection test are shown.")); + setTabOrder(m_ui->m_txtUrl->lineEdit(), m_ui->m_checkServerSideUpdate); + setTabOrder(m_ui->m_checkServerSideUpdate, m_ui->m_txtUsername->lineEdit()); + setTabOrder(m_ui->m_txtUsername->lineEdit(), m_ui->m_txtPassword->lineEdit()); + setTabOrder(m_ui->m_txtPassword->lineEdit(), m_ui->m_checkShowPassword); + setTabOrder(m_ui->m_checkShowPassword, m_ui->m_btnTestSetup); + setTabOrder(m_ui->m_btnTestSetup, m_ui->m_buttonBox); + connect(m_ui->m_checkShowPassword, &QCheckBox::toggled, this, &FormEditOwnCloudAccount::displayPassword); + connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &FormEditOwnCloudAccount::onClickedOk); + connect(m_ui->m_buttonBox, &QDialogButtonBox::rejected, this, &FormEditOwnCloudAccount::onClickedCancel); + connect(m_ui->m_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditOwnCloudAccount::onPasswordChanged); + connect(m_ui->m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditOwnCloudAccount::onUsernameChanged); + connect(m_ui->m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditOwnCloudAccount::onUrlChanged); + connect(m_ui->m_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditOwnCloudAccount::checkOkButton); + connect(m_ui->m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditOwnCloudAccount::checkOkButton); + connect(m_ui->m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditOwnCloudAccount::checkOkButton); + connect(m_ui->m_btnTestSetup, &QPushButton::clicked, this, &FormEditOwnCloudAccount::performTest); + onPasswordChanged(); + onUsernameChanged(); + onUrlChanged(); + checkOkButton(); + displayPassword(false); +} + +FormEditOwnCloudAccount::~FormEditOwnCloudAccount() { +} + +OwnCloudServiceRoot* FormEditOwnCloudAccount::execForCreate() { + setWindowTitle(tr("Add new ownCloud News account")); + exec(); + return m_editableRoot; +} + +void FormEditOwnCloudAccount::execForEdit(OwnCloudServiceRoot* existing_root) { + setWindowTitle(tr("Edit existing ownCloud News account")); + m_editableRoot = existing_root; + m_ui->m_txtUsername->lineEdit()->setText(existing_root->network()->authUsername()); + m_ui->m_txtPassword->lineEdit()->setText(existing_root->network()->authPassword()); + m_ui->m_txtUrl->lineEdit()->setText(existing_root->network()->url()); + m_ui->m_checkServerSideUpdate->setChecked(existing_root->network()->forceServerSideUpdate()); + exec(); +} + +void FormEditOwnCloudAccount::displayPassword(bool display) { + m_ui->m_txtPassword->lineEdit()->setEchoMode(display ? QLineEdit::Normal : QLineEdit::Password); +} + +void FormEditOwnCloudAccount::performTest() { + OwnCloudNetworkFactory factory; + factory.setAuthUsername(m_ui->m_txtUsername->lineEdit()->text()); + factory.setAuthPassword(m_ui->m_txtPassword->lineEdit()->text()); + factory.setUrl(m_ui->m_txtUrl->lineEdit()->text()); + factory.setForceServerSideUpdate(m_ui->m_checkServerSideUpdate->isChecked()); + OwnCloudStatusResponse result = factory.status(); + + if (result.isLoaded()) { + if (!SystemFactory::isVersionEqualOrNewer(result.version(), MINIMAL_OC_VERSION)) { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("Selected ownCloud News server is running unsupported version (%1). At least version %2 is required.").arg(result.version(), + MINIMAL_OC_VERSION), + tr("Selected ownCloud News server is running unsupported version.")); + } + + else { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Ok, + tr("ownCloud News server is okay, running with version %1, while at least version %2 is required.").arg(result.version(), + MINIMAL_OC_VERSION), + tr("ownCloud News server is okay.")); + } + } + + else if (factory.lastError() != QNetworkReply::NoError) { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("Network error: '%1'.").arg(NetworkFactory::networkErrorText(factory.lastError())), + tr("Network error, have you entered correct ownCloud endpoint and password?")); + } + + else { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("Unspecified error, did you enter correct URL?"), + tr("Unspecified error, did you enter correct URL?")); + } +} + +void FormEditOwnCloudAccount::onClickedOk() { + bool editing_account = true; + + if (m_editableRoot == nullptr) { + // We want to confirm newly created account. + // So save new account into DB, setup its properties. + m_editableRoot = new OwnCloudServiceRoot(); + editing_account = false; + } + + m_editableRoot->network()->setUrl(m_ui->m_txtUrl->lineEdit()->text()); + m_editableRoot->network()->setAuthUsername(m_ui->m_txtUsername->lineEdit()->text()); + m_editableRoot->network()->setAuthPassword(m_ui->m_txtPassword->lineEdit()->text()); + m_editableRoot->network()->setForceServerSideUpdate(m_ui->m_checkServerSideUpdate->isChecked()); + m_editableRoot->saveAccountDataToDatabase(); + accept(); + + if (editing_account) { + m_editableRoot->completelyRemoveAllData(); + m_editableRoot->syncIn(); + } +} + +void FormEditOwnCloudAccount::onClickedCancel() { + reject(); +} + +void FormEditOwnCloudAccount::onUsernameChanged() { + const QString username = m_ui->m_txtUsername->lineEdit()->text(); + + if (username.isEmpty()) { + m_ui->m_txtUsername->setStatus(WidgetWithStatus::Error, tr("Username cannot be empty.")); + } + + else { + m_ui->m_txtUsername->setStatus(WidgetWithStatus::Ok, tr("Username is okay.")); + } +} + +void FormEditOwnCloudAccount::onPasswordChanged() { + const QString password = m_ui->m_txtPassword->lineEdit()->text(); + + if (password.isEmpty()) { + m_ui->m_txtPassword->setStatus(WidgetWithStatus::Error, tr("Password cannot be empty.")); + } + + else { + m_ui->m_txtPassword->setStatus(WidgetWithStatus::Ok, tr("Password is okay.")); + } +} + +void FormEditOwnCloudAccount::onUrlChanged() { + const QString url = m_ui->m_txtUrl->lineEdit()->text(); + + if (url.isEmpty()) { + m_ui->m_txtUrl->setStatus(WidgetWithStatus::Error, tr("URL cannot be empty.")); + } + + else { + m_ui->m_txtUrl->setStatus(WidgetWithStatus::Ok, tr("URL is okay.")); + } +} + +void FormEditOwnCloudAccount::checkOkButton() { + m_btnOk->setEnabled(!m_ui->m_txtUsername->lineEdit()->text().isEmpty() && + !m_ui->m_txtPassword->lineEdit()->text().isEmpty() && + !m_ui->m_txtUrl->lineEdit()->text().isEmpty()); +} diff --git a/src/services/owncloud/gui/formeditowncloudaccount.h b/src/services/owncloud/gui/formeditowncloudaccount.h index 5f4e7f41d..f213e1439 100755 --- a/src/services/owncloud/gui/formeditowncloudaccount.h +++ b/src/services/owncloud/gui/formeditowncloudaccount.h @@ -1,59 +1,59 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FORMEDITOWNCLOUDACCOUNT_H -#define FORMEDITOWNCLOUDACCOUNT_H - -#include - -#include "ui_formeditowncloudaccount.h" - - -namespace Ui { - class FormEditAccount; -} - -class OwnCloudServiceRoot; - -class FormEditOwnCloudAccount : public QDialog { - Q_OBJECT - - public: - explicit FormEditOwnCloudAccount(QWidget *parent = 0); - virtual ~FormEditOwnCloudAccount(); - - OwnCloudServiceRoot *execForCreate(); - void execForEdit(OwnCloudServiceRoot *existing_root); - - private slots: - void displayPassword(bool display); - void performTest(); - void onClickedOk(); - void onClickedCancel(); - - void onUsernameChanged(); - void onPasswordChanged(); - void onUrlChanged(); - void checkOkButton(); - - private: - QScopedPointer m_ui; - OwnCloudServiceRoot *m_editableRoot; - QPushButton *m_btnOk; -}; - -#endif // FORMEDITOWNCLOUDACCOUNT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FORMEDITOWNCLOUDACCOUNT_H +#define FORMEDITOWNCLOUDACCOUNT_H + +#include + +#include "ui_formeditowncloudaccount.h" + + +namespace Ui { + class FormEditAccount; +} + +class OwnCloudServiceRoot; + +class FormEditOwnCloudAccount : public QDialog { + Q_OBJECT + + public: + explicit FormEditOwnCloudAccount(QWidget* parent = 0); + virtual ~FormEditOwnCloudAccount(); + + OwnCloudServiceRoot* execForCreate(); + void execForEdit(OwnCloudServiceRoot* existing_root); + + private slots: + void displayPassword(bool display); + void performTest(); + void onClickedOk(); + void onClickedCancel(); + + void onUsernameChanged(); + void onPasswordChanged(); + void onUrlChanged(); + void checkOkButton(); + + private: + QScopedPointer m_ui; + OwnCloudServiceRoot* m_editableRoot; + QPushButton* m_btnOk; +}; + +#endif // FORMEDITOWNCLOUDACCOUNT_H diff --git a/src/services/owncloud/gui/formowncloudfeeddetails.cpp b/src/services/owncloud/gui/formowncloudfeeddetails.cpp index b80afb158..f1ff70d1a 100755 --- a/src/services/owncloud/gui/formowncloudfeeddetails.cpp +++ b/src/services/owncloud/gui/formowncloudfeeddetails.cpp @@ -1,100 +1,98 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/owncloud/gui/formowncloudfeeddetails.h" - -#include "services/owncloud/owncloudfeed.h" -#include "services/owncloud/owncloudserviceroot.h" -#include "services/owncloud/network/owncloudnetworkfactory.h" -#include "miscellaneous/application.h" - -#include - - -FormOwnCloudFeedDetails::FormOwnCloudFeedDetails(ServiceRoot *service_root, QWidget *parent) - : FormFeedDetails(service_root, parent) { - m_ui->m_spinAutoUpdateInterval->setEnabled(false); - m_ui->m_cmbAutoUpdateType->setEnabled(false); - m_ui->m_cmbType->setEnabled(false); - m_ui->m_cmbEncoding->setEnabled(false); - m_ui->m_btnFetchMetadata->setEnabled(false); - m_ui->m_btnIcon->setEnabled(false); - m_ui->m_txtTitle->setEnabled(false); - m_ui->m_txtUrl->setEnabled(true); - m_ui->m_txtDescription->setEnabled(false); -} - -void FormOwnCloudFeedDetails::apply() { - if (m_editableFeed != nullptr) { - bool renamed = false; - - if (m_ui->m_txtTitle->lineEdit()->text() != m_editableFeed->title()) { - if (!qobject_cast(m_serviceRoot)->network()->renameFeed(m_ui->m_txtTitle->lineEdit()->text(), m_editableFeed->customId())) { - qWarning("ownCloud: Došlo k problému při prejmenování kanálu s ownCloud ID '%d'.", m_editableFeed->customId()); - } - else { - renamed = true; - } - } - - // User edited auto-update status. Save it. - OwnCloudFeed *new_feed_data = new OwnCloudFeed(); - - new_feed_data->setAutoUpdateType(static_cast(m_ui->m_cmbAutoUpdateType->itemData(m_ui->m_cmbAutoUpdateType->currentIndex()).toInt())); - new_feed_data->setAutoUpdateInitialInterval(m_ui->m_spinAutoUpdateInterval->value()); - - qobject_cast(m_editableFeed)->editItself(new_feed_data); - - delete new_feed_data; - - if (renamed) { - QTimer::singleShot(200, m_serviceRoot, SLOT(syncIn())); - } - } - else { - const RootItem *parent = static_cast(m_ui->m_cmbParentCategory->itemData(m_ui->m_cmbParentCategory->currentIndex()).value()); - const int category_id = parent->kind() == RootItemKind::ServiceRoot ? 0 : parent->customId(); - const bool response = qobject_cast(m_serviceRoot)->network()->createFeed(m_ui->m_txtUrl->lineEdit()->text(), category_id); - - if (response) { - // Feed was added online. - accept(); - qApp->showGuiMessage(tr("Feed added"), tr("Feed was added, triggering sync in now."), QSystemTrayIcon::Information); - QTimer::singleShot(100, m_serviceRoot, SLOT(syncIn())); - } - else { - reject(); - qApp->showGuiMessage(tr("Cannot add feed"), - tr("Feed was not added due to error."), - QSystemTrayIcon::Critical, qApp->mainFormWidget(), true); - } - } - - accept(); -} - -void FormOwnCloudFeedDetails::setEditableFeed(Feed *editable_feed) { - m_ui->m_cmbAutoUpdateType->setEnabled(true); - - FormFeedDetails::setEditableFeed(editable_feed); - - m_ui->m_txtTitle->setEnabled(true); - m_ui->m_gbAuthentication->setEnabled(false); - m_ui->m_txtUrl->setEnabled(false); - m_ui->m_lblParentCategory->setEnabled(false); - m_ui->m_cmbParentCategory->setEnabled(false); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/owncloud/gui/formowncloudfeeddetails.h" + +#include "services/owncloud/owncloudfeed.h" +#include "services/owncloud/owncloudserviceroot.h" +#include "services/owncloud/network/owncloudnetworkfactory.h" +#include "miscellaneous/application.h" + +#include + + +FormOwnCloudFeedDetails::FormOwnCloudFeedDetails(ServiceRoot* service_root, QWidget* parent) + : FormFeedDetails(service_root, parent) { + m_ui->m_spinAutoUpdateInterval->setEnabled(false); + m_ui->m_cmbAutoUpdateType->setEnabled(false); + m_ui->m_cmbType->setEnabled(false); + m_ui->m_cmbEncoding->setEnabled(false); + m_ui->m_btnFetchMetadata->setEnabled(false); + m_ui->m_btnIcon->setEnabled(false); + m_ui->m_txtTitle->setEnabled(false); + m_ui->m_txtUrl->setEnabled(true); + m_ui->m_txtDescription->setEnabled(false); +} + +void FormOwnCloudFeedDetails::apply() { + if (m_editableFeed != nullptr) { + bool renamed = false; + + if (m_ui->m_txtTitle->lineEdit()->text() != m_editableFeed->title()) { + if (!qobject_cast(m_serviceRoot)->network()->renameFeed(m_ui->m_txtTitle->lineEdit()->text(), m_editableFeed->customId())) { + qWarning("ownCloud: Došlo k problému při prejmenování kanálu s ownCloud ID '%d'.", m_editableFeed->customId()); + } + + else { + renamed = true; + } + } + + // User edited auto-update status. Save it. + OwnCloudFeed* new_feed_data = new OwnCloudFeed(); + new_feed_data->setAutoUpdateType(static_cast(m_ui->m_cmbAutoUpdateType->itemData(m_ui->m_cmbAutoUpdateType->currentIndex()).toInt())); + new_feed_data->setAutoUpdateInitialInterval(m_ui->m_spinAutoUpdateInterval->value()); + qobject_cast(m_editableFeed)->editItself(new_feed_data); + delete new_feed_data; + + if (renamed) { + QTimer::singleShot(200, m_serviceRoot, SLOT(syncIn())); + } + } + + else { + const RootItem* parent = static_cast(m_ui->m_cmbParentCategory->itemData(m_ui->m_cmbParentCategory->currentIndex()).value()); + const int category_id = parent->kind() == RootItemKind::ServiceRoot ? 0 : parent->customId(); + const bool response = qobject_cast(m_serviceRoot)->network()->createFeed(m_ui->m_txtUrl->lineEdit()->text(), category_id); + + if (response) { + // Feed was added online. + accept(); + qApp->showGuiMessage(tr("Feed added"), tr("Feed was added, triggering sync in now."), QSystemTrayIcon::Information); + QTimer::singleShot(100, m_serviceRoot, SLOT(syncIn())); + } + + else { + reject(); + qApp->showGuiMessage(tr("Cannot add feed"), + tr("Feed was not added due to error."), + QSystemTrayIcon::Critical, qApp->mainFormWidget(), true); + } + } + + accept(); +} + +void FormOwnCloudFeedDetails::setEditableFeed(Feed* editable_feed) { + m_ui->m_cmbAutoUpdateType->setEnabled(true); + FormFeedDetails::setEditableFeed(editable_feed); + m_ui->m_txtTitle->setEnabled(true); + m_ui->m_gbAuthentication->setEnabled(false); + m_ui->m_txtUrl->setEnabled(false); + m_ui->m_lblParentCategory->setEnabled(false); + m_ui->m_cmbParentCategory->setEnabled(false); +} diff --git a/src/services/owncloud/gui/formowncloudfeeddetails.h b/src/services/owncloud/gui/formowncloudfeeddetails.h index 069341928..1c1bdc913 100755 --- a/src/services/owncloud/gui/formowncloudfeeddetails.h +++ b/src/services/owncloud/gui/formowncloudfeeddetails.h @@ -1,37 +1,37 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FORMOWNCLOUDFEEDDETAILS_H -#define FORMOWNCLOUDFEEDDETAILS_H - -#include "services/abstract/gui/formfeeddetails.h" - - -class FormOwnCloudFeedDetails : public FormFeedDetails { - Q_OBJECT - - public: - explicit FormOwnCloudFeedDetails(ServiceRoot *service_root, QWidget *parent = 0); - - protected slots: - void apply(); - - protected: - void setEditableFeed(Feed *editable_feed); -}; - -#endif // FORMOWNCLOUDFEEDDETAILS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FORMOWNCLOUDFEEDDETAILS_H +#define FORMOWNCLOUDFEEDDETAILS_H + +#include "services/abstract/gui/formfeeddetails.h" + + +class FormOwnCloudFeedDetails : public FormFeedDetails { + Q_OBJECT + + public: + explicit FormOwnCloudFeedDetails(ServiceRoot* service_root, QWidget* parent = 0); + + protected slots: + void apply(); + + protected: + void setEditableFeed(Feed* editable_feed); +}; + +#endif // FORMOWNCLOUDFEEDDETAILS_H diff --git a/src/services/owncloud/network/owncloudnetworkfactory.cpp b/src/services/owncloud/network/owncloudnetworkfactory.cpp index f318e2b80..14a94801b 100755 --- a/src/services/owncloud/network/owncloudnetworkfactory.cpp +++ b/src/services/owncloud/network/owncloudnetworkfactory.cpp @@ -1,596 +1,581 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/owncloud/network/owncloudnetworkfactory.h" - -#include "services/owncloud/definitions.h" -#include "network-web/networkfactory.h" -#include "miscellaneous/application.h" -#include "miscellaneous/settings.h" -#include "miscellaneous/textfactory.h" -#include "services/abstract/rootitem.h" -#include "services/owncloud/owncloudcategory.h" -#include "services/owncloud/owncloudfeed.h" - -#include -#include -#include -#include - - -OwnCloudNetworkFactory::OwnCloudNetworkFactory() - : m_url(QString()), m_fixedUrl(QString()), m_forceServerSideUpdate(false), - m_authUsername(QString()), m_authPassword(QString()), m_urlUser(QString()), m_urlStatus(QString()), - m_urlFolders(QString()), m_urlFeeds(QString()), m_urlMessages(QString()), m_urlFeedsUpdate(QString()), - m_urlDeleteFeed(QString()), m_urlRenameFeed(QString()), m_userId(QString()) { -} - -OwnCloudNetworkFactory::~OwnCloudNetworkFactory() { -} - -QString OwnCloudNetworkFactory::url() const { - return m_url; -} - -void OwnCloudNetworkFactory::setUrl(const QString &url) { - m_url = url; - - if (url.endsWith('/')) { - m_fixedUrl = url; - } - else { - m_fixedUrl = url + '/'; - } - - // Store endpoints. - m_urlUser = m_fixedUrl + API_PATH + "user"; - m_urlStatus = m_fixedUrl + API_PATH + "status"; - m_urlFolders = m_fixedUrl + API_PATH + "folders"; - m_urlFeeds = m_fixedUrl + API_PATH + "feeds"; - m_urlMessages = m_fixedUrl + API_PATH + "items?id=%1&batchSize=%2&type=%3"; - m_urlFeedsUpdate = m_fixedUrl + API_PATH + "feeds/update?userId=%1&feedId=%2"; - m_urlDeleteFeed = m_fixedUrl + API_PATH + "feeds/%1"; - m_urlRenameFeed = m_fixedUrl + API_PATH + "feeds/%1/rename"; - - setUserId(QString()); -} - -bool OwnCloudNetworkFactory::forceServerSideUpdate() const { - return m_forceServerSideUpdate; -} - -void OwnCloudNetworkFactory::setForceServerSideUpdate(bool force_update) { - m_forceServerSideUpdate = force_update; -} - -QString OwnCloudNetworkFactory::authUsername() const { - return m_authUsername; -} - -void OwnCloudNetworkFactory::setAuthUsername(const QString &auth_username) { - m_authUsername = auth_username; - - setUserId(QString()); -} - -QString OwnCloudNetworkFactory::authPassword() const { - return m_authPassword; -} - -void OwnCloudNetworkFactory::setAuthPassword(const QString &auth_password) { - m_authPassword = auth_password; - - setUserId(QString()); -} - -QNetworkReply::NetworkError OwnCloudNetworkFactory::lastError() const { - return m_lastError; -} - -OwnCloudUserResponse OwnCloudNetworkFactory::userInfo() { - QByteArray result_raw; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_urlUser, - qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - QByteArray(), QString(), result_raw, - QNetworkAccessManager::GetOperation, - true, m_authUsername, m_authPassword, - true); - OwnCloudUserResponse user_response(QString::fromUtf8(result_raw)); - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("ownCloud: Obtaining user info failed with error %d.", network_reply.first); - } - - m_lastError = network_reply.first; - return user_response; -} - -OwnCloudStatusResponse OwnCloudNetworkFactory::status() { - QByteArray result_raw; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_urlStatus, - qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - QByteArray(), QString(), result_raw, - QNetworkAccessManager::GetOperation, - true, m_authUsername, m_authPassword, - true); - OwnCloudStatusResponse status_response(QString::fromUtf8(result_raw)); - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("ownCloud: Obtaining status info failed with error %d.", network_reply.first); - } - - m_lastError = network_reply.first; - return status_response; -} - -OwnCloudGetFeedsCategoriesResponse OwnCloudNetworkFactory::feedsCategories() { - QByteArray result_raw; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_urlFolders, - qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - QByteArray(), QString(), result_raw, - QNetworkAccessManager::GetOperation, - true, m_authUsername, m_authPassword, - true); - if (network_reply.first != QNetworkReply::NoError) { - qWarning("ownCloud: Obtaining of categories failed with error %d.", network_reply.first); - m_lastError = network_reply.first; - - return OwnCloudGetFeedsCategoriesResponse(); - } - - QString content_categories = QString::fromUtf8(result_raw); - - // Now, obtain feeds. - network_reply = NetworkFactory::performNetworkOperation(m_urlFeeds, - qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - QByteArray(), QString(), result_raw, - QNetworkAccessManager::GetOperation, - true, m_authUsername, m_authPassword, - true); - if (network_reply.first != QNetworkReply::NoError) { - qWarning("ownCloud: Obtaining of feeds failed with error %d.", network_reply.first); - m_lastError = network_reply.first; - return OwnCloudGetFeedsCategoriesResponse(); - } - - QString content_feeds = QString::fromUtf8(result_raw); - m_lastError = network_reply.first; - - return OwnCloudGetFeedsCategoriesResponse(content_categories, content_feeds); -} - -bool OwnCloudNetworkFactory::deleteFeed(int feed_id) { - QString final_url = m_urlDeleteFeed.arg(QString::number(feed_id)); - QByteArray raw_output; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(final_url, - qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - QByteArray(), QString(), - raw_output, QNetworkAccessManager::DeleteOperation, - true, m_authUsername, m_authPassword, true); - - m_lastError = network_reply.first; - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("ownCloud: Obtaining of categories failed with error %d.", network_reply.first); - return false; - } - else { - return true; - } -} - -bool OwnCloudNetworkFactory::createFeed(const QString &url, int parent_id) { - QJsonObject json; - - json["url"] = url; - json["folderId"] = parent_id; - - QByteArray result_raw; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_urlFeeds, - qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - QJsonDocument(json).toJson(QJsonDocument::Compact), - QSL("application/json"), - result_raw, - QNetworkAccessManager::PostOperation, - true, m_authUsername, m_authPassword, true); - - m_lastError = network_reply.first; - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("ownCloud: Creating of category failed with error %d.", network_reply.first); - return false; - } - else { - return true; - } -} - -bool OwnCloudNetworkFactory::renameFeed(const QString &new_name, int feed_id) { - QString final_url = m_urlRenameFeed.arg(QString::number(feed_id)); - QByteArray result_raw; - QJsonObject json; - - json["feedTitle"] = new_name; - - NetworkResult network_reply = NetworkFactory::performNetworkOperation(final_url, - qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - QJsonDocument(json).toJson(QJsonDocument::Compact), - QSL("application/json"), result_raw, - QNetworkAccessManager::PutOperation, - true, m_authUsername, m_authPassword, - true); - m_lastError = network_reply.first; - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("ownCloud: Renaming of feed failed with error %d.", network_reply.first); - return false; - } - else { - return true; - } -} - -OwnCloudGetMessagesResponse OwnCloudNetworkFactory::getMessages(int feed_id) { - if (forceServerSideUpdate()) { - triggerFeedUpdate(feed_id); - } - - QString final_url = m_urlMessages.arg(QString::number(feed_id), - QString::number(-1), - QString::number(0)); - QByteArray result_raw; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(final_url, - qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - QByteArray(), QString(), result_raw, - QNetworkAccessManager::GetOperation, - true, m_authUsername, m_authPassword, - true); - OwnCloudGetMessagesResponse msgs_response(QString::fromUtf8(result_raw)); - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("ownCloud: Obtaining messages failed with error %d.", network_reply.first); - } - - m_lastError = network_reply.first; - return msgs_response; -} - -QNetworkReply::NetworkError OwnCloudNetworkFactory::triggerFeedUpdate(int feed_id) { - if (userId().isEmpty()) { - // We need to get user ID first. - OwnCloudUserResponse info = userInfo(); - - if (lastError() != QNetworkReply::NoError) { - return lastError(); - } - else { - // We have new user ID, set it up. - setUserId(info.userId()); - } - } - - // Now, we can trigger the update. - QByteArray raw_output; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_urlFeedsUpdate.arg(userId(), - QString::number(feed_id)), - qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - QByteArray(), QString(), raw_output, - QNetworkAccessManager::GetOperation, - true, m_authUsername, m_authPassword, - true); - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("ownCloud: Feeds update failed with error %d.", network_reply.first); - } - - return (m_lastError = network_reply.first); -} - -QNetworkReply::NetworkError OwnCloudNetworkFactory::markMessagesRead(RootItem::ReadStatus status, - const QStringList &custom_ids) { - QJsonObject json; - QJsonArray ids; - QByteArray raw_output; - - QString final_url; - - if (status == RootItem::Read) { - final_url = m_fixedUrl + API_PATH + "items/read/multiple"; - } - else { - final_url = m_fixedUrl + API_PATH + "items/unread/multiple"; - } - - foreach (const QString &id, custom_ids) { - ids.append(QJsonValue(id.toInt())); - } - - json["items"] = ids; - - qDebug() << QSL("Raw output for marking msgs read with Nextcloud is : \n\n") << QString::fromUtf8(raw_output); - - NetworkResult network_reply = NetworkFactory::performNetworkOperation(final_url, - qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - QJsonDocument(json).toJson(QJsonDocument::Compact), - QSL("application/json"), - raw_output, - QNetworkAccessManager::PutOperation, - true, m_authUsername, m_authPassword, - true); - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("ownCloud: Marking messages as (un)read failed with error %d.", network_reply.first); - } - - return (m_lastError = network_reply.first); -} - -QNetworkReply::NetworkError OwnCloudNetworkFactory::markMessagesStarred(RootItem::Importance importance, - const QStringList &feed_ids, - const QStringList &guid_hashes) { - QJsonObject json; - QJsonArray ids; - QByteArray raw_output; - - QString final_url; - - if (importance == RootItem::Important) { - final_url = m_fixedUrl + API_PATH + "items/star/multiple"; - } - else { - final_url = m_fixedUrl + API_PATH + "items/unstar/multiple"; - } - - for (int i = 0; i < feed_ids.size(); i++) { - QJsonObject item; - item["feedId"] = feed_ids.at(i); - item["guidHash"] = guid_hashes.at(i); - - ids.append(item); - } - - json["items"] = ids; - - NetworkResult network_reply = NetworkFactory::performNetworkOperation(final_url, - qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - QJsonDocument(json).toJson(QJsonDocument::Compact), - "application/json", - raw_output, - QNetworkAccessManager::PutOperation, - true, m_authUsername, m_authPassword, - true); - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("ownCloud: Marking messages as (un)starred failed with error %d.", network_reply.first); - } - - return (m_lastError = network_reply.first); -} - -QString OwnCloudNetworkFactory::userId() const { - return m_userId; -} - -void OwnCloudNetworkFactory::setUserId(const QString &userId) { - m_userId = userId; -} - -OwnCloudResponse::OwnCloudResponse(const QString &raw_content) { - m_rawContent = QJsonDocument::fromJson(raw_content.toUtf8()).object(); - m_emptyString = raw_content.isEmpty(); -} - -OwnCloudResponse::~OwnCloudResponse() { -} - -bool OwnCloudResponse::isLoaded() const { - return !m_emptyString && !m_rawContent.isEmpty(); -} - -QString OwnCloudResponse::toString() const { - return QJsonDocument(m_rawContent).toJson(QJsonDocument::Compact); -} - -OwnCloudUserResponse::OwnCloudUserResponse(const QString &raw_content) : OwnCloudResponse(raw_content) { -} - -OwnCloudUserResponse::~OwnCloudUserResponse() { -} - -QString OwnCloudUserResponse::displayName() const { - if (isLoaded()) { - return m_rawContent["displayName"].toString(); - } - else { - return QString(); - } -} - -QString OwnCloudUserResponse::userId() const { - if (isLoaded()) { - return m_rawContent["userId"].toString(); - } - else { - return QString(); - } -} - -QDateTime OwnCloudUserResponse::lastLoginTime() const { - if (isLoaded()) { - return QDateTime::fromMSecsSinceEpoch(m_rawContent["lastLoginTimestamp"].toDouble()); - } - else { - return QDateTime(); - } -} - -QIcon OwnCloudUserResponse::avatar() const { - if (isLoaded()) { - QString image_data = m_rawContent["avatar"].toObject()["data"].toString(); - QByteArray decoded_data = QByteArray::fromBase64(image_data.toLocal8Bit()); - QPixmap image; - - if (image.loadFromData(decoded_data)) { - return QIcon(image); - } - } - - return QIcon(); -} - - -OwnCloudStatusResponse::OwnCloudStatusResponse(const QString &raw_content) : OwnCloudResponse(raw_content) { -} - -OwnCloudStatusResponse::~OwnCloudStatusResponse() { -} - -QString OwnCloudStatusResponse::version() const { - if (isLoaded()) { - return m_rawContent["version"].toString(); - } - else { - return QString(); - } -} - -bool OwnCloudStatusResponse::misconfiguredCron() const { - if (isLoaded()) { - return m_rawContent["warnings"].toObject()["improperlyConfiguredCron"].toBool(); - } - else { - return false; - } -} - - -OwnCloudGetFeedsCategoriesResponse::OwnCloudGetFeedsCategoriesResponse(const QString &raw_categories, - const QString &raw_feeds) - : m_contentCategories(raw_categories), m_contentFeeds(raw_feeds) { -} - -OwnCloudGetFeedsCategoriesResponse::~OwnCloudGetFeedsCategoriesResponse() { -} - -RootItem *OwnCloudGetFeedsCategoriesResponse::feedsCategories(bool obtain_icons) const { - RootItem *parent = new RootItem(); - QMap cats; - - cats.insert(0, parent); - - // Process categories first, then process feeds. - foreach (const QJsonValue &cat, QJsonDocument::fromJson(m_contentCategories.toUtf8()).object()["folders"].toArray()) { - QJsonObject item = cat.toObject(); - OwnCloudCategory *category = new OwnCloudCategory(); - - category->setTitle(item["name"].toString()); - category->setCustomId(item["id"].toInt()); - - cats.insert(category->customId(), category); - - // All categories in ownCloud are top-level. - parent->appendChild(category); - } - - // We have categories added, now add all feeds. - foreach (const QJsonValue &fed, QJsonDocument::fromJson(m_contentFeeds.toUtf8()).object()["feeds"].toArray()) { - QJsonObject item = fed.toObject(); - OwnCloudFeed *feed = new OwnCloudFeed(); - - if (obtain_icons) { - QString icon_path = item["faviconLink"].toString(); - - if (!icon_path.isEmpty()) { - QByteArray icon_data; - - if (NetworkFactory::performNetworkOperation(icon_path, DOWNLOAD_TIMEOUT, - QByteArray(), QString(), icon_data, - QNetworkAccessManager::GetOperation).first == QNetworkReply::NoError) { - // Icon downloaded, set it up. - QPixmap icon_pixmap; - icon_pixmap.loadFromData(icon_data); - feed->setIcon(QIcon(icon_pixmap)); - } - } - } - - feed->setUrl(item["link"].toString()); - feed->setTitle(item["title"].toString()); - feed->setCustomId(item["id"].toInt()); - - qDebug("Custom ID of next fetched Nextcloud feed is '%d'.", item["id"].toInt()); - - cats.value(item["folderId"].toInt())->appendChild(feed); - } - - return parent; -} - - -OwnCloudGetMessagesResponse::OwnCloudGetMessagesResponse(const QString &raw_content) : OwnCloudResponse(raw_content) { -} - -OwnCloudGetMessagesResponse::~OwnCloudGetMessagesResponse() { -} - -QList OwnCloudGetMessagesResponse::messages() const { - QList msgs; - - foreach (const QJsonValue &message, m_rawContent["items"].toArray()) { - QJsonObject message_map = message.toObject(); - Message msg; - - msg.m_author = message_map["author"].toString(); - msg.m_contents = message_map["body"].toString(); - msg.m_created = TextFactory::parseDateTime(message_map["pubDate"].toDouble() * 1000); - msg.m_createdFromFeed = true; - msg.m_customId = QString::number(message_map["id"].toInt()); - msg.m_customHash = message_map["guidHash"].toString(); - - QString enclosure_link = message_map["enclosureLink"].toString(); - - if (!enclosure_link.isEmpty()) { - Enclosure enclosure; - - enclosure.m_mimeType = message_map["enclosureMime"].toString(); - enclosure.m_url = enclosure_link; - - msg.m_enclosures.append(enclosure); - } - - msg.m_feedId = QString::number(message_map["feedId"].toInt()); - msg.m_isImportant = message_map["starred"].toBool(); - msg.m_isRead = !message_map["unread"].toBool(); - msg.m_title = message_map["title"].toString(); - msg.m_url = message_map["url"].toString(); - - msgs.append(msg); - } - - return msgs; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/owncloud/network/owncloudnetworkfactory.h" + +#include "services/owncloud/definitions.h" +#include "network-web/networkfactory.h" +#include "miscellaneous/application.h" +#include "miscellaneous/settings.h" +#include "miscellaneous/textfactory.h" +#include "services/abstract/rootitem.h" +#include "services/owncloud/owncloudcategory.h" +#include "services/owncloud/owncloudfeed.h" + +#include +#include +#include +#include + + +OwnCloudNetworkFactory::OwnCloudNetworkFactory() + : m_url(QString()), m_fixedUrl(QString()), m_forceServerSideUpdate(false), + m_authUsername(QString()), m_authPassword(QString()), m_urlUser(QString()), m_urlStatus(QString()), + m_urlFolders(QString()), m_urlFeeds(QString()), m_urlMessages(QString()), m_urlFeedsUpdate(QString()), + m_urlDeleteFeed(QString()), m_urlRenameFeed(QString()), m_userId(QString()) { +} + +OwnCloudNetworkFactory::~OwnCloudNetworkFactory() { +} + +QString OwnCloudNetworkFactory::url() const { + return m_url; +} + +void OwnCloudNetworkFactory::setUrl(const QString& url) { + m_url = url; + + if (url.endsWith('/')) { + m_fixedUrl = url; + } + + else { + m_fixedUrl = url + '/'; + } + + // Store endpoints. + m_urlUser = m_fixedUrl + API_PATH + "user"; + m_urlStatus = m_fixedUrl + API_PATH + "status"; + m_urlFolders = m_fixedUrl + API_PATH + "folders"; + m_urlFeeds = m_fixedUrl + API_PATH + "feeds"; + m_urlMessages = m_fixedUrl + API_PATH + "items?id=%1&batchSize=%2&type=%3"; + m_urlFeedsUpdate = m_fixedUrl + API_PATH + "feeds/update?userId=%1&feedId=%2"; + m_urlDeleteFeed = m_fixedUrl + API_PATH + "feeds/%1"; + m_urlRenameFeed = m_fixedUrl + API_PATH + "feeds/%1/rename"; + setUserId(QString()); +} + +bool OwnCloudNetworkFactory::forceServerSideUpdate() const { + return m_forceServerSideUpdate; +} + +void OwnCloudNetworkFactory::setForceServerSideUpdate(bool force_update) { + m_forceServerSideUpdate = force_update; +} + +QString OwnCloudNetworkFactory::authUsername() const { + return m_authUsername; +} + +void OwnCloudNetworkFactory::setAuthUsername(const QString& auth_username) { + m_authUsername = auth_username; + setUserId(QString()); +} + +QString OwnCloudNetworkFactory::authPassword() const { + return m_authPassword; +} + +void OwnCloudNetworkFactory::setAuthPassword(const QString& auth_password) { + m_authPassword = auth_password; + setUserId(QString()); +} + +QNetworkReply::NetworkError OwnCloudNetworkFactory::lastError() const { + return m_lastError; +} + +OwnCloudUserResponse OwnCloudNetworkFactory::userInfo() { + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_urlUser, + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + QByteArray(), QString(), result_raw, + QNetworkAccessManager::GetOperation, + true, m_authUsername, m_authPassword, + true); + OwnCloudUserResponse user_response(QString::fromUtf8(result_raw)); + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("ownCloud: Obtaining user info failed with error %d.", network_reply.first); + } + + m_lastError = network_reply.first; + return user_response; +} + +OwnCloudStatusResponse OwnCloudNetworkFactory::status() { + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_urlStatus, + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + QByteArray(), QString(), result_raw, + QNetworkAccessManager::GetOperation, + true, m_authUsername, m_authPassword, + true); + OwnCloudStatusResponse status_response(QString::fromUtf8(result_raw)); + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("ownCloud: Obtaining status info failed with error %d.", network_reply.first); + } + + m_lastError = network_reply.first; + return status_response; +} + +OwnCloudGetFeedsCategoriesResponse OwnCloudNetworkFactory::feedsCategories() { + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_urlFolders, + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + QByteArray(), QString(), result_raw, + QNetworkAccessManager::GetOperation, + true, m_authUsername, m_authPassword, + true); + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("ownCloud: Obtaining of categories failed with error %d.", network_reply.first); + m_lastError = network_reply.first; + return OwnCloudGetFeedsCategoriesResponse(); + } + + QString content_categories = QString::fromUtf8(result_raw); + // Now, obtain feeds. + network_reply = NetworkFactory::performNetworkOperation(m_urlFeeds, + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + QByteArray(), QString(), result_raw, + QNetworkAccessManager::GetOperation, + true, m_authUsername, m_authPassword, + true); + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("ownCloud: Obtaining of feeds failed with error %d.", network_reply.first); + m_lastError = network_reply.first; + return OwnCloudGetFeedsCategoriesResponse(); + } + + QString content_feeds = QString::fromUtf8(result_raw); + m_lastError = network_reply.first; + return OwnCloudGetFeedsCategoriesResponse(content_categories, content_feeds); +} + +bool OwnCloudNetworkFactory::deleteFeed(int feed_id) { + QString final_url = m_urlDeleteFeed.arg(QString::number(feed_id)); + QByteArray raw_output; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(final_url, + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + QByteArray(), QString(), + raw_output, QNetworkAccessManager::DeleteOperation, + true, m_authUsername, m_authPassword, true); + m_lastError = network_reply.first; + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("ownCloud: Obtaining of categories failed with error %d.", network_reply.first); + return false; + } + + else { + return true; + } +} + +bool OwnCloudNetworkFactory::createFeed(const QString& url, int parent_id) { + QJsonObject json; + json["url"] = url; + json["folderId"] = parent_id; + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_urlFeeds, + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + QJsonDocument(json).toJson(QJsonDocument::Compact), + QSL("application/json"), + result_raw, + QNetworkAccessManager::PostOperation, + true, m_authUsername, m_authPassword, true); + m_lastError = network_reply.first; + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("ownCloud: Creating of category failed with error %d.", network_reply.first); + return false; + } + + else { + return true; + } +} + +bool OwnCloudNetworkFactory::renameFeed(const QString& new_name, int feed_id) { + QString final_url = m_urlRenameFeed.arg(QString::number(feed_id)); + QByteArray result_raw; + QJsonObject json; + json["feedTitle"] = new_name; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(final_url, + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + QJsonDocument(json).toJson(QJsonDocument::Compact), + QSL("application/json"), result_raw, + QNetworkAccessManager::PutOperation, + true, m_authUsername, m_authPassword, + true); + m_lastError = network_reply.first; + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("ownCloud: Renaming of feed failed with error %d.", network_reply.first); + return false; + } + + else { + return true; + } +} + +OwnCloudGetMessagesResponse OwnCloudNetworkFactory::getMessages(int feed_id) { + if (forceServerSideUpdate()) { + triggerFeedUpdate(feed_id); + } + + QString final_url = m_urlMessages.arg(QString::number(feed_id), + QString::number(-1), + QString::number(0)); + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(final_url, + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + QByteArray(), QString(), result_raw, + QNetworkAccessManager::GetOperation, + true, m_authUsername, m_authPassword, + true); + OwnCloudGetMessagesResponse msgs_response(QString::fromUtf8(result_raw)); + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("ownCloud: Obtaining messages failed with error %d.", network_reply.first); + } + + m_lastError = network_reply.first; + return msgs_response; +} + +QNetworkReply::NetworkError OwnCloudNetworkFactory::triggerFeedUpdate(int feed_id) { + if (userId().isEmpty()) { + // We need to get user ID first. + OwnCloudUserResponse info = userInfo(); + + if (lastError() != QNetworkReply::NoError) { + return lastError(); + } + + else { + // We have new user ID, set it up. + setUserId(info.userId()); + } + } + + // Now, we can trigger the update. + QByteArray raw_output; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_urlFeedsUpdate.arg(userId(), + QString::number(feed_id)), + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + QByteArray(), QString(), raw_output, + QNetworkAccessManager::GetOperation, + true, m_authUsername, m_authPassword, + true); + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("ownCloud: Feeds update failed with error %d.", network_reply.first); + } + + return (m_lastError = network_reply.first); +} + +QNetworkReply::NetworkError OwnCloudNetworkFactory::markMessagesRead(RootItem::ReadStatus status, + const QStringList& custom_ids) { + QJsonObject json; + QJsonArray ids; + QByteArray raw_output; + QString final_url; + + if (status == RootItem::Read) { + final_url = m_fixedUrl + API_PATH + "items/read/multiple"; + } + + else { + final_url = m_fixedUrl + API_PATH + "items/unread/multiple"; + } + + foreach (const QString& id, custom_ids) { + ids.append(QJsonValue(id.toInt())); + } + + json["items"] = ids; + qDebug() << QSL("Raw output for marking msgs read with Nextcloud is : \n\n") << QString::fromUtf8(raw_output); + NetworkResult network_reply = NetworkFactory::performNetworkOperation(final_url, + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + QJsonDocument(json).toJson(QJsonDocument::Compact), + QSL("application/json"), + raw_output, + QNetworkAccessManager::PutOperation, + true, m_authUsername, m_authPassword, + true); + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("ownCloud: Marking messages as (un)read failed with error %d.", network_reply.first); + } + + return (m_lastError = network_reply.first); +} + +QNetworkReply::NetworkError OwnCloudNetworkFactory::markMessagesStarred(RootItem::Importance importance, + const QStringList& feed_ids, + const QStringList& guid_hashes) { + QJsonObject json; + QJsonArray ids; + QByteArray raw_output; + QString final_url; + + if (importance == RootItem::Important) { + final_url = m_fixedUrl + API_PATH + "items/star/multiple"; + } + + else { + final_url = m_fixedUrl + API_PATH + "items/unstar/multiple"; + } + + for (int i = 0; i < feed_ids.size(); i++) { + QJsonObject item; + item["feedId"] = feed_ids.at(i); + item["guidHash"] = guid_hashes.at(i); + ids.append(item); + } + + json["items"] = ids; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(final_url, + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + QJsonDocument(json).toJson(QJsonDocument::Compact), + "application/json", + raw_output, + QNetworkAccessManager::PutOperation, + true, m_authUsername, m_authPassword, + true); + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("ownCloud: Marking messages as (un)starred failed with error %d.", network_reply.first); + } + + return (m_lastError = network_reply.first); +} + +QString OwnCloudNetworkFactory::userId() const { + return m_userId; +} + +void OwnCloudNetworkFactory::setUserId(const QString& userId) { + m_userId = userId; +} + +OwnCloudResponse::OwnCloudResponse(const QString& raw_content) { + m_rawContent = QJsonDocument::fromJson(raw_content.toUtf8()).object(); + m_emptyString = raw_content.isEmpty(); +} + +OwnCloudResponse::~OwnCloudResponse() { +} + +bool OwnCloudResponse::isLoaded() const { + return !m_emptyString && !m_rawContent.isEmpty(); +} + +QString OwnCloudResponse::toString() const { + return QJsonDocument(m_rawContent).toJson(QJsonDocument::Compact); +} + +OwnCloudUserResponse::OwnCloudUserResponse(const QString& raw_content) : OwnCloudResponse(raw_content) { +} + +OwnCloudUserResponse::~OwnCloudUserResponse() { +} + +QString OwnCloudUserResponse::displayName() const { + if (isLoaded()) { + return m_rawContent["displayName"].toString(); + } + + else { + return QString(); + } +} + +QString OwnCloudUserResponse::userId() const { + if (isLoaded()) { + return m_rawContent["userId"].toString(); + } + + else { + return QString(); + } +} + +QDateTime OwnCloudUserResponse::lastLoginTime() const { + if (isLoaded()) { + return QDateTime::fromMSecsSinceEpoch(m_rawContent["lastLoginTimestamp"].toDouble()); + } + + else { + return QDateTime(); + } +} + +QIcon OwnCloudUserResponse::avatar() const { + if (isLoaded()) { + QString image_data = m_rawContent["avatar"].toObject()["data"].toString(); + QByteArray decoded_data = QByteArray::fromBase64(image_data.toLocal8Bit()); + QPixmap image; + + if (image.loadFromData(decoded_data)) { + return QIcon(image); + } + } + + return QIcon(); +} + + +OwnCloudStatusResponse::OwnCloudStatusResponse(const QString& raw_content) : OwnCloudResponse(raw_content) { +} + +OwnCloudStatusResponse::~OwnCloudStatusResponse() { +} + +QString OwnCloudStatusResponse::version() const { + if (isLoaded()) { + return m_rawContent["version"].toString(); + } + + else { + return QString(); + } +} + +bool OwnCloudStatusResponse::misconfiguredCron() const { + if (isLoaded()) { + return m_rawContent["warnings"].toObject()["improperlyConfiguredCron"].toBool(); + } + + else { + return false; + } +} + + +OwnCloudGetFeedsCategoriesResponse::OwnCloudGetFeedsCategoriesResponse(const QString& raw_categories, + const QString& raw_feeds) + : m_contentCategories(raw_categories), m_contentFeeds(raw_feeds) { +} + +OwnCloudGetFeedsCategoriesResponse::~OwnCloudGetFeedsCategoriesResponse() { +} + +RootItem* OwnCloudGetFeedsCategoriesResponse::feedsCategories(bool obtain_icons) const { + RootItem* parent = new RootItem(); + QMap cats; + cats.insert(0, parent); + + // Process categories first, then process feeds. + foreach (const QJsonValue& cat, QJsonDocument::fromJson(m_contentCategories.toUtf8()).object()["folders"].toArray()) { + QJsonObject item = cat.toObject(); + OwnCloudCategory* category = new OwnCloudCategory(); + category->setTitle(item["name"].toString()); + category->setCustomId(item["id"].toInt()); + cats.insert(category->customId(), category); + // All categories in ownCloud are top-level. + parent->appendChild(category); + } + + // We have categories added, now add all feeds. + foreach (const QJsonValue& fed, QJsonDocument::fromJson(m_contentFeeds.toUtf8()).object()["feeds"].toArray()) { + QJsonObject item = fed.toObject(); + OwnCloudFeed* feed = new OwnCloudFeed(); + + if (obtain_icons) { + QString icon_path = item["faviconLink"].toString(); + + if (!icon_path.isEmpty()) { + QByteArray icon_data; + + if (NetworkFactory::performNetworkOperation(icon_path, DOWNLOAD_TIMEOUT, + QByteArray(), QString(), icon_data, + QNetworkAccessManager::GetOperation).first == QNetworkReply::NoError) { + // Icon downloaded, set it up. + QPixmap icon_pixmap; + icon_pixmap.loadFromData(icon_data); + feed->setIcon(QIcon(icon_pixmap)); + } + } + } + + feed->setUrl(item["link"].toString()); + feed->setTitle(item["title"].toString()); + feed->setCustomId(item["id"].toInt()); + qDebug("Custom ID of next fetched Nextcloud feed is '%d'.", item["id"].toInt()); + cats.value(item["folderId"].toInt())->appendChild(feed); + } + + return parent; +} + + +OwnCloudGetMessagesResponse::OwnCloudGetMessagesResponse(const QString& raw_content) : OwnCloudResponse(raw_content) { +} + +OwnCloudGetMessagesResponse::~OwnCloudGetMessagesResponse() { +} + +QList OwnCloudGetMessagesResponse::messages() const { + QList msgs; + + foreach (const QJsonValue& message, m_rawContent["items"].toArray()) { + QJsonObject message_map = message.toObject(); + Message msg; + msg.m_author = message_map["author"].toString(); + msg.m_contents = message_map["body"].toString(); + msg.m_created = TextFactory::parseDateTime(message_map["pubDate"].toDouble() * 1000); + msg.m_createdFromFeed = true; + msg.m_customId = QString::number(message_map["id"].toInt()); + msg.m_customHash = message_map["guidHash"].toString(); + QString enclosure_link = message_map["enclosureLink"].toString(); + + if (!enclosure_link.isEmpty()) { + Enclosure enclosure; + enclosure.m_mimeType = message_map["enclosureMime"].toString(); + enclosure.m_url = enclosure_link; + msg.m_enclosures.append(enclosure); + } + + msg.m_feedId = QString::number(message_map["feedId"].toInt()); + msg.m_isImportant = message_map["starred"].toBool(); + msg.m_isRead = !message_map["unread"].toBool(); + msg.m_title = message_map["title"].toString(); + msg.m_url = message_map["url"].toString(); + msgs.append(msg); + } + + return msgs; +} diff --git a/src/services/owncloud/network/owncloudnetworkfactory.h b/src/services/owncloud/network/owncloudnetworkfactory.h index 5167d70b2..84305af36 100755 --- a/src/services/owncloud/network/owncloudnetworkfactory.h +++ b/src/services/owncloud/network/owncloudnetworkfactory.h @@ -1,157 +1,157 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef OWNCLOUDNETWORKFACTORY_H -#define OWNCLOUDNETWORKFACTORY_H - -#include "core/message.h" -#include "services/abstract/rootitem.h" - -#include -#include -#include -#include -#include - - -class OwnCloudResponse { - public: - explicit OwnCloudResponse(const QString &raw_content = QString()); - virtual ~OwnCloudResponse(); - - bool isLoaded() const; - QString toString() const; - - protected: - QJsonObject m_rawContent; - bool m_emptyString; -}; - -class OwnCloudUserResponse : public OwnCloudResponse { - public: - explicit OwnCloudUserResponse(const QString &raw_content = QString()); - virtual ~OwnCloudUserResponse(); - - QString userId() const; - QString displayName() const; - QDateTime lastLoginTime() const; - QIcon avatar() const; -}; - -class OwnCloudGetMessagesResponse : public OwnCloudResponse { - public: - explicit OwnCloudGetMessagesResponse(const QString &raw_content = QString()); - virtual ~OwnCloudGetMessagesResponse(); - - QList messages() const; -}; - -class OwnCloudStatusResponse : public OwnCloudResponse { - public: - explicit OwnCloudStatusResponse(const QString &raw_content = QString()); - virtual ~OwnCloudStatusResponse(); - - QString version() const; - bool misconfiguredCron() const; -}; - -class RootItem; - -class OwnCloudGetFeedsCategoriesResponse { - public: - explicit OwnCloudGetFeedsCategoriesResponse(const QString &raw_categories = QString(), - const QString &raw_feeds = QString()); - virtual ~OwnCloudGetFeedsCategoriesResponse(); - - // Returns tree of feeds/categories. - // Top-level root of the tree is not needed here. - // Returned items do not have primary IDs assigned. - RootItem *feedsCategories(bool obtain_icons) const; - - private: - QString m_contentCategories; - QString m_contentFeeds; -}; - -class OwnCloudNetworkFactory { - public: - explicit OwnCloudNetworkFactory(); - virtual ~OwnCloudNetworkFactory(); - - QString url() const; - void setUrl(const QString &url); - - bool forceServerSideUpdate() const; - void setForceServerSideUpdate(bool force_update); - - QString authUsername() const; - void setAuthUsername(const QString &auth_username); - - QString authPassword() const; - void setAuthPassword(const QString &auth_password); - - QString userId() const; - void setUserId(const QString &userId); - - QNetworkReply::NetworkError lastError() const; - - // Operations. - - // Get user info. - OwnCloudUserResponse userInfo(); - - // Get version info. - OwnCloudStatusResponse status(); - - // Get feeds & categories (used for sync-in). - OwnCloudGetFeedsCategoriesResponse feedsCategories(); - - // Feed operations. - bool deleteFeed(int feed_id); - bool createFeed(const QString &url, int parent_id); - bool renameFeed(const QString &new_name, int feed_id); - - // Get messages for given feed. - OwnCloudGetMessagesResponse getMessages(int feed_id); - - // Misc methods. - QNetworkReply::NetworkError triggerFeedUpdate(int feed_id); - QNetworkReply::NetworkError markMessagesRead(RootItem::ReadStatus status, const QStringList &custom_ids); - QNetworkReply::NetworkError markMessagesStarred(RootItem::Importance importance, const QStringList &feed_ids, - const QStringList &guid_hashes); - - private: - QString m_url; - QString m_fixedUrl; - bool m_forceServerSideUpdate; - QString m_authUsername; - QString m_authPassword; - QNetworkReply::NetworkError m_lastError; - - // Endpoints. - QString m_urlUser; - QString m_urlStatus; - QString m_urlFolders; - QString m_urlFeeds; - QString m_urlMessages; - QString m_urlFeedsUpdate; - QString m_urlDeleteFeed; - QString m_urlRenameFeed; - QString m_userId; -}; - -#endif // OWNCLOUDNETWORKFACTORY_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef OWNCLOUDNETWORKFACTORY_H +#define OWNCLOUDNETWORKFACTORY_H + +#include "core/message.h" +#include "services/abstract/rootitem.h" + +#include +#include +#include +#include +#include + + +class OwnCloudResponse { + public: + explicit OwnCloudResponse(const QString& raw_content = QString()); + virtual ~OwnCloudResponse(); + + bool isLoaded() const; + QString toString() const; + + protected: + QJsonObject m_rawContent; + bool m_emptyString; +}; + +class OwnCloudUserResponse : public OwnCloudResponse { + public: + explicit OwnCloudUserResponse(const QString& raw_content = QString()); + virtual ~OwnCloudUserResponse(); + + QString userId() const; + QString displayName() const; + QDateTime lastLoginTime() const; + QIcon avatar() const; +}; + +class OwnCloudGetMessagesResponse : public OwnCloudResponse { + public: + explicit OwnCloudGetMessagesResponse(const QString& raw_content = QString()); + virtual ~OwnCloudGetMessagesResponse(); + + QList messages() const; +}; + +class OwnCloudStatusResponse : public OwnCloudResponse { + public: + explicit OwnCloudStatusResponse(const QString& raw_content = QString()); + virtual ~OwnCloudStatusResponse(); + + QString version() const; + bool misconfiguredCron() const; +}; + +class RootItem; + +class OwnCloudGetFeedsCategoriesResponse { + public: + explicit OwnCloudGetFeedsCategoriesResponse(const QString& raw_categories = QString(), + const QString& raw_feeds = QString()); + virtual ~OwnCloudGetFeedsCategoriesResponse(); + + // Returns tree of feeds/categories. + // Top-level root of the tree is not needed here. + // Returned items do not have primary IDs assigned. + RootItem* feedsCategories(bool obtain_icons) const; + + private: + QString m_contentCategories; + QString m_contentFeeds; +}; + +class OwnCloudNetworkFactory { + public: + explicit OwnCloudNetworkFactory(); + virtual ~OwnCloudNetworkFactory(); + + QString url() const; + void setUrl(const QString& url); + + bool forceServerSideUpdate() const; + void setForceServerSideUpdate(bool force_update); + + QString authUsername() const; + void setAuthUsername(const QString& auth_username); + + QString authPassword() const; + void setAuthPassword(const QString& auth_password); + + QString userId() const; + void setUserId(const QString& userId); + + QNetworkReply::NetworkError lastError() const; + + // Operations. + + // Get user info. + OwnCloudUserResponse userInfo(); + + // Get version info. + OwnCloudStatusResponse status(); + + // Get feeds & categories (used for sync-in). + OwnCloudGetFeedsCategoriesResponse feedsCategories(); + + // Feed operations. + bool deleteFeed(int feed_id); + bool createFeed(const QString& url, int parent_id); + bool renameFeed(const QString& new_name, int feed_id); + + // Get messages for given feed. + OwnCloudGetMessagesResponse getMessages(int feed_id); + + // Misc methods. + QNetworkReply::NetworkError triggerFeedUpdate(int feed_id); + QNetworkReply::NetworkError markMessagesRead(RootItem::ReadStatus status, const QStringList& custom_ids); + QNetworkReply::NetworkError markMessagesStarred(RootItem::Importance importance, const QStringList& feed_ids, + const QStringList& guid_hashes); + + private: + QString m_url; + QString m_fixedUrl; + bool m_forceServerSideUpdate; + QString m_authUsername; + QString m_authPassword; + QNetworkReply::NetworkError m_lastError; + + // Endpoints. + QString m_urlUser; + QString m_urlStatus; + QString m_urlFolders; + QString m_urlFeeds; + QString m_urlMessages; + QString m_urlFeedsUpdate; + QString m_urlDeleteFeed; + QString m_urlRenameFeed; + QString m_userId; +}; + +#endif // OWNCLOUDNETWORKFACTORY_H diff --git a/src/services/owncloud/owncloudcategory.cpp b/src/services/owncloud/owncloudcategory.cpp index f6196e391..64695447b 100755 --- a/src/services/owncloud/owncloudcategory.cpp +++ b/src/services/owncloud/owncloudcategory.cpp @@ -1,47 +1,47 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/owncloud/owncloudcategory.h" - -#include "services/owncloud/owncloudserviceroot.h" -#include "miscellaneous/application.h" -#include "miscellaneous/iconfactory.h" - - -OwnCloudCategory::OwnCloudCategory(RootItem *parent) : Category(parent) { - // Categories in ownCloud have now icons etc. They just have titles. - setIcon(qApp->icons()->fromTheme(QSL("folder"))); -} - -OwnCloudCategory::OwnCloudCategory(const QSqlRecord &record) : Category(nullptr) { - setIcon(qApp->icons()->fromTheme(QSL("folder"))); - setId(record.value(CAT_DB_ID_INDEX).toInt()); - setTitle(record.value(CAT_DB_TITLE_INDEX).toString()); - setCustomId(record.value(CAT_DB_CUSTOM_ID_INDEX).toInt()); -} - -OwnCloudServiceRoot *OwnCloudCategory::serviceRoot() const { - return qobject_cast(getParentServiceRoot()); -} - -bool OwnCloudCategory::markAsReadUnread(RootItem::ReadStatus status) { - serviceRoot()->addMessageStatesToCache(getParentServiceRoot()->customIDSOfMessagesForItem(this), status); - return serviceRoot()->markFeedsReadUnread(getSubTreeFeeds(), status); -} - -OwnCloudCategory::~OwnCloudCategory() { -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/owncloud/owncloudcategory.h" + +#include "services/owncloud/owncloudserviceroot.h" +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" + + +OwnCloudCategory::OwnCloudCategory(RootItem* parent) : Category(parent) { + // Categories in ownCloud have now icons etc. They just have titles. + setIcon(qApp->icons()->fromTheme(QSL("folder"))); +} + +OwnCloudCategory::OwnCloudCategory(const QSqlRecord& record) : Category(nullptr) { + setIcon(qApp->icons()->fromTheme(QSL("folder"))); + setId(record.value(CAT_DB_ID_INDEX).toInt()); + setTitle(record.value(CAT_DB_TITLE_INDEX).toString()); + setCustomId(record.value(CAT_DB_CUSTOM_ID_INDEX).toInt()); +} + +OwnCloudServiceRoot* OwnCloudCategory::serviceRoot() const { + return qobject_cast(getParentServiceRoot()); +} + +bool OwnCloudCategory::markAsReadUnread(RootItem::ReadStatus status) { + serviceRoot()->addMessageStatesToCache(getParentServiceRoot()->customIDSOfMessagesForItem(this), status); + return serviceRoot()->markFeedsReadUnread(getSubTreeFeeds(), status); +} + +OwnCloudCategory::~OwnCloudCategory() { +} diff --git a/src/services/owncloud/owncloudcategory.h b/src/services/owncloud/owncloudcategory.h index 87dcd8cb6..bc086980e 100755 --- a/src/services/owncloud/owncloudcategory.h +++ b/src/services/owncloud/owncloudcategory.h @@ -1,40 +1,40 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef OWNCLOUDSERVICECATEGORY_H -#define OWNCLOUDSERVICECATEGORY_H - -#include "services/abstract/category.h" - - -class OwnCloudServiceRoot; - -class OwnCloudCategory : public Category { - Q_OBJECT - - public: - explicit OwnCloudCategory(RootItem *parent = nullptr); - explicit OwnCloudCategory(const QSqlRecord &record); - virtual ~OwnCloudCategory(); - - bool markAsReadUnread(ReadStatus status); - - private: - OwnCloudServiceRoot *serviceRoot() const; -}; - -#endif // OWNCLOUDSERVICECATEGORY_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef OWNCLOUDSERVICECATEGORY_H +#define OWNCLOUDSERVICECATEGORY_H + +#include "services/abstract/category.h" + + +class OwnCloudServiceRoot; + +class OwnCloudCategory : public Category { + Q_OBJECT + + public: + explicit OwnCloudCategory(RootItem* parent = nullptr); + explicit OwnCloudCategory(const QSqlRecord& record); + virtual ~OwnCloudCategory(); + + bool markAsReadUnread(ReadStatus status); + + private: + OwnCloudServiceRoot* serviceRoot() const; +}; + +#endif // OWNCLOUDSERVICECATEGORY_H diff --git a/src/services/owncloud/owncloudfeed.cpp b/src/services/owncloud/owncloudfeed.cpp index aa23773e8..9e290dabe 100755 --- a/src/services/owncloud/owncloudfeed.cpp +++ b/src/services/owncloud/owncloudfeed.cpp @@ -1,121 +1,121 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/owncloud/owncloudfeed.h" - -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/databasequeries.h" -#include "services/owncloud/owncloudserviceroot.h" -#include "services/owncloud/network/owncloudnetworkfactory.h" -#include "services/owncloud/gui/formowncloudfeeddetails.h" - -#include - - -OwnCloudFeed::OwnCloudFeed(RootItem *parent) : Feed(parent) { -} - -OwnCloudFeed::OwnCloudFeed(const QSqlRecord &record) : Feed(nullptr) { - setTitle(record.value(FDS_DB_TITLE_INDEX).toString()); - setId(record.value(FDS_DB_ID_INDEX).toInt()); - setIcon(qApp->icons()->fromByteArray(record.value(FDS_DB_ICON_INDEX).toByteArray())); - setAutoUpdateType(static_cast(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt())); - setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt()); - setCustomId(record.value(FDS_DB_CUSTOM_ID_INDEX).toInt()); - - qDebug("Custom ID of Nextcloud feed when loading from DB is '%s'.", qPrintable(record.value(FDS_DB_CUSTOM_ID_INDEX).toString())); -} - -OwnCloudFeed::~OwnCloudFeed() { -} - -bool OwnCloudFeed::canBeEdited() const { - return true; -} - -bool OwnCloudFeed::editViaGui() { - QPointer form_pointer = new FormOwnCloudFeedDetails(serviceRoot(), qApp->mainFormWidget()); - - form_pointer.data()->addEditFeed(this, nullptr); - delete form_pointer.data(); - return false; -} - -bool OwnCloudFeed::canBeDeleted() const { - return true; -} - -bool OwnCloudFeed::deleteViaGui() { - if (serviceRoot()->network()->deleteFeed(customId()) && removeItself()) { - serviceRoot()->requestItemRemoval(this); - return true; - } - else { - return false; - } -} - -bool OwnCloudFeed::editItself(OwnCloudFeed *new_feed_data) { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - if (!DatabaseQueries::editBaseFeed(database, id(), new_feed_data->autoUpdateType(), - new_feed_data->autoUpdateInitialInterval())) { - // Persistent storage update failed, no way to continue now. - return false; - } - else { - setAutoUpdateType(new_feed_data->autoUpdateType()); - setAutoUpdateInitialInterval(new_feed_data->autoUpdateInitialInterval()); - return true; - } -} - -bool OwnCloudFeed::removeItself() { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - return DatabaseQueries::deleteFeed(database, customId(), serviceRoot()->accountId()); -} - -bool OwnCloudFeed::markAsReadUnread(RootItem::ReadStatus status) { - serviceRoot()->addMessageStatesToCache(getParentServiceRoot()->customIDSOfMessagesForItem(this), status); - return getParentServiceRoot()->markFeedsReadUnread(QList() << this, status); -} - -bool OwnCloudFeed::cleanMessages(bool clear_only_read) { - return getParentServiceRoot()->cleanFeeds(QList() << this, clear_only_read); -} - -OwnCloudServiceRoot *OwnCloudFeed::serviceRoot() const { - return qobject_cast(getParentServiceRoot()); -} - -QList OwnCloudFeed::obtainNewMessages(bool *error_during_obtaining) { - OwnCloudGetMessagesResponse messages = serviceRoot()->network()->getMessages(customId()); - - if (serviceRoot()->network()->lastError() != QNetworkReply::NoError) { - setStatus(Feed::NetworkError); - *error_during_obtaining = true; - serviceRoot()->itemChanged(QList() << this); - return QList(); - } - else { - *error_during_obtaining = false; - return messages.messages(); - } - - return QList(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/owncloud/owncloudfeed.h" + +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/databasequeries.h" +#include "services/owncloud/owncloudserviceroot.h" +#include "services/owncloud/network/owncloudnetworkfactory.h" +#include "services/owncloud/gui/formowncloudfeeddetails.h" + +#include + + +OwnCloudFeed::OwnCloudFeed(RootItem* parent) : Feed(parent) { +} + +OwnCloudFeed::OwnCloudFeed(const QSqlRecord& record) : Feed(nullptr) { + setTitle(record.value(FDS_DB_TITLE_INDEX).toString()); + setId(record.value(FDS_DB_ID_INDEX).toInt()); + setIcon(qApp->icons()->fromByteArray(record.value(FDS_DB_ICON_INDEX).toByteArray())); + setAutoUpdateType(static_cast(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt())); + setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt()); + setCustomId(record.value(FDS_DB_CUSTOM_ID_INDEX).toInt()); + qDebug("Custom ID of Nextcloud feed when loading from DB is '%s'.", qPrintable(record.value(FDS_DB_CUSTOM_ID_INDEX).toString())); +} + +OwnCloudFeed::~OwnCloudFeed() { +} + +bool OwnCloudFeed::canBeEdited() const { + return true; +} + +bool OwnCloudFeed::editViaGui() { + QPointer form_pointer = new FormOwnCloudFeedDetails(serviceRoot(), qApp->mainFormWidget()); + form_pointer.data()->addEditFeed(this, nullptr); + delete form_pointer.data(); + return false; +} + +bool OwnCloudFeed::canBeDeleted() const { + return true; +} + +bool OwnCloudFeed::deleteViaGui() { + if (serviceRoot()->network()->deleteFeed(customId()) && removeItself()) { + serviceRoot()->requestItemRemoval(this); + return true; + } + + else { + return false; + } +} + +bool OwnCloudFeed::editItself(OwnCloudFeed* new_feed_data) { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + + if (!DatabaseQueries::editBaseFeed(database, id(), new_feed_data->autoUpdateType(), + new_feed_data->autoUpdateInitialInterval())) { + // Persistent storage update failed, no way to continue now. + return false; + } + + else { + setAutoUpdateType(new_feed_data->autoUpdateType()); + setAutoUpdateInitialInterval(new_feed_data->autoUpdateInitialInterval()); + return true; + } +} + +bool OwnCloudFeed::removeItself() { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + return DatabaseQueries::deleteFeed(database, customId(), serviceRoot()->accountId()); +} + +bool OwnCloudFeed::markAsReadUnread(RootItem::ReadStatus status) { + serviceRoot()->addMessageStatesToCache(getParentServiceRoot()->customIDSOfMessagesForItem(this), status); + return getParentServiceRoot()->markFeedsReadUnread(QList() << this, status); +} + +bool OwnCloudFeed::cleanMessages(bool clear_only_read) { + return getParentServiceRoot()->cleanFeeds(QList() << this, clear_only_read); +} + +OwnCloudServiceRoot* OwnCloudFeed::serviceRoot() const { + return qobject_cast(getParentServiceRoot()); +} + +QList OwnCloudFeed::obtainNewMessages(bool* error_during_obtaining) { + OwnCloudGetMessagesResponse messages = serviceRoot()->network()->getMessages(customId()); + + if (serviceRoot()->network()->lastError() != QNetworkReply::NoError) { + setStatus(Feed::NetworkError); + *error_during_obtaining = true; + serviceRoot()->itemChanged(QList() << this); + return QList(); + } + + else { + *error_during_obtaining = false; + return messages.messages(); + } + + return QList(); +} diff --git a/src/services/owncloud/owncloudfeed.h b/src/services/owncloud/owncloudfeed.h index 9204e4ba3..601a031ab 100755 --- a/src/services/owncloud/owncloudfeed.h +++ b/src/services/owncloud/owncloudfeed.h @@ -1,51 +1,51 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef OWNCLOUDFEED_H -#define OWNCLOUDFEED_H - -#include "services/abstract/feed.h" - - -class OwnCloudServiceRoot; - -class OwnCloudFeed : public Feed { - Q_OBJECT - - public: - explicit OwnCloudFeed(RootItem *parent = nullptr); - explicit OwnCloudFeed(const QSqlRecord &record); - virtual ~OwnCloudFeed(); - - bool canBeEdited() const; - bool editViaGui(); - bool canBeDeleted() const; - bool deleteViaGui(); - - bool editItself(OwnCloudFeed *new_feed_data); - bool removeItself(); - - bool markAsReadUnread(ReadStatus status); - bool cleanMessages(bool clear_only_read); - - OwnCloudServiceRoot *serviceRoot() const; - - private: - QList obtainNewMessages(bool *error_during_obtaining); -}; - -#endif // OWNCLOUDFEED_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef OWNCLOUDFEED_H +#define OWNCLOUDFEED_H + +#include "services/abstract/feed.h" + + +class OwnCloudServiceRoot; + +class OwnCloudFeed : public Feed { + Q_OBJECT + + public: + explicit OwnCloudFeed(RootItem* parent = nullptr); + explicit OwnCloudFeed(const QSqlRecord& record); + virtual ~OwnCloudFeed(); + + bool canBeEdited() const; + bool editViaGui(); + bool canBeDeleted() const; + bool deleteViaGui(); + + bool editItself(OwnCloudFeed* new_feed_data); + bool removeItself(); + + bool markAsReadUnread(ReadStatus status); + bool cleanMessages(bool clear_only_read); + + OwnCloudServiceRoot* serviceRoot() const; + + private: + QList obtainNewMessages(bool* error_during_obtaining); +}; + +#endif // OWNCLOUDFEED_H diff --git a/src/services/owncloud/owncloudrecyclebin.cpp b/src/services/owncloud/owncloudrecyclebin.cpp index 3926a3fbf..3a682e1cb 100755 --- a/src/services/owncloud/owncloudrecyclebin.cpp +++ b/src/services/owncloud/owncloudrecyclebin.cpp @@ -1,40 +1,40 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/owncloud/owncloudrecyclebin.h" - -#include "services/owncloud/owncloudserviceroot.h" -#include "services/abstract/cacheforserviceroot.h" -#include "services/owncloud/network/owncloudnetworkfactory.h" - -#include - - -OwnCloudRecycleBin::OwnCloudRecycleBin(RootItem *parent) : RecycleBin(parent) { -} - -OwnCloudRecycleBin::~OwnCloudRecycleBin() { -} - -OwnCloudServiceRoot *OwnCloudRecycleBin::serviceRoot() { - return qobject_cast(getParentServiceRoot()); -} - -bool OwnCloudRecycleBin::markAsReadUnread(RootItem::ReadStatus status) { - serviceRoot()->addMessageStatesToCache(getParentServiceRoot()->customIDSOfMessagesForItem(this), status); - return RecycleBin::markAsReadUnread(status); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/owncloud/owncloudrecyclebin.h" + +#include "services/owncloud/owncloudserviceroot.h" +#include "services/abstract/cacheforserviceroot.h" +#include "services/owncloud/network/owncloudnetworkfactory.h" + +#include + + +OwnCloudRecycleBin::OwnCloudRecycleBin(RootItem* parent) : RecycleBin(parent) { +} + +OwnCloudRecycleBin::~OwnCloudRecycleBin() { +} + +OwnCloudServiceRoot* OwnCloudRecycleBin::serviceRoot() { + return qobject_cast(getParentServiceRoot()); +} + +bool OwnCloudRecycleBin::markAsReadUnread(RootItem::ReadStatus status) { + serviceRoot()->addMessageStatesToCache(getParentServiceRoot()->customIDSOfMessagesForItem(this), status); + return RecycleBin::markAsReadUnread(status); +} diff --git a/src/services/owncloud/owncloudrecyclebin.h b/src/services/owncloud/owncloudrecyclebin.h index 4aa1b862e..39246a66c 100755 --- a/src/services/owncloud/owncloudrecyclebin.h +++ b/src/services/owncloud/owncloudrecyclebin.h @@ -1,37 +1,37 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef OWNCLOUDRECYCLEBIN_H -#define OWNCLOUDRECYCLEBIN_H - -#include "services/abstract/recyclebin.h" - - -class OwnCloudServiceRoot; - -class OwnCloudRecycleBin : public RecycleBin { - Q_OBJECT - - public: - explicit OwnCloudRecycleBin(RootItem *parent = nullptr); - virtual ~OwnCloudRecycleBin(); - - OwnCloudServiceRoot *serviceRoot(); - bool markAsReadUnread(ReadStatus status); -}; - -#endif // OWNCLOUDRECYCLEBIN_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef OWNCLOUDRECYCLEBIN_H +#define OWNCLOUDRECYCLEBIN_H + +#include "services/abstract/recyclebin.h" + + +class OwnCloudServiceRoot; + +class OwnCloudRecycleBin : public RecycleBin { + Q_OBJECT + + public: + explicit OwnCloudRecycleBin(RootItem* parent = nullptr); + virtual ~OwnCloudRecycleBin(); + + OwnCloudServiceRoot* serviceRoot(); + bool markAsReadUnread(ReadStatus status); +}; + +#endif // OWNCLOUDRECYCLEBIN_H diff --git a/src/services/owncloud/owncloudserviceentrypoint.cpp b/src/services/owncloud/owncloudserviceentrypoint.cpp index 45244f19d..c8fda2ba9 100755 --- a/src/services/owncloud/owncloudserviceentrypoint.cpp +++ b/src/services/owncloud/owncloudserviceentrypoint.cpp @@ -1,73 +1,72 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/owncloud/owncloudserviceentrypoint.h" - -#include "definitions/definitions.h" -#include "miscellaneous/application.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/databasequeries.h" -#include "services/owncloud/definitions.h" -#include "services/owncloud/owncloudserviceroot.h" -#include "services/owncloud/gui/formeditowncloudaccount.h" - - -OwnCloudServiceEntryPoint::OwnCloudServiceEntryPoint() { -} - -OwnCloudServiceEntryPoint::~OwnCloudServiceEntryPoint() { -} - -ServiceRoot *OwnCloudServiceEntryPoint::createNewRoot() const { - QScopedPointer form_acc(new FormEditOwnCloudAccount(qApp->mainFormWidget())); - return form_acc->execForCreate(); -} - -QList OwnCloudServiceEntryPoint::initializeSubtree() const { - QSqlDatabase database = qApp->database()->connection(QSL("OwnCloudServiceEntryPoint"), DatabaseFactory::FromSettings); - - return DatabaseQueries::getOwnCloudAccounts(database); -} - -bool OwnCloudServiceEntryPoint::isSingleInstanceService() const { - return false; -} - -QString OwnCloudServiceEntryPoint::name() const { - return QSL("NextCloud News"); -} - -QString OwnCloudServiceEntryPoint::code() const { - return SERVICE_CODE_OWNCLOUD; -} - -QString OwnCloudServiceEntryPoint::description() const { - return QObject::tr("The News app is an RSS/Atom feed aggregator. It is part of Nextcloud suite. This plugin implements %1 API.").arg(API_VERSION); -} - -QString OwnCloudServiceEntryPoint::version() const { - return APP_VERSION; -} - -QString OwnCloudServiceEntryPoint::author() const { - return APP_AUTHOR; -} - -QIcon OwnCloudServiceEntryPoint::icon() const { - return qApp->icons()->miscIcon(QSL("nextcloud")); -} - +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/owncloud/owncloudserviceentrypoint.h" + +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/databasequeries.h" +#include "services/owncloud/definitions.h" +#include "services/owncloud/owncloudserviceroot.h" +#include "services/owncloud/gui/formeditowncloudaccount.h" + + +OwnCloudServiceEntryPoint::OwnCloudServiceEntryPoint() { +} + +OwnCloudServiceEntryPoint::~OwnCloudServiceEntryPoint() { +} + +ServiceRoot* OwnCloudServiceEntryPoint::createNewRoot() const { + QScopedPointer form_acc(new FormEditOwnCloudAccount(qApp->mainFormWidget())); + return form_acc->execForCreate(); +} + +QList OwnCloudServiceEntryPoint::initializeSubtree() const { + QSqlDatabase database = qApp->database()->connection(QSL("OwnCloudServiceEntryPoint"), DatabaseFactory::FromSettings); + return DatabaseQueries::getOwnCloudAccounts(database); +} + +bool OwnCloudServiceEntryPoint::isSingleInstanceService() const { + return false; +} + +QString OwnCloudServiceEntryPoint::name() const { + return QSL("NextCloud News"); +} + +QString OwnCloudServiceEntryPoint::code() const { + return SERVICE_CODE_OWNCLOUD; +} + +QString OwnCloudServiceEntryPoint::description() const { + return QObject::tr("The News app is an RSS/Atom feed aggregator. It is part of Nextcloud suite. This plugin implements %1 API.").arg(API_VERSION); +} + +QString OwnCloudServiceEntryPoint::version() const { + return APP_VERSION; +} + +QString OwnCloudServiceEntryPoint::author() const { + return APP_AUTHOR; +} + +QIcon OwnCloudServiceEntryPoint::icon() const { + return qApp->icons()->miscIcon(QSL("nextcloud")); +} + diff --git a/src/services/owncloud/owncloudserviceentrypoint.h b/src/services/owncloud/owncloudserviceentrypoint.h index 5ebc04de1..770bdd671 100755 --- a/src/services/owncloud/owncloudserviceentrypoint.h +++ b/src/services/owncloud/owncloudserviceentrypoint.h @@ -1,40 +1,40 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef OWNCLOUDSERVICEENTRYPOINT_H -#define OWNCLOUDSERVICEENTRYPOINT_H - -#include "services/abstract/serviceentrypoint.h" - - -class OwnCloudServiceEntryPoint : public ServiceEntryPoint { - public: - explicit OwnCloudServiceEntryPoint(); - virtual ~OwnCloudServiceEntryPoint(); - - ServiceRoot *createNewRoot() const; - QList initializeSubtree() const; - bool isSingleInstanceService() const; - QString name() const; - QString code() const; - QString description() const; - QString version() const; - QString author() const; - QIcon icon() const; -}; - -#endif // OWNCLOUDSERVICEENTRYPOINT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef OWNCLOUDSERVICEENTRYPOINT_H +#define OWNCLOUDSERVICEENTRYPOINT_H + +#include "services/abstract/serviceentrypoint.h" + + +class OwnCloudServiceEntryPoint : public ServiceEntryPoint { + public: + explicit OwnCloudServiceEntryPoint(); + virtual ~OwnCloudServiceEntryPoint(); + + ServiceRoot* createNewRoot() const; + QList initializeSubtree() const; + bool isSingleInstanceService() const; + QString name() const; + QString code() const; + QString description() const; + QString version() const; + QString author() const; + QIcon icon() const; +}; + +#endif // OWNCLOUDSERVICEENTRYPOINT_H diff --git a/src/services/owncloud/owncloudserviceroot.cpp b/src/services/owncloud/owncloudserviceroot.cpp index 1a35b09d3..12115bac4 100755 --- a/src/services/owncloud/owncloudserviceroot.cpp +++ b/src/services/owncloud/owncloudserviceroot.cpp @@ -1,307 +1,299 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/owncloud/owncloudserviceroot.h" - -#include "definitions/definitions.h" -#include "miscellaneous/databasequeries.h" -#include "miscellaneous/application.h" -#include "miscellaneous/textfactory.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/mutex.h" -#include "services/owncloud/owncloudserviceentrypoint.h" -#include "services/owncloud/owncloudrecyclebin.h" -#include "services/owncloud/owncloudfeed.h" -#include "services/owncloud/owncloudcategory.h" -#include "services/owncloud/network/owncloudnetworkfactory.h" -#include "services/owncloud/gui/formeditowncloudaccount.h" -#include "services/owncloud/gui/formowncloudfeeddetails.h" - - -OwnCloudServiceRoot::OwnCloudServiceRoot(RootItem *parent) - : ServiceRoot(parent), CacheForServiceRoot(), m_recycleBin(new OwnCloudRecycleBin(this)), - m_actionSyncIn(nullptr), m_serviceMenu(QList()), m_network(new OwnCloudNetworkFactory()) { - setIcon(OwnCloudServiceEntryPoint().icon()); -} - -OwnCloudServiceRoot::~OwnCloudServiceRoot() { - delete m_network; -} - -bool OwnCloudServiceRoot::canBeEdited() const { - return true; -} - -bool OwnCloudServiceRoot::canBeDeleted() const { - return true; -} - -bool OwnCloudServiceRoot::editViaGui() { - QScopedPointer form_pointer(new FormEditOwnCloudAccount(qApp->mainFormWidget())); - form_pointer.data()->execForEdit(this); - - return true; -} - -bool OwnCloudServiceRoot::deleteViaGui() { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - if (DatabaseQueries::deleteOwnCloudAccount(database, accountId())) { - return ServiceRoot::deleteViaGui(); - } - else { - return false; - } -} - -bool OwnCloudServiceRoot::supportsFeedAdding() const { - return true; -} - -bool OwnCloudServiceRoot::supportsCategoryAdding() const { - return false; -} - -QList OwnCloudServiceRoot::serviceMenu() { - if (m_serviceMenu.isEmpty()) { - m_actionSyncIn = new QAction(qApp->icons()->fromTheme(QSL("view-refresh")), tr("Sync in"), this); - - connect(m_actionSyncIn, &QAction::triggered, this, &OwnCloudServiceRoot::syncIn); - m_serviceMenu.append(m_actionSyncIn); - } - - return m_serviceMenu; -} - -RecycleBin *OwnCloudServiceRoot::recycleBin() const { - return m_recycleBin; -} - -void OwnCloudServiceRoot::start(bool freshly_activated) { - Q_UNUSED(freshly_activated) - - loadFromDatabase(); - - if (qApp->isFirstRun(QSL("3.1.1")) || (childCount() == 1 && child(0)->kind() == RootItemKind::Bin)) { - syncIn(); - } -} - -void OwnCloudServiceRoot::stop() { -} - -QString OwnCloudServiceRoot::code() const { - return OwnCloudServiceEntryPoint().code(); -} - -bool OwnCloudServiceRoot::markAsReadUnread(RootItem::ReadStatus status) { - addMessageStatesToCache(customIDSOfMessagesForItem(this), status); - return ServiceRoot::markAsReadUnread(status); -} - -OwnCloudNetworkFactory *OwnCloudServiceRoot::network() const { - return m_network; -} - -void OwnCloudServiceRoot::saveAllCachedData() { - QPair, QMap>> msgCache = takeMessageCache(); - QMapIterator i(msgCache.first); - - // Save the actual data read/unread. - while (i.hasNext()) { - i.next(); - auto key = i.key(); - QStringList ids = i.value(); - - if (!ids.isEmpty()) { - network()->markMessagesRead(key, ids); - } - } - - QMapIterator> j(msgCache.second); - - // Save the actual data important/not important. - while (j.hasNext()) { - j.next(); - auto key = j.key(); - QList messages = j.value(); - - if (!messages.isEmpty()) { - QStringList feed_ids, guid_hashes; - - foreach (const Message &msg, messages) { - feed_ids.append(msg.m_feedId); - guid_hashes.append(msg.m_customHash); - } - - network()->markMessagesStarred(key, feed_ids, guid_hashes); - } - } -} - -bool OwnCloudServiceRoot::onBeforeSetMessagesRead(RootItem *selected_item, - const QList &messages, - RootItem::ReadStatus read) { - Q_UNUSED(selected_item) - - addMessageStatesToCache(customIDsOfMessages(messages), read); - return true; -} - -bool OwnCloudServiceRoot::onBeforeSwitchMessageImportance(RootItem *selected_item, - const QList &changes) { - Q_UNUSED(selected_item) - - // Now, we need to separate the changes because of ownCloud API limitations. - QList mark_starred_msgs; - QList mark_unstarred_msgs; - - foreach (const ImportanceChange &pair, changes) { - if (pair.second == RootItem::Important) { - mark_starred_msgs.append(pair.first); - } - else { - mark_unstarred_msgs.append(pair.first); - } - } - - if (!mark_starred_msgs.isEmpty()) { - addMessageStatesToCache(mark_starred_msgs, RootItem::Important); - } - - if (!mark_unstarred_msgs.isEmpty()) { - addMessageStatesToCache(mark_unstarred_msgs, RootItem::NotImportant); - } - - return true; -} - -void OwnCloudServiceRoot::updateTitle() { - QString host = QUrl(m_network->url()).host(); - - if (host.isEmpty()) { - host = m_network->url(); - } - - setTitle(m_network->authUsername() + QL1S("@") + host + QSL(" (Nextcloud News)")); -} - -void OwnCloudServiceRoot::saveAccountDataToDatabase() { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - if (accountId() != NO_PARENT_CATEGORY) { - if (DatabaseQueries::overwriteOwnCloudAccount(database, m_network->authUsername(), - m_network->authPassword(), m_network->url(), - m_network->forceServerSideUpdate(), accountId())) { - updateTitle(); - itemChanged(QList() << this); - } - } - else { - bool saved; - int id_to_assign = DatabaseQueries::createAccount(database, code(), &saved); - - if (saved) { - if (DatabaseQueries::createOwnCloudAccount(database, id_to_assign, m_network->authUsername(), - m_network->authPassword(), m_network->url(), - m_network->forceServerSideUpdate())) { - setId(id_to_assign); - setAccountId(id_to_assign); - updateTitle(); - } - } - } -} - -void OwnCloudServiceRoot::addNewFeed(const QString &url) { - if (!qApp->feedUpdateLock()->tryLock()) { - // Lock was not obtained because - // it is used probably by feed updater or application - // is quitting. - qApp->showGuiMessage(tr("Cannot add item"), - tr("Cannot add feed because another critical operation is ongoing."), - QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); - // Thus, cannot delete and quit the method. - return; - } - - QScopedPointer form_pointer(new FormOwnCloudFeedDetails(this, qApp->mainFormWidget())); - - form_pointer.data()->addEditFeed(nullptr, this, url); - qApp->feedUpdateLock()->unlock(); -} - -void OwnCloudServiceRoot::addNewCategory() { -} - -QMap OwnCloudServiceRoot::storeCustomFeedsData() { - QMap custom_data; - - foreach (const Feed *feed, getSubTreeFeeds()) { - QVariantMap feed_custom_data; - - feed_custom_data.insert(QSL("auto_update_interval"), feed->autoUpdateInitialInterval()); - feed_custom_data.insert(QSL("auto_update_type"), feed->autoUpdateType()); - - custom_data.insert(feed->customId(), feed_custom_data); - } - - return custom_data; -} - -void OwnCloudServiceRoot::restoreCustomFeedsData(const QMap &data, const QHash &feeds) { - QMapIterator i(data); - - while (i.hasNext()) { - i.next(); - - const int custom_id = i.key(); - - if (feeds.contains(custom_id)) { - Feed *feed = feeds.value(custom_id); - QVariantMap feed_custom_data = i.value().toMap(); - - feed->setAutoUpdateInitialInterval(feed_custom_data.value(QSL("auto_update_interval")).toInt()); - feed->setAutoUpdateType(static_cast(feed_custom_data.value(QSL("auto_update_type")).toInt())); - } - } -} - -RootItem *OwnCloudServiceRoot::obtainNewTreeForSyncIn() const { - OwnCloudGetFeedsCategoriesResponse feed_cats_response = m_network->feedsCategories(); - - if (m_network->lastError() == QNetworkReply::NoError) { - return feed_cats_response.feedsCategories(true); - } - else { - return nullptr; - } -} - -void OwnCloudServiceRoot::loadFromDatabase() { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - Assignment categories = DatabaseQueries::getOwnCloudCategories(database, accountId()); - Assignment feeds = DatabaseQueries::getOwnCloudFeeds(database, accountId()); - - // All data are now obtained, lets create the hierarchy. - assembleCategories(categories); - assembleFeeds(feeds); - - // As the last item, add recycle bin, which is needed. - appendChild(m_recycleBin); - updateCounts(true); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/owncloud/owncloudserviceroot.h" + +#include "definitions/definitions.h" +#include "miscellaneous/databasequeries.h" +#include "miscellaneous/application.h" +#include "miscellaneous/textfactory.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/mutex.h" +#include "services/owncloud/owncloudserviceentrypoint.h" +#include "services/owncloud/owncloudrecyclebin.h" +#include "services/owncloud/owncloudfeed.h" +#include "services/owncloud/owncloudcategory.h" +#include "services/owncloud/network/owncloudnetworkfactory.h" +#include "services/owncloud/gui/formeditowncloudaccount.h" +#include "services/owncloud/gui/formowncloudfeeddetails.h" + + +OwnCloudServiceRoot::OwnCloudServiceRoot(RootItem* parent) + : ServiceRoot(parent), CacheForServiceRoot(), m_recycleBin(new OwnCloudRecycleBin(this)), + m_actionSyncIn(nullptr), m_serviceMenu(QList()), m_network(new OwnCloudNetworkFactory()) { + setIcon(OwnCloudServiceEntryPoint().icon()); +} + +OwnCloudServiceRoot::~OwnCloudServiceRoot() { + delete m_network; +} + +bool OwnCloudServiceRoot::canBeEdited() const { + return true; +} + +bool OwnCloudServiceRoot::canBeDeleted() const { + return true; +} + +bool OwnCloudServiceRoot::editViaGui() { + QScopedPointer form_pointer(new FormEditOwnCloudAccount(qApp->mainFormWidget())); + form_pointer.data()->execForEdit(this); + return true; +} + +bool OwnCloudServiceRoot::deleteViaGui() { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + + if (DatabaseQueries::deleteOwnCloudAccount(database, accountId())) { + return ServiceRoot::deleteViaGui(); + } + + else { + return false; + } +} + +bool OwnCloudServiceRoot::supportsFeedAdding() const { + return true; +} + +bool OwnCloudServiceRoot::supportsCategoryAdding() const { + return false; +} + +QList OwnCloudServiceRoot::serviceMenu() { + if (m_serviceMenu.isEmpty()) { + m_actionSyncIn = new QAction(qApp->icons()->fromTheme(QSL("view-refresh")), tr("Sync in"), this); + connect(m_actionSyncIn, &QAction::triggered, this, &OwnCloudServiceRoot::syncIn); + m_serviceMenu.append(m_actionSyncIn); + } + + return m_serviceMenu; +} + +RecycleBin* OwnCloudServiceRoot::recycleBin() const { + return m_recycleBin; +} + +void OwnCloudServiceRoot::start(bool freshly_activated) { + Q_UNUSED(freshly_activated) + loadFromDatabase(); + + if (qApp->isFirstRun(QSL("3.1.1")) || (childCount() == 1 && child(0)->kind() == RootItemKind::Bin)) { + syncIn(); + } +} + +void OwnCloudServiceRoot::stop() { +} + +QString OwnCloudServiceRoot::code() const { + return OwnCloudServiceEntryPoint().code(); +} + +bool OwnCloudServiceRoot::markAsReadUnread(RootItem::ReadStatus status) { + addMessageStatesToCache(customIDSOfMessagesForItem(this), status); + return ServiceRoot::markAsReadUnread(status); +} + +OwnCloudNetworkFactory* OwnCloudServiceRoot::network() const { + return m_network; +} + +void OwnCloudServiceRoot::saveAllCachedData() { + QPair, QMap>> msgCache = takeMessageCache(); + QMapIterator i(msgCache.first); + + // Save the actual data read/unread. + while (i.hasNext()) { + i.next(); + auto key = i.key(); + QStringList ids = i.value(); + + if (!ids.isEmpty()) { + network()->markMessagesRead(key, ids); + } + } + + QMapIterator> j(msgCache.second); + + // Save the actual data important/not important. + while (j.hasNext()) { + j.next(); + auto key = j.key(); + QList messages = j.value(); + + if (!messages.isEmpty()) { + QStringList feed_ids, guid_hashes; + + foreach (const Message& msg, messages) { + feed_ids.append(msg.m_feedId); + guid_hashes.append(msg.m_customHash); + } + + network()->markMessagesStarred(key, feed_ids, guid_hashes); + } + } +} + +bool OwnCloudServiceRoot::onBeforeSetMessagesRead(RootItem* selected_item, + const QList& messages, + RootItem::ReadStatus read) { + Q_UNUSED(selected_item) + addMessageStatesToCache(customIDsOfMessages(messages), read); + return true; +} + +bool OwnCloudServiceRoot::onBeforeSwitchMessageImportance(RootItem* selected_item, + const QList& changes) { + Q_UNUSED(selected_item) + // Now, we need to separate the changes because of ownCloud API limitations. + QList mark_starred_msgs; + QList mark_unstarred_msgs; + + foreach (const ImportanceChange& pair, changes) { + if (pair.second == RootItem::Important) { + mark_starred_msgs.append(pair.first); + } + + else { + mark_unstarred_msgs.append(pair.first); + } + } + + if (!mark_starred_msgs.isEmpty()) { + addMessageStatesToCache(mark_starred_msgs, RootItem::Important); + } + + if (!mark_unstarred_msgs.isEmpty()) { + addMessageStatesToCache(mark_unstarred_msgs, RootItem::NotImportant); + } + + return true; +} + +void OwnCloudServiceRoot::updateTitle() { + QString host = QUrl(m_network->url()).host(); + + if (host.isEmpty()) { + host = m_network->url(); + } + + setTitle(m_network->authUsername() + QL1S("@") + host + QSL(" (Nextcloud News)")); +} + +void OwnCloudServiceRoot::saveAccountDataToDatabase() { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + + if (accountId() != NO_PARENT_CATEGORY) { + if (DatabaseQueries::overwriteOwnCloudAccount(database, m_network->authUsername(), + m_network->authPassword(), m_network->url(), + m_network->forceServerSideUpdate(), accountId())) { + updateTitle(); + itemChanged(QList() << this); + } + } + + else { + bool saved; + int id_to_assign = DatabaseQueries::createAccount(database, code(), &saved); + + if (saved) { + if (DatabaseQueries::createOwnCloudAccount(database, id_to_assign, m_network->authUsername(), + m_network->authPassword(), m_network->url(), + m_network->forceServerSideUpdate())) { + setId(id_to_assign); + setAccountId(id_to_assign); + updateTitle(); + } + } + } +} + +void OwnCloudServiceRoot::addNewFeed(const QString& url) { + if (!qApp->feedUpdateLock()->tryLock()) { + // Lock was not obtained because + // it is used probably by feed updater or application + // is quitting. + qApp->showGuiMessage(tr("Cannot add item"), + tr("Cannot add feed because another critical operation is ongoing."), + QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); + // Thus, cannot delete and quit the method. + return; + } + + QScopedPointer form_pointer(new FormOwnCloudFeedDetails(this, qApp->mainFormWidget())); + form_pointer.data()->addEditFeed(nullptr, this, url); + qApp->feedUpdateLock()->unlock(); +} + +void OwnCloudServiceRoot::addNewCategory() { +} + +QMap OwnCloudServiceRoot::storeCustomFeedsData() { + QMap custom_data; + + foreach (const Feed* feed, getSubTreeFeeds()) { + QVariantMap feed_custom_data; + feed_custom_data.insert(QSL("auto_update_interval"), feed->autoUpdateInitialInterval()); + feed_custom_data.insert(QSL("auto_update_type"), feed->autoUpdateType()); + custom_data.insert(feed->customId(), feed_custom_data); + } + + return custom_data; +} + +void OwnCloudServiceRoot::restoreCustomFeedsData(const QMap& data, const QHash& feeds) { + QMapIterator i(data); + + while (i.hasNext()) { + i.next(); + const int custom_id = i.key(); + + if (feeds.contains(custom_id)) { + Feed* feed = feeds.value(custom_id); + QVariantMap feed_custom_data = i.value().toMap(); + feed->setAutoUpdateInitialInterval(feed_custom_data.value(QSL("auto_update_interval")).toInt()); + feed->setAutoUpdateType(static_cast(feed_custom_data.value(QSL("auto_update_type")).toInt())); + } + } +} + +RootItem* OwnCloudServiceRoot::obtainNewTreeForSyncIn() const { + OwnCloudGetFeedsCategoriesResponse feed_cats_response = m_network->feedsCategories(); + + if (m_network->lastError() == QNetworkReply::NoError) { + return feed_cats_response.feedsCategories(true); + } + + else { + return nullptr; + } +} + +void OwnCloudServiceRoot::loadFromDatabase() { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + Assignment categories = DatabaseQueries::getOwnCloudCategories(database, accountId()); + Assignment feeds = DatabaseQueries::getOwnCloudFeeds(database, accountId()); + // All data are now obtained, lets create the hierarchy. + assembleCategories(categories); + assembleFeeds(feeds); + // As the last item, add recycle bin, which is needed. + appendChild(m_recycleBin); + updateCounts(true); +} diff --git a/src/services/owncloud/owncloudserviceroot.h b/src/services/owncloud/owncloudserviceroot.h index aed6846e5..fcbcf64af 100755 --- a/src/services/owncloud/owncloudserviceroot.h +++ b/src/services/owncloud/owncloudserviceroot.h @@ -1,79 +1,79 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef OWNCLOUDSERVICEROOT_H -#define OWNCLOUDSERVICEROOT_H - -#include "services/abstract/serviceroot.h" -#include "services/abstract/cacheforserviceroot.h" - -#include - - -class OwnCloudNetworkFactory; -class OwnCloudRecycleBin; -class Mutex; - -class OwnCloudServiceRoot : public ServiceRoot, public CacheForServiceRoot { - Q_OBJECT - - public: - explicit OwnCloudServiceRoot(RootItem *parent = nullptr); - virtual ~OwnCloudServiceRoot(); - - bool canBeEdited() const; - bool canBeDeleted() const; - bool editViaGui(); - bool deleteViaGui(); - bool supportsFeedAdding() const; - bool supportsCategoryAdding() const; - QList serviceMenu(); - RecycleBin *recycleBin() const; - void start(bool freshly_activated); - void stop(); - QString code() const; - bool markAsReadUnread(ReadStatus status); - - OwnCloudNetworkFactory *network() const; - - bool onBeforeSetMessagesRead(RootItem *selected_item, const QList &messages, ReadStatus read); - bool onBeforeSwitchMessageImportance(RootItem *selected_item, const QList &changes); - - void updateTitle(); - void saveAccountDataToDatabase(); - - - void saveAllCachedData(); - - public slots: - void addNewFeed(const QString &url); - void addNewCategory(); - - private: - QMap storeCustomFeedsData(); - void restoreCustomFeedsData(const QMap &data, const QHash &feeds); - RootItem *obtainNewTreeForSyncIn() const; - - void loadFromDatabase(); - - OwnCloudRecycleBin *m_recycleBin; - QAction *m_actionSyncIn; - QList m_serviceMenu; - OwnCloudNetworkFactory *m_network; -}; - -#endif // OWNCLOUDSERVICEROOT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef OWNCLOUDSERVICEROOT_H +#define OWNCLOUDSERVICEROOT_H + +#include "services/abstract/serviceroot.h" +#include "services/abstract/cacheforserviceroot.h" + +#include + + +class OwnCloudNetworkFactory; +class OwnCloudRecycleBin; +class Mutex; + +class OwnCloudServiceRoot : public ServiceRoot, public CacheForServiceRoot { + Q_OBJECT + + public: + explicit OwnCloudServiceRoot(RootItem* parent = nullptr); + virtual ~OwnCloudServiceRoot(); + + bool canBeEdited() const; + bool canBeDeleted() const; + bool editViaGui(); + bool deleteViaGui(); + bool supportsFeedAdding() const; + bool supportsCategoryAdding() const; + QList serviceMenu(); + RecycleBin* recycleBin() const; + void start(bool freshly_activated); + void stop(); + QString code() const; + bool markAsReadUnread(ReadStatus status); + + OwnCloudNetworkFactory* network() const; + + bool onBeforeSetMessagesRead(RootItem* selected_item, const QList& messages, ReadStatus read); + bool onBeforeSwitchMessageImportance(RootItem* selected_item, const QList& changes); + + void updateTitle(); + void saveAccountDataToDatabase(); + + + void saveAllCachedData(); + + public slots: + void addNewFeed(const QString& url); + void addNewCategory(); + + private: + QMap storeCustomFeedsData(); + void restoreCustomFeedsData(const QMap& data, const QHash& feeds); + RootItem* obtainNewTreeForSyncIn() const; + + void loadFromDatabase(); + + OwnCloudRecycleBin* m_recycleBin; + QAction* m_actionSyncIn; + QList m_serviceMenu; + OwnCloudNetworkFactory* m_network; +}; + +#endif // OWNCLOUDSERVICEROOT_H diff --git a/src/services/standard/atomparser.cpp b/src/services/standard/atomparser.cpp old mode 100644 new mode 100755 index ddb84bd70..280b78949 --- a/src/services/standard/atomparser.cpp +++ b/src/services/standard/atomparser.cpp @@ -1,129 +1,130 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/standard/atomparser.h" - -#include "miscellaneous/textfactory.h" -#include "network-web/webfactory.h" - -#include "exceptions/applicationexception.h" - - -AtomParser::AtomParser(const QString &data) : FeedParser(data), m_atomNamespace(QSL("http://www.w3.org/2005/Atom")) { -} - -AtomParser::~AtomParser() { -} - -QString AtomParser::feedAuthor() const { - QDomNodeList authors = m_xml.documentElement().elementsByTagNameNS(m_atomNamespace, QSL("author")); - QStringList author_str; - - for (int i = 0; i < authors.size(); i++) { - QDomNodeList names = authors.at(i).toElement().elementsByTagNameNS(m_atomNamespace, QSL("name")); - - if (!names.isEmpty()) { - author_str.append(names.at(0).toElement().text()); - } - } - - return author_str.join(", "); -} - -Message AtomParser::extractMessage(const QDomElement &msg_element, QDateTime current_time) const { - Message new_message; - QString title = textsFromPath(msg_element, m_atomNamespace, QSL("title"), true).join(QSL(", ")); - QString summary = textsFromPath(msg_element, m_atomNamespace, QSL("content"), true).join(QSL(", ")); - - if (summary.isEmpty()) { - summary = textsFromPath(msg_element, m_atomNamespace, QSL("summary"), true).join(QSL(", ")); - } - - // Now we obtained maximum of information for title & description. - if (title.isEmpty() && summary.isEmpty()) { - // BOTH title and description are empty, skip this message. - throw new ApplicationException(QSL("Not enough data for the message.")); - } - - // Title is not empty, description does not matter. - new_message.m_title = WebFactory::instance()->stripTags(title); - new_message.m_contents = summary; - new_message.m_author = WebFactory::instance()->escapeHtml(messageAuthor(msg_element)); - - QString updated = textsFromPath(msg_element, m_atomNamespace, QSL("updated"), true).join(QSL(", ")); - - // Deal with creation date. - new_message.m_created = TextFactory::parseDateTime(updated); - new_message.m_createdFromFeed = !new_message.m_created.isNull(); - - if (!new_message.m_createdFromFeed) { - // Date was NOT obtained from the feed, set current date as creation date for the message. - new_message.m_created = current_time; - } - - // Deal with links - QDomNodeList elem_links = msg_element.toElement().elementsByTagNameNS(m_atomNamespace, QSL("link")); - QString last_link_alternate, last_link_other; - - for (int i = 0; i < elem_links.size(); i++) { - QDomElement link = elem_links.at(i).toElement(); - QString attribute = link.attribute(QSL("rel")); - - if (attribute == QSL("enclosure")) { - new_message.m_enclosures.append(Enclosure(link.attribute(QSL("href")), link.attribute(QSL("type")))); - - qDebug("Adding enclosure '%s' for the message.", qPrintable(new_message.m_enclosures.last().m_url)); - } - else if (attribute.isEmpty() || attribute == QSL("alternate")) { - last_link_alternate = link.attribute(QSL("href")); - } - else { - last_link_other = link.attribute(QSL("href")); - } - } - - if (!last_link_alternate.isEmpty()) { - new_message.m_url = last_link_alternate; - } - else if (!last_link_other.isEmpty()) { - new_message.m_url = last_link_other; - } - else if (!new_message.m_enclosures.isEmpty()) { - new_message.m_url = new_message.m_enclosures.first().m_url; - } - - return new_message; -} - -QString AtomParser::messageAuthor(const QDomElement &msg_element) const { - QDomNodeList authors = msg_element.elementsByTagNameNS(m_atomNamespace, QSL("author")); - QStringList author_str; - - for (int i = 0; i < authors.size(); i++) { - QDomNodeList names = authors.at(i).toElement().elementsByTagNameNS(m_atomNamespace, QSL("name")); - - if (!names.isEmpty()) { - author_str.append(names.at(0).toElement().text()); - } - } - - return author_str.join(", "); -} - -QDomNodeList AtomParser::messageElements() { - return m_xml.elementsByTagNameNS(m_atomNamespace, QSL("entry")); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/standard/atomparser.h" + +#include "miscellaneous/textfactory.h" +#include "network-web/webfactory.h" + +#include "exceptions/applicationexception.h" + + +AtomParser::AtomParser(const QString& data) : FeedParser(data), m_atomNamespace(QSL("http://www.w3.org/2005/Atom")) { +} + +AtomParser::~AtomParser() { +} + +QString AtomParser::feedAuthor() const { + QDomNodeList authors = m_xml.documentElement().elementsByTagNameNS(m_atomNamespace, QSL("author")); + QStringList author_str; + + for (int i = 0; i < authors.size(); i++) { + QDomNodeList names = authors.at(i).toElement().elementsByTagNameNS(m_atomNamespace, QSL("name")); + + if (!names.isEmpty()) { + author_str.append(names.at(0).toElement().text()); + } + } + + return author_str.join(", "); +} + +Message AtomParser::extractMessage(const QDomElement& msg_element, QDateTime current_time) const { + Message new_message; + QString title = textsFromPath(msg_element, m_atomNamespace, QSL("title"), true).join(QSL(", ")); + QString summary = textsFromPath(msg_element, m_atomNamespace, QSL("content"), true).join(QSL(", ")); + + if (summary.isEmpty()) { + summary = textsFromPath(msg_element, m_atomNamespace, QSL("summary"), true).join(QSL(", ")); + } + + // Now we obtained maximum of information for title & description. + if (title.isEmpty() && summary.isEmpty()) { + // BOTH title and description are empty, skip this message. + throw new ApplicationException(QSL("Not enough data for the message.")); + } + + // Title is not empty, description does not matter. + new_message.m_title = WebFactory::instance()->stripTags(title); + new_message.m_contents = summary; + new_message.m_author = WebFactory::instance()->escapeHtml(messageAuthor(msg_element)); + QString updated = textsFromPath(msg_element, m_atomNamespace, QSL("updated"), true).join(QSL(", ")); + // Deal with creation date. + new_message.m_created = TextFactory::parseDateTime(updated); + new_message.m_createdFromFeed = !new_message.m_created.isNull(); + + if (!new_message.m_createdFromFeed) { + // Date was NOT obtained from the feed, set current date as creation date for the message. + new_message.m_created = current_time; + } + + // Deal with links + QDomNodeList elem_links = msg_element.toElement().elementsByTagNameNS(m_atomNamespace, QSL("link")); + QString last_link_alternate, last_link_other; + + for (int i = 0; i < elem_links.size(); i++) { + QDomElement link = elem_links.at(i).toElement(); + QString attribute = link.attribute(QSL("rel")); + + if (attribute == QSL("enclosure")) { + new_message.m_enclosures.append(Enclosure(link.attribute(QSL("href")), link.attribute(QSL("type")))); + qDebug("Adding enclosure '%s' for the message.", qPrintable(new_message.m_enclosures.last().m_url)); + } + + else if (attribute.isEmpty() || attribute == QSL("alternate")) { + last_link_alternate = link.attribute(QSL("href")); + } + + else { + last_link_other = link.attribute(QSL("href")); + } + } + + if (!last_link_alternate.isEmpty()) { + new_message.m_url = last_link_alternate; + } + + else if (!last_link_other.isEmpty()) { + new_message.m_url = last_link_other; + } + + else if (!new_message.m_enclosures.isEmpty()) { + new_message.m_url = new_message.m_enclosures.first().m_url; + } + + return new_message; +} + +QString AtomParser::messageAuthor(const QDomElement& msg_element) const { + QDomNodeList authors = msg_element.elementsByTagNameNS(m_atomNamespace, QSL("author")); + QStringList author_str; + + for (int i = 0; i < authors.size(); i++) { + QDomNodeList names = authors.at(i).toElement().elementsByTagNameNS(m_atomNamespace, QSL("name")); + + if (!names.isEmpty()) { + author_str.append(names.at(0).toElement().text()); + } + } + + return author_str.join(", "); +} + +QDomNodeList AtomParser::messageElements() { + return m_xml.elementsByTagNameNS(m_atomNamespace, QSL("entry")); +} diff --git a/src/services/standard/atomparser.h b/src/services/standard/atomparser.h old mode 100644 new mode 100755 index 82e6f22f0..5dd50755b --- a/src/services/standard/atomparser.h +++ b/src/services/standard/atomparser.h @@ -1,44 +1,44 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef ATOMPARSER_H -#define ATOMPARSER_H - -#include "services/standard/feedparser.h" - -#include "core/message.h" - -#include -#include - - -class AtomParser : public FeedParser { - public: - explicit AtomParser(const QString &data); - virtual ~AtomParser(); - - private: - QDomNodeList messageElements(); - QString feedAuthor() const; - Message extractMessage(const QDomElement &msg_element, QDateTime current_time) const; - QString messageAuthor(const QDomElement &msg_element) const; - - private: - QString m_atomNamespace; -}; - -#endif // ATOMPARSER_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef ATOMPARSER_H +#define ATOMPARSER_H + +#include "services/standard/feedparser.h" + +#include "core/message.h" + +#include +#include + + +class AtomParser : public FeedParser { + public: + explicit AtomParser(const QString& data); + virtual ~AtomParser(); + + private: + QDomNodeList messageElements(); + QString feedAuthor() const; + Message extractMessage(const QDomElement& msg_element, QDateTime current_time) const; + QString messageAuthor(const QDomElement& msg_element) const; + + private: + QString m_atomNamespace; +}; + +#endif // ATOMPARSER_H diff --git a/src/services/standard/feedparser.cpp b/src/services/standard/feedparser.cpp old mode 100644 new mode 100755 index 7da63dc13..e2b8a1445 --- a/src/services/standard/feedparser.cpp +++ b/src/services/standard/feedparser.cpp @@ -1,100 +1,99 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/standard/feedparser.h" - -#include "exceptions/applicationexception.h" - - -FeedParser::FeedParser(const QString &data) : m_xmlData(data) { - m_xml.setContent(m_xmlData, true); -} - -FeedParser::~FeedParser() { -} - -QList FeedParser::messages() { - QString feed_author = feedAuthor(); - - QList messages; - QDateTime current_time = QDateTime::currentDateTime(); - - // Pull out all messages. - QDomNodeList messages_in_xml = messageElements(); - - for (int i = 0; i < messages_in_xml.size(); i++) { - QDomNode message_item = messages_in_xml.item(i); - - try { - Message new_message = extractMessage(message_item.toElement(), current_time); - - if (new_message.m_author.isEmpty()) { - new_message.m_author = feed_author; - } - - messages.append(new_message); - } - catch (const ApplicationException &ex) { - qDebug(qPrintable(ex.message())); - } - } - - return messages; -} - -QStringList FeedParser::textsFromPath(const QDomElement &element, const QString &namespace_uri, - const QString &xml_path, bool only_first) const { - QStringList paths = xml_path.split('/'); - QStringList result; - QList current_elements; - current_elements.append(element); - - while (!paths.isEmpty()) { - QList next_elements; - QString next_local_name = paths.takeFirst(); - - foreach (const QDomElement &elem, current_elements) { - QDomNodeList elements = elem.elementsByTagNameNS(namespace_uri, next_local_name); - - for (int i = 0; i < elements.size(); i++) { - next_elements.append(elements.at(i).toElement()); - - if (only_first) { - break; - } - } - - if (next_elements.size() == 1 && only_first) { - break; - } - } - - current_elements = next_elements; - } - - if (!current_elements.isEmpty()) { - foreach (const QDomElement &elem, current_elements) { - result.append(elem.text()); - } - } - - return result; -} - -QString FeedParser::feedAuthor() const { - return ""; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/standard/feedparser.h" + +#include "exceptions/applicationexception.h" + + +FeedParser::FeedParser(const QString& data) : m_xmlData(data) { + m_xml.setContent(m_xmlData, true); +} + +FeedParser::~FeedParser() { +} + +QList FeedParser::messages() { + QString feed_author = feedAuthor(); + QList messages; + QDateTime current_time = QDateTime::currentDateTime(); + // Pull out all messages. + QDomNodeList messages_in_xml = messageElements(); + + for (int i = 0; i < messages_in_xml.size(); i++) { + QDomNode message_item = messages_in_xml.item(i); + + try { + Message new_message = extractMessage(message_item.toElement(), current_time); + + if (new_message.m_author.isEmpty()) { + new_message.m_author = feed_author; + } + + messages.append(new_message); + } + + catch (const ApplicationException& ex) { + qDebug(qPrintable(ex.message())); + } + } + + return messages; +} + +QStringList FeedParser::textsFromPath(const QDomElement& element, const QString& namespace_uri, + const QString& xml_path, bool only_first) const { + QStringList paths = xml_path.split('/'); + QStringList result; + QList current_elements; + current_elements.append(element); + + while (!paths.isEmpty()) { + QList next_elements; + QString next_local_name = paths.takeFirst(); + + foreach (const QDomElement& elem, current_elements) { + QDomNodeList elements = elem.elementsByTagNameNS(namespace_uri, next_local_name); + + for (int i = 0; i < elements.size(); i++) { + next_elements.append(elements.at(i).toElement()); + + if (only_first) { + break; + } + } + + if (next_elements.size() == 1 && only_first) { + break; + } + } + + current_elements = next_elements; + } + + if (!current_elements.isEmpty()) { + foreach (const QDomElement& elem, current_elements) { + result.append(elem.text()); + } + } + + return result; +} + +QString FeedParser::feedAuthor() const { + return ""; +} diff --git a/src/services/standard/feedparser.h b/src/services/standard/feedparser.h old mode 100644 new mode 100755 index d18ca4469..78cb08c3d --- a/src/services/standard/feedparser.h +++ b/src/services/standard/feedparser.h @@ -1,45 +1,45 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FEEDPARSER_H -#define FEEDPARSER_H - -#include -#include - -#include "core/message.h" - - -class FeedParser { - public: - explicit FeedParser(const QString &data); - virtual ~FeedParser(); - - virtual QList messages(); - - protected: - QStringList textsFromPath(const QDomElement &element, const QString &namespace_uri, const QString &xml_path, bool only_first) const; - virtual QDomNodeList messageElements() = 0; - virtual QString feedAuthor() const; - virtual Message extractMessage(const QDomElement &msg_element, QDateTime current_time) const = 0; - - protected: - QString m_xmlData; - QDomDocument m_xml; -}; - -#endif // FEEDPARSER_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FEEDPARSER_H +#define FEEDPARSER_H + +#include +#include + +#include "core/message.h" + + +class FeedParser { + public: + explicit FeedParser(const QString& data); + virtual ~FeedParser(); + + virtual QList messages(); + + protected: + QStringList textsFromPath(const QDomElement& element, const QString& namespace_uri, const QString& xml_path, bool only_first) const; + virtual QDomNodeList messageElements() = 0; + virtual QString feedAuthor() const; + virtual Message extractMessage(const QDomElement& msg_element, QDateTime current_time) const = 0; + + protected: + QString m_xmlData; + QDomDocument m_xml; +}; + +#endif // FEEDPARSER_H diff --git a/src/services/standard/gui/formstandardcategorydetails.cpp b/src/services/standard/gui/formstandardcategorydetails.cpp index e9a0ac9db..b36559cba 100755 --- a/src/services/standard/gui/formstandardcategorydetails.cpp +++ b/src/services/standard/gui/formstandardcategorydetails.cpp @@ -39,226 +39,220 @@ #include -FormStandardCategoryDetails::FormStandardCategoryDetails(StandardServiceRoot *service_root, QWidget *parent) - : QDialog(parent), m_editableCategory(nullptr), m_serviceRoot(service_root) { - initialize(); - createConnections(); - - // Initialize text boxes. - onTitleChanged(QString()); - onDescriptionChanged(QString()); +FormStandardCategoryDetails::FormStandardCategoryDetails(StandardServiceRoot* service_root, QWidget* parent) + : QDialog(parent), m_editableCategory(nullptr), m_serviceRoot(service_root) { + initialize(); + createConnections(); + // Initialize text boxes. + onTitleChanged(QString()); + onDescriptionChanged(QString()); } FormStandardCategoryDetails::~FormStandardCategoryDetails() { - qDebug("Destroying FormCategoryDetails instance."); + qDebug("Destroying FormCategoryDetails instance."); } void FormStandardCategoryDetails::createConnections() { - // General connections. - connect(m_ui->m_buttonBox, SIGNAL(accepted()), this, SLOT(apply())); - connect(m_ui->m_txtTitle->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(onTitleChanged(QString))); - connect(m_ui->m_txtDescription->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(onDescriptionChanged(QString))); - - // Icon connections. - connect(m_actionLoadIconFromFile, SIGNAL(triggered()), this, SLOT(onLoadIconFromFile())); - connect(m_actionNoIcon, SIGNAL(triggered()), this, SLOT(onNoIconSelected())); - connect(m_actionUseDefaultIcon, SIGNAL(triggered()), this, SLOT(onUseDefaultIcon())); + // General connections. + connect(m_ui->m_buttonBox, SIGNAL(accepted()), this, SLOT(apply())); + connect(m_ui->m_txtTitle->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(onTitleChanged(QString))); + connect(m_ui->m_txtDescription->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(onDescriptionChanged(QString))); + // Icon connections. + connect(m_actionLoadIconFromFile, SIGNAL(triggered()), this, SLOT(onLoadIconFromFile())); + connect(m_actionNoIcon, SIGNAL(triggered()), this, SLOT(onNoIconSelected())); + connect(m_actionUseDefaultIcon, SIGNAL(triggered()), this, SLOT(onUseDefaultIcon())); } -void FormStandardCategoryDetails::setEditableCategory(StandardCategory *editable_category) { - m_editableCategory = editable_category; - - m_ui->m_cmbParentCategory->setCurrentIndex(m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) editable_category->parent()))); - m_ui->m_txtTitle->lineEdit()->setText(editable_category->title()); - m_ui->m_txtDescription->lineEdit()->setText(editable_category->description()); - m_ui->m_btnIcon->setIcon(editable_category->icon()); +void FormStandardCategoryDetails::setEditableCategory(StandardCategory* editable_category) { + m_editableCategory = editable_category; + m_ui->m_cmbParentCategory->setCurrentIndex(m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) editable_category->parent()))); + m_ui->m_txtTitle->lineEdit()->setText(editable_category->title()); + m_ui->m_txtDescription->lineEdit()->setText(editable_category->description()); + m_ui->m_btnIcon->setIcon(editable_category->icon()); } -int FormStandardCategoryDetails::addEditCategory(StandardCategory *input_category, RootItem *parent_to_select) { - // Load categories. - loadCategories(m_serviceRoot->getSubTreeCategories(), m_serviceRoot, input_category); +int FormStandardCategoryDetails::addEditCategory(StandardCategory* input_category, RootItem* parent_to_select) { + // Load categories. + loadCategories(m_serviceRoot->getSubTreeCategories(), m_serviceRoot, input_category); - if (input_category == nullptr) { - // User is adding new category. - setWindowTitle(tr("Add new category")); + if (input_category == nullptr) { + // User is adding new category. + setWindowTitle(tr("Add new category")); + // Make sure that "default" icon is used as the default option for new + // categories. + m_actionUseDefaultIcon->trigger(); - // Make sure that "default" icon is used as the default option for new - // categories. - m_actionUseDefaultIcon->trigger(); + // Load parent from suggested item. + if (parent_to_select != nullptr) { + if (parent_to_select->kind() == RootItemKind::Category) { + m_ui->m_cmbParentCategory->setCurrentIndex(m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) parent_to_select))); + } - // Load parent from suggested item. - if (parent_to_select != nullptr) { - if (parent_to_select->kind() == RootItemKind::Category) { - m_ui->m_cmbParentCategory->setCurrentIndex(m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) parent_to_select))); - } - else if (parent_to_select->kind() == RootItemKind::Feed) { - int target_item = m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) parent_to_select->parent())); + else if (parent_to_select->kind() == RootItemKind::Feed) { + int target_item = m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) parent_to_select->parent())); - if (target_item >= 0) { - m_ui->m_cmbParentCategory->setCurrentIndex(target_item); - } - } - } - } - else { - // User is editing existing category. - setWindowTitle(tr("Edit existing category")); - setEditableCategory(input_category); - } + if (target_item >= 0) { + m_ui->m_cmbParentCategory->setCurrentIndex(target_item); + } + } + } + } - // Run the dialog. - return QDialog::exec(); + else { + // User is editing existing category. + setWindowTitle(tr("Edit existing category")); + setEditableCategory(input_category); + } + + // Run the dialog. + return QDialog::exec(); } void FormStandardCategoryDetails::apply() { - RootItem *parent = static_cast(m_ui->m_cmbParentCategory->itemData(m_ui->m_cmbParentCategory->currentIndex()).value()); - StandardCategory *new_category = new StandardCategory(); + RootItem* parent = static_cast(m_ui->m_cmbParentCategory->itemData(m_ui->m_cmbParentCategory->currentIndex()).value()); + StandardCategory* new_category = new StandardCategory(); + new_category->setTitle(m_ui->m_txtTitle->lineEdit()->text()); + new_category->setCreationDate(QDateTime::currentDateTime()); + new_category->setDescription(m_ui->m_txtDescription->lineEdit()->text()); + new_category->setIcon(m_ui->m_btnIcon->icon()); - new_category->setTitle(m_ui->m_txtTitle->lineEdit()->text()); - new_category->setCreationDate(QDateTime::currentDateTime()); - new_category->setDescription(m_ui->m_txtDescription->lineEdit()->text()); - new_category->setIcon(m_ui->m_btnIcon->icon()); + if (m_editableCategory == nullptr) { + // Add the category. + if (new_category->addItself(parent)) { + m_serviceRoot->requestItemReassignment(new_category, parent); + accept(); + } - if (m_editableCategory == nullptr) { - // Add the category. - if (new_category->addItself(parent)) { - m_serviceRoot->requestItemReassignment(new_category, parent); - accept(); - } - else { - delete new_category; - qApp->showGuiMessage(tr("Cannot add category"), - tr("Category was not added due to error."), - QSystemTrayIcon::Critical, - qApp->mainFormWidget(), true); - } - } - else { - new_category->setParent(parent); + else { + delete new_category; + qApp->showGuiMessage(tr("Cannot add category"), + tr("Category was not added due to error."), + QSystemTrayIcon::Critical, + qApp->mainFormWidget(), true); + } + } - bool edited = m_editableCategory->editItself(new_category); + else { + new_category->setParent(parent); + bool edited = m_editableCategory->editItself(new_category); - if (edited) { - m_serviceRoot->requestItemReassignment(m_editableCategory, new_category->parent()); - accept(); - } - else { - qApp->showGuiMessage(tr("Cannot edit category"), - tr("Category was not edited due to error."), - QSystemTrayIcon::Critical, this, true); - } + if (edited) { + m_serviceRoot->requestItemReassignment(m_editableCategory, new_category->parent()); + accept(); + } - delete new_category; - } + else { + qApp->showGuiMessage(tr("Cannot edit category"), + tr("Category was not edited due to error."), + QSystemTrayIcon::Critical, this, true); + } + + delete new_category; + } } -void FormStandardCategoryDetails::onTitleChanged(const QString &new_title){ - if (new_title.simplified().size() >= MIN_CATEGORY_NAME_LENGTH) { - m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); - m_ui->m_txtTitle->setStatus(WidgetWithStatus::Ok, tr("Category name is ok.")); - } - else { - m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - m_ui->m_txtTitle->setStatus(WidgetWithStatus::Error, tr("Category name is too short.")); - } +void FormStandardCategoryDetails::onTitleChanged(const QString& new_title) { + if (new_title.simplified().size() >= MIN_CATEGORY_NAME_LENGTH) { + m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); + m_ui->m_txtTitle->setStatus(WidgetWithStatus::Ok, tr("Category name is ok.")); + } + + else { + m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + m_ui->m_txtTitle->setStatus(WidgetWithStatus::Error, tr("Category name is too short.")); + } } -void FormStandardCategoryDetails::onDescriptionChanged(const QString &new_description) { - if (new_description.simplified().isEmpty()) { - m_ui->m_txtDescription->setStatus(LineEditWithStatus::Warning, tr("Description is empty.")); - } - else { - m_ui->m_txtDescription->setStatus(LineEditWithStatus::Ok, tr("The description is ok.")); - } +void FormStandardCategoryDetails::onDescriptionChanged(const QString& new_description) { + if (new_description.simplified().isEmpty()) { + m_ui->m_txtDescription->setStatus(LineEditWithStatus::Warning, tr("Description is empty.")); + } + + else { + m_ui->m_txtDescription->setStatus(LineEditWithStatus::Ok, tr("The description is ok.")); + } } void FormStandardCategoryDetails::onNoIconSelected() { - m_ui->m_btnIcon->setIcon(QIcon()); + m_ui->m_btnIcon->setIcon(QIcon()); } void FormStandardCategoryDetails::onLoadIconFromFile() { - QFileDialog dialog(this, tr("Select icon file for the category"), - qApp->getHomeFolderPath(), tr("Images (*.bmp *.jpg *.jpeg *.png *.svg *.tga)")); - dialog.setFileMode(QFileDialog::ExistingFile); - dialog.setWindowIcon(qApp->icons()->fromTheme(QSL("image-x-generic"))); - dialog.setOptions(QFileDialog::DontUseNativeDialog | QFileDialog::ReadOnly); - dialog.setViewMode(QFileDialog::Detail); - dialog.setLabelText(QFileDialog::Accept, tr("Select icon")); - dialog.setLabelText(QFileDialog::Reject, tr("Cancel")); - //: Label to describe the folder for icon file selection dialog. - dialog.setLabelText(QFileDialog::LookIn, tr("Look in:")); - dialog.setLabelText(QFileDialog::FileName, tr("Icon name:")); - dialog.setLabelText(QFileDialog::FileType, tr("Icon type:")); + QFileDialog dialog(this, tr("Select icon file for the category"), + qApp->getHomeFolderPath(), tr("Images (*.bmp *.jpg *.jpeg *.png *.svg *.tga)")); + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.setWindowIcon(qApp->icons()->fromTheme(QSL("image-x-generic"))); + dialog.setOptions(QFileDialog::DontUseNativeDialog | QFileDialog::ReadOnly); + dialog.setViewMode(QFileDialog::Detail); + dialog.setLabelText(QFileDialog::Accept, tr("Select icon")); + dialog.setLabelText(QFileDialog::Reject, tr("Cancel")); + //: Label to describe the folder for icon file selection dialog. + dialog.setLabelText(QFileDialog::LookIn, tr("Look in:")); + dialog.setLabelText(QFileDialog::FileName, tr("Icon name:")); + dialog.setLabelText(QFileDialog::FileType, tr("Icon type:")); - if (dialog.exec() == QDialog::Accepted) { - m_ui->m_btnIcon->setIcon(QIcon(dialog.selectedFiles().value(0))); - } + if (dialog.exec() == QDialog::Accepted) { + m_ui->m_btnIcon->setIcon(QIcon(dialog.selectedFiles().value(0))); + } } void FormStandardCategoryDetails::onUseDefaultIcon() { - m_ui->m_btnIcon->setIcon(qApp->icons()->fromTheme(QSL("folder"))); + m_ui->m_btnIcon->setIcon(qApp->icons()->fromTheme(QSL("folder"))); } void FormStandardCategoryDetails::initialize() { - m_ui.reset(new Ui::FormStandardCategoryDetails()); - m_ui->setupUi(this); - - // Set text boxes. - m_ui->m_txtTitle->lineEdit()->setPlaceholderText(tr("Category title")); - m_ui->m_txtTitle->lineEdit()->setToolTip(tr("Set title for your category.")); - - m_ui->m_txtDescription->lineEdit()->setPlaceholderText(tr("Category description")); - m_ui->m_txtDescription->lineEdit()->setToolTip(tr("Set description for your category.")); - - // Set flags and attributes. - setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint | Qt::WindowTitleHint); - setWindowIcon(qApp->icons()->fromTheme(QSL("folder"))); - - // Setup button box. - m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - - // Setup menu & actions for icon selection. - m_iconMenu = new QMenu(tr("Icon selection"), this); - m_actionLoadIconFromFile = new QAction(qApp->icons()->fromTheme(QSL("image-x-generic")), - tr("Load icon from file..."), - this); - m_actionNoIcon = new QAction(qApp->icons()->fromTheme(QSL("dialog-error")), - tr("Do not use icon"), - this); - m_actionUseDefaultIcon = new QAction(qApp->icons()->fromTheme(QSL("folder")), - tr("Use default icon"), - this); - m_iconMenu->addAction(m_actionLoadIconFromFile); - m_iconMenu->addAction(m_actionUseDefaultIcon); - m_iconMenu->addAction(m_actionNoIcon); - m_ui->m_btnIcon->setMenu(m_iconMenu); - - // Setup tab order. - setTabOrder(m_ui->m_cmbParentCategory, m_ui->m_txtTitle->lineEdit()); - setTabOrder(m_ui->m_txtTitle->lineEdit(), m_ui->m_txtDescription->lineEdit()); - setTabOrder(m_ui->m_txtDescription->lineEdit(), m_ui->m_btnIcon); - setTabOrder(m_ui->m_btnIcon, m_ui->m_buttonBox); - - m_ui->m_txtTitle->lineEdit()->setFocus(Qt::TabFocusReason); + m_ui.reset(new Ui::FormStandardCategoryDetails()); + m_ui->setupUi(this); + // Set text boxes. + m_ui->m_txtTitle->lineEdit()->setPlaceholderText(tr("Category title")); + m_ui->m_txtTitle->lineEdit()->setToolTip(tr("Set title for your category.")); + m_ui->m_txtDescription->lineEdit()->setPlaceholderText(tr("Category description")); + m_ui->m_txtDescription->lineEdit()->setToolTip(tr("Set description for your category.")); + // Set flags and attributes. + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint | Qt::WindowTitleHint); + setWindowIcon(qApp->icons()->fromTheme(QSL("folder"))); + // Setup button box. + m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + // Setup menu & actions for icon selection. + m_iconMenu = new QMenu(tr("Icon selection"), this); + m_actionLoadIconFromFile = new QAction(qApp->icons()->fromTheme(QSL("image-x-generic")), + tr("Load icon from file..."), + this); + m_actionNoIcon = new QAction(qApp->icons()->fromTheme(QSL("dialog-error")), + tr("Do not use icon"), + this); + m_actionUseDefaultIcon = new QAction(qApp->icons()->fromTheme(QSL("folder")), + tr("Use default icon"), + this); + m_iconMenu->addAction(m_actionLoadIconFromFile); + m_iconMenu->addAction(m_actionUseDefaultIcon); + m_iconMenu->addAction(m_actionNoIcon); + m_ui->m_btnIcon->setMenu(m_iconMenu); + // Setup tab order. + setTabOrder(m_ui->m_cmbParentCategory, m_ui->m_txtTitle->lineEdit()); + setTabOrder(m_ui->m_txtTitle->lineEdit(), m_ui->m_txtDescription->lineEdit()); + setTabOrder(m_ui->m_txtDescription->lineEdit(), m_ui->m_btnIcon); + setTabOrder(m_ui->m_btnIcon, m_ui->m_buttonBox); + m_ui->m_txtTitle->lineEdit()->setFocus(Qt::TabFocusReason); } void FormStandardCategoryDetails::loadCategories(const QList categories, - RootItem *root_item, - StandardCategory *input_category) { - m_ui->m_cmbParentCategory->addItem(root_item->icon(), - root_item->title(), - QVariant::fromValue((void*) root_item)); + RootItem* root_item, + StandardCategory* input_category) { + m_ui->m_cmbParentCategory->addItem(root_item->icon(), + root_item->title(), + QVariant::fromValue((void*) root_item)); - foreach (Category *category, categories) { - if (input_category != nullptr && (category == input_category || category->isChildOf(input_category))) { - // This category cannot be selected as the new - // parent for currently edited category, so - // don't add it. - continue; - } + foreach (Category* category, categories) { + if (input_category != nullptr && (category == input_category || category->isChildOf(input_category))) { + // This category cannot be selected as the new + // parent for currently edited category, so + // don't add it. + continue; + } - m_ui->m_cmbParentCategory->addItem(category->data(FDS_MODEL_TITLE_INDEX, Qt::DecorationRole).value(), - category->title(), - QVariant::fromValue((void*) category)); - } + m_ui->m_cmbParentCategory->addItem(category->data(FDS_MODEL_TITLE_INDEX, Qt::DecorationRole).value(), + category->title(), + QVariant::fromValue((void*) category)); + } } diff --git a/src/services/standard/gui/formstandardcategorydetails.h b/src/services/standard/gui/formstandardcategorydetails.h index d6fdb30ef..f52578a74 100755 --- a/src/services/standard/gui/formstandardcategorydetails.h +++ b/src/services/standard/gui/formstandardcategorydetails.h @@ -24,7 +24,7 @@ namespace Ui { - class FormStandardCategoryDetails; + class FormStandardCategoryDetails; } class Category; @@ -36,54 +36,54 @@ class QMenu; class QAction; class FormStandardCategoryDetails : public QDialog { - Q_OBJECT + Q_OBJECT - public: - // Constructors and destructors. - explicit FormStandardCategoryDetails(StandardServiceRoot *service_root, QWidget *parent = 0); - virtual ~FormStandardCategoryDetails(); + public: + // Constructors and destructors. + explicit FormStandardCategoryDetails(StandardServiceRoot* service_root, QWidget* parent = 0); + virtual ~FormStandardCategoryDetails(); - public slots: - // Executes add/edit standard category dialog. - int addEditCategory(StandardCategory *input_category, RootItem *parent_to_select); + public slots: + // Executes add/edit standard category dialog. + int addEditCategory(StandardCategory* input_category, RootItem* parent_to_select); - protected slots: - // Applies changes. - void apply(); + protected slots: + // Applies changes. + void apply(); - // Trigerred when title/description changes. - void onTitleChanged(const QString &new_title); - void onDescriptionChanged(const QString &new_description); + // Trigerred when title/description changes. + void onTitleChanged(const QString& new_title); + void onDescriptionChanged(const QString& new_description); - // Icon selectors. - void onNoIconSelected(); - void onLoadIconFromFile(); - void onUseDefaultIcon(); + // Icon selectors. + void onNoIconSelected(); + void onLoadIconFromFile(); + void onUseDefaultIcon(); - protected: - // Creates needed connections. - void createConnections(); + protected: + // Creates needed connections. + void createConnections(); - // Sets the category which will be edited. - void setEditableCategory(StandardCategory *editable_category); + // Sets the category which will be edited. + void setEditableCategory(StandardCategory* editable_category); - // Initializes the dialog. - void initialize(); + // Initializes the dialog. + void initialize(); - // Loads categories into the dialog + give root "category" - // and make sure that no childs of input category (including) - // input category are loaded. - void loadCategories(const QList categories, RootItem *root_item, StandardCategory *input_category); + // Loads categories into the dialog + give root "category" + // and make sure that no childs of input category (including) + // input category are loaded. + void loadCategories(const QList categories, RootItem* root_item, StandardCategory* input_category); - private: - QScopedPointer m_ui; - StandardCategory *m_editableCategory; - StandardServiceRoot *m_serviceRoot; + private: + QScopedPointer m_ui; + StandardCategory* m_editableCategory; + StandardServiceRoot* m_serviceRoot; - QMenu *m_iconMenu; - QAction *m_actionLoadIconFromFile; - QAction *m_actionUseDefaultIcon; - QAction *m_actionNoIcon; + QMenu* m_iconMenu; + QAction* m_actionLoadIconFromFile; + QAction* m_actionUseDefaultIcon; + QAction* m_actionNoIcon; }; #endif // FORMCATEGORYDETAILS_H diff --git a/src/services/standard/gui/formstandardfeeddetails.cpp b/src/services/standard/gui/formstandardfeeddetails.cpp index 09dc1d853..9f01ae692 100755 --- a/src/services/standard/gui/formstandardfeeddetails.cpp +++ b/src/services/standard/gui/formstandardfeeddetails.cpp @@ -1,91 +1,90 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/standard/gui/formstandardfeeddetails.h" - -#include "services/standard/standardfeed.h" -#include "services/abstract/serviceroot.h" -#include "miscellaneous/application.h" - - -FormStandardFeedDetails::FormStandardFeedDetails(ServiceRoot *service_root, QWidget *parent) - : FormFeedDetails(service_root, parent) { -} - -void FormStandardFeedDetails::apply() { - RootItem *parent = static_cast(m_ui->m_cmbParentCategory->itemData(m_ui->m_cmbParentCategory->currentIndex()).value()); - StandardFeed::Type type = static_cast(m_ui->m_cmbType->itemData(m_ui->m_cmbType->currentIndex()).value()); - StandardFeed *new_feed = new StandardFeed(); - - // Setup data for new_feed. - new_feed->setTitle(m_ui->m_txtTitle->lineEdit()->text()); - new_feed->setCreationDate(QDateTime::currentDateTime()); - new_feed->setDescription(m_ui->m_txtDescription->lineEdit()->text()); - new_feed->setIcon(m_ui->m_btnIcon->icon()); - new_feed->setEncoding(m_ui->m_cmbEncoding->currentText()); - new_feed->setType(type); - new_feed->setUrl(m_ui->m_txtUrl->lineEdit()->text()); - new_feed->setPasswordProtected(m_ui->m_gbAuthentication->isChecked()); - new_feed->setUsername(m_ui->m_txtUsername->lineEdit()->text()); - new_feed->setPassword(m_ui->m_txtPassword->lineEdit()->text()); - new_feed->setAutoUpdateType(static_cast(m_ui->m_cmbAutoUpdateType->itemData(m_ui->m_cmbAutoUpdateType->currentIndex()).toInt())); - new_feed->setAutoUpdateInitialInterval(m_ui->m_spinAutoUpdateInterval->value()); - - if (m_editableFeed == nullptr) { - // Add the feed. - if (new_feed->addItself(parent)) { - m_serviceRoot->requestItemReassignment(new_feed, parent); - accept(); - } - else { - delete new_feed; - qApp->showGuiMessage(tr("Cannot add feed"), - tr("Feed was not added due to error."), - QSystemTrayIcon::Critical, this, true); - } - } - else { - new_feed->setParent(parent); - - // Edit the feed. - bool edited = qobject_cast(m_editableFeed)->editItself(new_feed); - - if (edited) { - m_serviceRoot->requestItemReassignment(m_editableFeed, new_feed->parent()); - accept(); - } - else { - qApp->showGuiMessage(tr("Cannot edit feed"), - tr("Feed was not edited due to error."), - QSystemTrayIcon::Critical, this, true); - } - - delete new_feed; - } -} - -void FormStandardFeedDetails::setEditableFeed(Feed *editable_feed) { - FormFeedDetails::setEditableFeed(editable_feed); - - StandardFeed *feed = qobject_cast(editable_feed); - - m_ui->m_cmbType->setCurrentIndex(m_ui->m_cmbType->findData(QVariant::fromValue((int) feed->type()))); - m_ui->m_cmbEncoding->setCurrentIndex(m_ui->m_cmbEncoding->findData(feed->encoding(), Qt::DisplayRole, Qt::MatchFixedString)); - m_ui->m_gbAuthentication->setChecked(feed->passwordProtected()); - m_ui->m_txtUsername->lineEdit()->setText(feed->username()); - m_ui->m_txtPassword->lineEdit()->setText(feed->password()); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/standard/gui/formstandardfeeddetails.h" + +#include "services/standard/standardfeed.h" +#include "services/abstract/serviceroot.h" +#include "miscellaneous/application.h" + + +FormStandardFeedDetails::FormStandardFeedDetails(ServiceRoot* service_root, QWidget* parent) + : FormFeedDetails(service_root, parent) { +} + +void FormStandardFeedDetails::apply() { + RootItem* parent = static_cast(m_ui->m_cmbParentCategory->itemData(m_ui->m_cmbParentCategory->currentIndex()).value()); + StandardFeed::Type type = static_cast(m_ui->m_cmbType->itemData(m_ui->m_cmbType->currentIndex()).value()); + StandardFeed* new_feed = new StandardFeed(); + // Setup data for new_feed. + new_feed->setTitle(m_ui->m_txtTitle->lineEdit()->text()); + new_feed->setCreationDate(QDateTime::currentDateTime()); + new_feed->setDescription(m_ui->m_txtDescription->lineEdit()->text()); + new_feed->setIcon(m_ui->m_btnIcon->icon()); + new_feed->setEncoding(m_ui->m_cmbEncoding->currentText()); + new_feed->setType(type); + new_feed->setUrl(m_ui->m_txtUrl->lineEdit()->text()); + new_feed->setPasswordProtected(m_ui->m_gbAuthentication->isChecked()); + new_feed->setUsername(m_ui->m_txtUsername->lineEdit()->text()); + new_feed->setPassword(m_ui->m_txtPassword->lineEdit()->text()); + new_feed->setAutoUpdateType(static_cast(m_ui->m_cmbAutoUpdateType->itemData(m_ui->m_cmbAutoUpdateType->currentIndex()).toInt())); + new_feed->setAutoUpdateInitialInterval(m_ui->m_spinAutoUpdateInterval->value()); + + if (m_editableFeed == nullptr) { + // Add the feed. + if (new_feed->addItself(parent)) { + m_serviceRoot->requestItemReassignment(new_feed, parent); + accept(); + } + + else { + delete new_feed; + qApp->showGuiMessage(tr("Cannot add feed"), + tr("Feed was not added due to error."), + QSystemTrayIcon::Critical, this, true); + } + } + + else { + new_feed->setParent(parent); + // Edit the feed. + bool edited = qobject_cast(m_editableFeed)->editItself(new_feed); + + if (edited) { + m_serviceRoot->requestItemReassignment(m_editableFeed, new_feed->parent()); + accept(); + } + + else { + qApp->showGuiMessage(tr("Cannot edit feed"), + tr("Feed was not edited due to error."), + QSystemTrayIcon::Critical, this, true); + } + + delete new_feed; + } +} + +void FormStandardFeedDetails::setEditableFeed(Feed* editable_feed) { + FormFeedDetails::setEditableFeed(editable_feed); + StandardFeed* feed = qobject_cast(editable_feed); + m_ui->m_cmbType->setCurrentIndex(m_ui->m_cmbType->findData(QVariant::fromValue((int) feed->type()))); + m_ui->m_cmbEncoding->setCurrentIndex(m_ui->m_cmbEncoding->findData(feed->encoding(), Qt::DisplayRole, Qt::MatchFixedString)); + m_ui->m_gbAuthentication->setChecked(feed->passwordProtected()); + m_ui->m_txtUsername->lineEdit()->setText(feed->username()); + m_ui->m_txtPassword->lineEdit()->setText(feed->password()); +} diff --git a/src/services/standard/gui/formstandardfeeddetails.h b/src/services/standard/gui/formstandardfeeddetails.h index 21298fa1c..7e968b2b7 100755 --- a/src/services/standard/gui/formstandardfeeddetails.h +++ b/src/services/standard/gui/formstandardfeeddetails.h @@ -1,37 +1,37 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FORMSSFEEDDETAILS_H -#define FORMSSFEEDDETAILS_H - -#include "services/abstract/gui/formfeeddetails.h" - - -class FormStandardFeedDetails : public FormFeedDetails { - Q_OBJECT - - public: - explicit FormStandardFeedDetails(ServiceRoot *service_root, QWidget *parent = 0); - - protected slots: - void apply(); - - protected: - void setEditableFeed(Feed *editable_feed); -}; - -#endif // FORMSSFEEDDETAILS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FORMSSFEEDDETAILS_H +#define FORMSSFEEDDETAILS_H + +#include "services/abstract/gui/formfeeddetails.h" + + +class FormStandardFeedDetails : public FormFeedDetails { + Q_OBJECT + + public: + explicit FormStandardFeedDetails(ServiceRoot* service_root, QWidget* parent = 0); + + protected slots: + void apply(); + + protected: + void setEditableFeed(Feed* editable_feed); +}; + +#endif // FORMSSFEEDDETAILS_H diff --git a/src/services/standard/gui/formstandardimportexport.cpp b/src/services/standard/gui/formstandardimportexport.cpp index 47b230b38..58ef4730a 100755 --- a/src/services/standard/gui/formstandardimportexport.cpp +++ b/src/services/standard/gui/formstandardimportexport.cpp @@ -33,285 +33,277 @@ #include -FormStandardImportExport::FormStandardImportExport(StandardServiceRoot *service_root, QWidget *parent) - : QDialog(parent), m_ui(new Ui::FormStandardImportExport), m_serviceRoot(service_root) { - m_ui->setupUi(this); - m_model = new FeedsImportExportModel(m_ui->m_treeFeeds); - - connect(m_model, &FeedsImportExportModel::parsingStarted, this, &FormStandardImportExport::onParsingStarted); - connect(m_model, &FeedsImportExportModel::parsingFinished, this, &FormStandardImportExport::onParsingFinished); - connect(m_model, &FeedsImportExportModel::parsingProgress, this, &FormStandardImportExport::onParsingProgress); - - setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); - - m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::Error, tr("No file is selected."), tr("No file is selected.")); - m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->disconnect(); - m_ui->m_lblResult->setStatus(WidgetWithStatus::Warning, tr("No operation executed yet."), tr("No operation executed yet.")); - - connect(m_ui->m_buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &FormStandardImportExport::performAction); - connect(m_ui->m_btnSelectFile, &QPushButton::clicked, this, &FormStandardImportExport::selectFile); - connect(m_ui->m_btnCheckAllItems, &QPushButton::clicked, m_model, &FeedsImportExportModel::checkAllItems); - connect(m_ui->m_btnUncheckAllItems, &QPushButton::clicked, m_model, &FeedsImportExportModel::uncheckAllItems); +FormStandardImportExport::FormStandardImportExport(StandardServiceRoot* service_root, QWidget* parent) + : QDialog(parent), m_ui(new Ui::FormStandardImportExport), m_serviceRoot(service_root) { + m_ui->setupUi(this); + m_model = new FeedsImportExportModel(m_ui->m_treeFeeds); + connect(m_model, &FeedsImportExportModel::parsingStarted, this, &FormStandardImportExport::onParsingStarted); + connect(m_model, &FeedsImportExportModel::parsingFinished, this, &FormStandardImportExport::onParsingFinished); + connect(m_model, &FeedsImportExportModel::parsingProgress, this, &FormStandardImportExport::onParsingProgress); + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); + m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::Error, tr("No file is selected."), tr("No file is selected.")); + m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->disconnect(); + m_ui->m_lblResult->setStatus(WidgetWithStatus::Warning, tr("No operation executed yet."), tr("No operation executed yet.")); + connect(m_ui->m_buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &FormStandardImportExport::performAction); + connect(m_ui->m_btnSelectFile, &QPushButton::clicked, this, &FormStandardImportExport::selectFile); + connect(m_ui->m_btnCheckAllItems, &QPushButton::clicked, m_model, &FeedsImportExportModel::checkAllItems); + connect(m_ui->m_btnUncheckAllItems, &QPushButton::clicked, m_model, &FeedsImportExportModel::uncheckAllItems); } FormStandardImportExport::~FormStandardImportExport() { } -void FormStandardImportExport::setMode(const FeedsImportExportModel::Mode &mode) { - m_model->setMode(mode); - m_ui->m_progressBar->setVisible(false); +void FormStandardImportExport::setMode(const FeedsImportExportModel::Mode& mode) { + m_model->setMode(mode); + m_ui->m_progressBar->setVisible(false); - switch (mode) { - case FeedsImportExportModel::Export: { - m_model->setRootItem(m_serviceRoot); - m_model->checkAllItems(); - m_ui->m_treeFeeds->setModel(m_model); - m_ui->m_treeFeeds->expandAll(); - m_ui->m_cmbRootNode->setVisible(false); - m_ui->m_lblRootNode->setVisible(false); - m_ui->m_groupFile->setTitle(tr("Destination file")); - m_ui->m_groupFeeds->setTitle(tr("Source feeds && categories")); - setWindowTitle(tr("Export feeds")); - setWindowIcon(qApp->icons()->fromTheme(QSL("document-export"))); - break; - } + switch (mode) { + case FeedsImportExportModel::Export: { + m_model->setRootItem(m_serviceRoot); + m_model->checkAllItems(); + m_ui->m_treeFeeds->setModel(m_model); + m_ui->m_treeFeeds->expandAll(); + m_ui->m_cmbRootNode->setVisible(false); + m_ui->m_lblRootNode->setVisible(false); + m_ui->m_groupFile->setTitle(tr("Destination file")); + m_ui->m_groupFeeds->setTitle(tr("Source feeds && categories")); + setWindowTitle(tr("Export feeds")); + setWindowIcon(qApp->icons()->fromTheme(QSL("document-export"))); + break; + } - case FeedsImportExportModel::Import: { - m_ui->m_groupFile->setTitle(tr("Source file")); - m_ui->m_groupFeeds->setTitle(tr("Target feeds && categories")); - m_ui->m_groupFeeds->setDisabled(true); + case FeedsImportExportModel::Import: { + m_ui->m_groupFile->setTitle(tr("Source file")); + m_ui->m_groupFeeds->setTitle(tr("Target feeds && categories")); + m_ui->m_groupFeeds->setDisabled(true); + // Load categories. + loadCategories(m_serviceRoot->getSubTreeCategories(), m_serviceRoot); + setWindowTitle(tr("Import feeds")); + setWindowIcon(qApp->icons()->fromTheme(QSL("document-import"))); + break; + } - // Load categories. - loadCategories(m_serviceRoot->getSubTreeCategories(), m_serviceRoot); - setWindowTitle(tr("Import feeds")); - setWindowIcon(qApp->icons()->fromTheme(QSL("document-import"))); - break; - } + default: + break; + } - default: - break; - } - - m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } void FormStandardImportExport::selectFile() { - switch (m_model->mode()) { - case FeedsImportExportModel::Import: - selectImportFile(); - break; + switch (m_model->mode()) { + case FeedsImportExportModel::Import: + selectImportFile(); + break; - case FeedsImportExportModel::Export: { - selectExportFile(); - break; - } + case FeedsImportExportModel::Export: { + selectExportFile(); + break; + } - default: - break; - } + default: + break; + } } void FormStandardImportExport::onParsingStarted() { - m_ui->m_lblResult->setStatus(WidgetWithStatus::Progress, tr("Parsing data..."), tr("Parsing data...")); - m_ui->m_btnSelectFile->setEnabled(false); - m_ui->m_groupFeeds->setEnabled(false); - m_ui->m_progressBar->setValue(0); - m_ui->m_progressBar->setVisible(true); - - m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + m_ui->m_lblResult->setStatus(WidgetWithStatus::Progress, tr("Parsing data..."), tr("Parsing data...")); + m_ui->m_btnSelectFile->setEnabled(false); + m_ui->m_groupFeeds->setEnabled(false); + m_ui->m_progressBar->setValue(0); + m_ui->m_progressBar->setVisible(true); + m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } void FormStandardImportExport::onParsingFinished(int count_failed, int count_succeeded, bool parsing_error) { - Q_UNUSED(count_failed) - Q_UNUSED(count_succeeded) + Q_UNUSED(count_failed) + Q_UNUSED(count_succeeded) + m_ui->m_progressBar->setVisible(false); + m_ui->m_progressBar->setValue(0); + m_model->checkAllItems(); - m_ui->m_progressBar->setVisible(false); - m_ui->m_progressBar->setValue(0); - m_model->checkAllItems(); + if (!parsing_error) { + m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, tr("Feeds were loaded."), tr("Feeds were loaded.")); + m_ui->m_groupFeeds->setEnabled(true); + m_ui->m_btnSelectFile->setEnabled(true); + m_ui->m_treeFeeds->setModel(m_model); + m_ui->m_treeFeeds->expandAll(); + } - if (!parsing_error) { - m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, tr("Feeds were loaded."), tr("Feeds were loaded.")); - m_ui->m_groupFeeds->setEnabled(true); - m_ui->m_btnSelectFile->setEnabled(true); - m_ui->m_treeFeeds->setModel(m_model); - m_ui->m_treeFeeds->expandAll(); - } - else { - m_ui->m_groupFeeds->setEnabled(false); - m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Error, file is not well-formed. Select another file."), - tr("Error occurred. File is not well-formed. Select another file.")); - } + else { + m_ui->m_groupFeeds->setEnabled(false); + m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Error, file is not well-formed. Select another file."), + tr("Error occurred. File is not well-formed. Select another file.")); + } - m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); + m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } void FormStandardImportExport::onParsingProgress(int completed, int total) { - m_ui->m_progressBar->setMaximum(total); - m_ui->m_progressBar->setValue(completed); + m_ui->m_progressBar->setMaximum(total); + m_ui->m_progressBar->setValue(completed); } void FormStandardImportExport::selectExportFile() { - const QString filter_opml20 = tr("OPML 2.0 files (*.opml)"); - const QString filter_txt_url_per_line = tr("TXT files [one URL per line] (*.txt)"); + const QString filter_opml20 = tr("OPML 2.0 files (*.opml)"); + const QString filter_txt_url_per_line = tr("TXT files [one URL per line] (*.txt)"); + QString filter; + QString selected_filter; + // Add more filters here. + filter += filter_opml20; + filter += ";;"; + filter += filter_txt_url_per_line; + QString selected_file = QFileDialog::getSaveFileName(this, tr("Select file for feeds export"), + qApp->getHomeFolderPath(), filter, &selected_filter); - QString filter; - QString selected_filter; + if (!selected_file.isEmpty()) { + if (selected_filter == filter_opml20) { + m_conversionType = OPML20; - // Add more filters here. - filter += filter_opml20; - filter += ";;"; - filter += filter_txt_url_per_line; + if (!selected_file.endsWith(QL1S(".opml"))) { + selected_file += QL1S(".opml"); + } + } - QString selected_file = QFileDialog::getSaveFileName(this, tr("Select file for feeds export"), - qApp->getHomeFolderPath(), filter, &selected_filter); + else if (selected_filter == filter_txt_url_per_line) { + m_conversionType = TXTUrlPerLine; - if (!selected_file.isEmpty()) { - if (selected_filter == filter_opml20) { - m_conversionType = OPML20; + if (!selected_file.endsWith(QL1S(".txt"))) { + selected_file += QL1S(".txt"); + } + } - if (!selected_file.endsWith(QL1S(".opml"))) { - selected_file += QL1S(".opml"); - } - } - else if (selected_filter == filter_txt_url_per_line) { - m_conversionType = TXTUrlPerLine; + m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::Ok, QDir::toNativeSeparators(selected_file), tr("File is selected.")); + } - if (!selected_file.endsWith(QL1S(".txt"))) { - selected_file += QL1S(".txt"); - } - } - - m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::Ok, QDir::toNativeSeparators(selected_file), tr("File is selected.")); - } - - m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(m_ui->m_lblSelectFile->status() == WidgetWithStatus::Ok); + m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(m_ui->m_lblSelectFile->status() == WidgetWithStatus::Ok); } void FormStandardImportExport::selectImportFile() { - const QString filter_opml20 = tr("OPML 2.0 files (*.opml)"); - const QString filter_txt_url_per_line = tr("TXT files [one URL per line] (*.txt)"); + const QString filter_opml20 = tr("OPML 2.0 files (*.opml)"); + const QString filter_txt_url_per_line = tr("TXT files [one URL per line] (*.txt)"); + QString filter; + QString selected_filter; + // Add more filters here. + filter += filter_opml20; + filter += ";;"; + filter += filter_txt_url_per_line; + const QString selected_file = QFileDialog::getOpenFileName(this, tr("Select file for feeds import"), qApp->getHomeFolderPath(), + filter, &selected_filter); - QString filter; - QString selected_filter; + if (!selected_file.isEmpty()) { + if (selected_filter == filter_opml20) { + m_conversionType = OPML20; + } - // Add more filters here. - filter += filter_opml20; - filter += ";;"; - filter += filter_txt_url_per_line; + else if (selected_filter == filter_txt_url_per_line) { + m_conversionType = TXTUrlPerLine; + } - const QString selected_file = QFileDialog::getOpenFileName(this, tr("Select file for feeds import"), qApp->getHomeFolderPath(), - filter, &selected_filter); - - if (!selected_file.isEmpty()) { - if (selected_filter == filter_opml20) { - m_conversionType = OPML20; - } - else if (selected_filter == filter_txt_url_per_line) { - m_conversionType = TXTUrlPerLine; - } - - m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::Ok, QDir::toNativeSeparators(selected_file), tr("File is selected.")); - - QMessageBox::StandardButton answer = MessageBox::show(this, QMessageBox::Warning, tr("Get online metadata"), - tr("Metadata for your feeds can be fetched online. Note that the action " - "could take several minutes, depending on number of feeds."), - tr("Do you want to fetch feed metadata online?"), QString(), QMessageBox::Yes | QMessageBox::No, - QMessageBox::Yes); - - parseImportFile(selected_file, answer == QMessageBox::Yes); - } + m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::Ok, QDir::toNativeSeparators(selected_file), tr("File is selected.")); + QMessageBox::StandardButton answer = MessageBox::show(this, QMessageBox::Warning, tr("Get online metadata"), + tr("Metadata for your feeds can be fetched online. Note that the action " + "could take several minutes, depending on number of feeds."), + tr("Do you want to fetch feed metadata online?"), QString(), QMessageBox::Yes | QMessageBox::No, + QMessageBox::Yes); + parseImportFile(selected_file, answer == QMessageBox::Yes); + } } -void FormStandardImportExport::parseImportFile(const QString &file_name, bool fetch_metadata_online) { - QFile input_file(file_name); - QByteArray input_data; +void FormStandardImportExport::parseImportFile(const QString& file_name, bool fetch_metadata_online) { + QFile input_file(file_name); + QByteArray input_data; - if (input_file.open(QIODevice::Text | QIODevice::Unbuffered | QIODevice::ReadOnly)) { - input_data = input_file.readAll(); - input_file.close(); - } - else { - m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Cannot open source file."), tr("Cannot open source file.")); - return; - } + if (input_file.open(QIODevice::Text | QIODevice::Unbuffered | QIODevice::ReadOnly)) { + input_data = input_file.readAll(); + input_file.close(); + } - switch (m_conversionType) { - case OPML20: - m_model->importAsOPML20(input_data, fetch_metadata_online); - break; + else { + m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Cannot open source file."), tr("Cannot open source file.")); + return; + } - case TXTUrlPerLine: - m_model->importAsTxtURLPerLine(input_data, fetch_metadata_online); - break; + switch (m_conversionType) { + case OPML20: + m_model->importAsOPML20(input_data, fetch_metadata_online); + break; - default: - return; - } + case TXTUrlPerLine: + m_model->importAsTxtURLPerLine(input_data, fetch_metadata_online); + break; + + default: + return; + } } void FormStandardImportExport::performAction() { - switch (m_model->mode()) { - case FeedsImportExportModel::Import: - importFeeds(); - break; + switch (m_model->mode()) { + case FeedsImportExportModel::Import: + importFeeds(); + break; - case FeedsImportExportModel::Export: - exportFeeds(); - break; + case FeedsImportExportModel::Export: + exportFeeds(); + break; - default: - break; - } + default: + break; + } } void FormStandardImportExport::exportFeeds() { - QByteArray result_data; - bool result_export = false; + QByteArray result_data; + bool result_export = false; - switch (m_conversionType) { - case OPML20: - result_export = m_model->exportToOMPL20(result_data); - break; + switch (m_conversionType) { + case OPML20: + result_export = m_model->exportToOMPL20(result_data); + break; - case TXTUrlPerLine: - result_export = m_model->exportToTxtURLPerLine(result_data); - break; + case TXTUrlPerLine: + result_export = m_model->exportToTxtURLPerLine(result_data); + break; - default: - break; - } + default: + break; + } - if (result_export) { - try { - IOFactory::writeTextFile(m_ui->m_lblSelectFile->label()->text(), result_data); - m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, tr("Feeds were exported successfully."), tr("Feeds were exported successfully.")); - } - catch (IOException &ex) { - m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Cannot write into destination file: '%1'."), ex.message()); - } - } - else { - m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Critical error occurred."), tr("Critical error occurred.")); - } + if (result_export) { + try { + IOFactory::writeTextFile(m_ui->m_lblSelectFile->label()->text(), result_data); + m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, tr("Feeds were exported successfully."), tr("Feeds were exported successfully.")); + } + + catch (IOException& ex) { + m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Cannot write into destination file: '%1'."), ex.message()); + } + } + + else { + m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Critical error occurred."), tr("Critical error occurred.")); + } } void FormStandardImportExport::importFeeds() { - QString output_message; - RootItem *parent = static_cast(m_ui->m_cmbRootNode->itemData(m_ui->m_cmbRootNode->currentIndex()).value()); + QString output_message; + RootItem* parent = static_cast(m_ui->m_cmbRootNode->itemData(m_ui->m_cmbRootNode->currentIndex()).value()); - if (m_serviceRoot->mergeImportExportModel(m_model, parent, output_message)) { - m_serviceRoot->requestItemExpand(parent->getSubTree(), true); - m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, output_message, output_message); - } - else { - m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, output_message, output_message); - } + if (m_serviceRoot->mergeImportExportModel(m_model, parent, output_message)) { + m_serviceRoot->requestItemExpand(parent->getSubTree(), true); + m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, output_message, output_message); + } + + else { + m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, output_message, output_message); + } } -void FormStandardImportExport::loadCategories(const QList categories, RootItem *root_item) { - m_ui->m_cmbRootNode->addItem(root_item->icon(), root_item->title(), QVariant::fromValue((void*) root_item)); +void FormStandardImportExport::loadCategories(const QList categories, RootItem* root_item) { + m_ui->m_cmbRootNode->addItem(root_item->icon(), root_item->title(), QVariant::fromValue((void*) root_item)); - foreach (Category *category, categories) { - m_ui->m_cmbRootNode->addItem(category->icon(), category->title(), QVariant::fromValue((void*) category)); - } + foreach (Category* category, categories) { + m_ui->m_cmbRootNode->addItem(category->icon(), category->title(), QVariant::fromValue((void*) category)); + } } diff --git a/src/services/standard/gui/formstandardimportexport.h b/src/services/standard/gui/formstandardimportexport.h index 46dc264e1..9806c0a78 100755 --- a/src/services/standard/gui/formstandardimportexport.h +++ b/src/services/standard/gui/formstandardimportexport.h @@ -24,49 +24,49 @@ #include "services/standard/standardfeedsimportexportmodel.h" namespace Ui { - class FormStandardImportExport; + class FormStandardImportExport; } class Category; class StandardServiceRoot; class FormStandardImportExport : public QDialog { - Q_OBJECT + Q_OBJECT - public: - enum ConversionType { - OPML20 = 0, - TXTUrlPerLine = 1 - }; + public: + enum ConversionType { + OPML20 = 0, + TXTUrlPerLine = 1 + }; - // Constructors. - explicit FormStandardImportExport(StandardServiceRoot *service_root, QWidget *parent = 0); - virtual ~FormStandardImportExport(); + // Constructors. + explicit FormStandardImportExport(StandardServiceRoot* service_root, QWidget* parent = 0); + virtual ~FormStandardImportExport(); - void setMode(const FeedsImportExportModel::Mode &mode); + void setMode(const FeedsImportExportModel::Mode& mode); - private slots: - void performAction(); - void selectFile(); + private slots: + void performAction(); + void selectFile(); - void onParsingStarted(); - void onParsingFinished(int count_failed, int count_succeeded, bool parsing_error); - void onParsingProgress(int completed, int total); + void onParsingStarted(); + void onParsingFinished(int count_failed, int count_succeeded, bool parsing_error); + void onParsingProgress(int completed, int total); - private: - void selectExportFile(); - void selectImportFile(); - void parseImportFile(const QString &file_name, bool fetch_metadata_online); + private: + void selectExportFile(); + void selectImportFile(); + void parseImportFile(const QString& file_name, bool fetch_metadata_online); - void exportFeeds(); - void importFeeds(); + void exportFeeds(); + void importFeeds(); - void loadCategories(const QList categories, RootItem *root_item); + void loadCategories(const QList categories, RootItem* root_item); - QScopedPointer m_ui; - ConversionType m_conversionType; - FeedsImportExportModel *m_model; - StandardServiceRoot *m_serviceRoot; + QScopedPointer m_ui; + ConversionType m_conversionType; + FeedsImportExportModel* m_model; + StandardServiceRoot* m_serviceRoot; }; #endif // FORMEXPORT_H diff --git a/src/services/standard/rdfparser.cpp b/src/services/standard/rdfparser.cpp old mode 100644 new mode 100755 index bce9ad952..c3f1e914f --- a/src/services/standard/rdfparser.cpp +++ b/src/services/standard/rdfparser.cpp @@ -1,101 +1,98 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/standard/rdfparser.h" - -#include "miscellaneous/textfactory.h" -#include "network-web/webfactory.h" - -#include - - -RdfParser::RdfParser() { -} - -RdfParser::~RdfParser() { -} - -QList RdfParser::parseXmlData(const QString &data) { - QList messages; - QDomDocument xml_file; - QDateTime current_time = QDateTime::currentDateTime(); - - xml_file.setContent(data, true); - - // Pull out all messages. - QDomNodeList messages_in_xml = xml_file.elementsByTagName(QSL("item")); - - for (int i = 0; i < messages_in_xml.size(); i++) { - QDomNode message_item = messages_in_xml.item(i); - Message new_message; - - // Deal with title and description. - QString elem_title = message_item.namedItem(QSL("title")).toElement().text().simplified(); - QString elem_description = message_item.namedItem(QSL("description")).toElement().text(); - - // Now we obtained maximum of information for title & description. - if (elem_title.isEmpty()) { - if (elem_description.isEmpty()) { - // BOTH title and description are empty, skip this message. - continue; - } - else { - // Title is empty but description is not. - new_message.m_title = WebFactory::instance()->escapeHtml(WebFactory::instance()->stripTags(elem_description.simplified())); - new_message.m_contents = elem_description; - } - } - else { - // Title is really not empty, description does not matter. - new_message.m_title = WebFactory::instance()->escapeHtml(WebFactory::instance()->stripTags(elem_title)); - new_message.m_contents = elem_description; - } - - - // Deal with link and author. - new_message.m_url = message_item.namedItem(QSL("link")).toElement().text(); - new_message.m_author = message_item.namedItem(QSL("creator")).toElement().text(); - - // Deal with creation date. - QString elem_updated = message_item.namedItem(QSL("date")).toElement().text(); - - if (elem_updated.isEmpty()) { - elem_updated = message_item.namedItem(QSL("dc:date")).toElement().text(); - } - - // Deal with creation date. - new_message.m_created = TextFactory::parseDateTime(elem_updated); - new_message.m_createdFromFeed = !new_message.m_created.isNull(); - - if (!new_message.m_createdFromFeed) { - // Date was NOT obtained from the feed, set current date as creation date for the message. - new_message.m_created = current_time; - } - - if (new_message.m_author.isNull()) { - new_message.m_author = ""; - } - - if (new_message.m_url.isNull()) { - new_message.m_url = ""; - } - - messages.append(new_message); - } - - return messages; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/standard/rdfparser.h" + +#include "miscellaneous/textfactory.h" +#include "network-web/webfactory.h" + +#include + + +RdfParser::RdfParser() { +} + +RdfParser::~RdfParser() { +} + +QList RdfParser::parseXmlData(const QString& data) { + QList messages; + QDomDocument xml_file; + QDateTime current_time = QDateTime::currentDateTime(); + xml_file.setContent(data, true); + // Pull out all messages. + QDomNodeList messages_in_xml = xml_file.elementsByTagName(QSL("item")); + + for (int i = 0; i < messages_in_xml.size(); i++) { + QDomNode message_item = messages_in_xml.item(i); + Message new_message; + // Deal with title and description. + QString elem_title = message_item.namedItem(QSL("title")).toElement().text().simplified(); + QString elem_description = message_item.namedItem(QSL("description")).toElement().text(); + + // Now we obtained maximum of information for title & description. + if (elem_title.isEmpty()) { + if (elem_description.isEmpty()) { + // BOTH title and description are empty, skip this message. + continue; + } + + else { + // Title is empty but description is not. + new_message.m_title = WebFactory::instance()->escapeHtml(WebFactory::instance()->stripTags(elem_description.simplified())); + new_message.m_contents = elem_description; + } + } + + else { + // Title is really not empty, description does not matter. + new_message.m_title = WebFactory::instance()->escapeHtml(WebFactory::instance()->stripTags(elem_title)); + new_message.m_contents = elem_description; + } + + // Deal with link and author. + new_message.m_url = message_item.namedItem(QSL("link")).toElement().text(); + new_message.m_author = message_item.namedItem(QSL("creator")).toElement().text(); + // Deal with creation date. + QString elem_updated = message_item.namedItem(QSL("date")).toElement().text(); + + if (elem_updated.isEmpty()) { + elem_updated = message_item.namedItem(QSL("dc:date")).toElement().text(); + } + + // Deal with creation date. + new_message.m_created = TextFactory::parseDateTime(elem_updated); + new_message.m_createdFromFeed = !new_message.m_created.isNull(); + + if (!new_message.m_createdFromFeed) { + // Date was NOT obtained from the feed, set current date as creation date for the message. + new_message.m_created = current_time; + } + + if (new_message.m_author.isNull()) { + new_message.m_author = ""; + } + + if (new_message.m_url.isNull()) { + new_message.m_url = ""; + } + + messages.append(new_message); + } + + return messages; +} diff --git a/src/services/standard/rdfparser.h b/src/services/standard/rdfparser.h old mode 100644 new mode 100755 index 071619bcf..760d05c44 --- a/src/services/standard/rdfparser.h +++ b/src/services/standard/rdfparser.h @@ -1,34 +1,34 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef RDFPARSER_H -#define RDFPARSER_H - -#include "core/message.h" - -#include - - -class RdfParser { - public: - explicit RdfParser(); - virtual ~RdfParser(); - - QList parseXmlData(const QString &data); -}; - -#endif // RDFPARSER_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef RDFPARSER_H +#define RDFPARSER_H + +#include "core/message.h" + +#include + + +class RdfParser { + public: + explicit RdfParser(); + virtual ~RdfParser(); + + QList parseXmlData(const QString& data); +}; + +#endif // RDFPARSER_H diff --git a/src/services/standard/rssparser.cpp b/src/services/standard/rssparser.cpp old mode 100644 new mode 100755 index ff43127f1..3a9cd8426 --- a/src/services/standard/rssparser.cpp +++ b/src/services/standard/rssparser.cpp @@ -1,122 +1,123 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/standard/rssparser.h" - -#include "miscellaneous/textfactory.h" -#include "network-web/webfactory.h" -#include "miscellaneous/iofactory.h" -#include "exceptions/applicationexception.h" - -#include - - -RssParser::RssParser(const QString &data) : FeedParser(data) { -} - -RssParser::~RssParser() { -} - -QDomNodeList RssParser::messageElements() { - QDomNode channel_elem = m_xml.namedItem(QSL("rss")).namedItem(QSL("channel")); - - if (channel_elem.isNull()) { - return QDomNodeList(); - } - else { - return channel_elem.toElement().elementsByTagName(QSL("item")); - } -} - -Message RssParser::extractMessage(const QDomElement &msg_element, QDateTime current_time) const { - Message new_message; - - // Deal with titles & descriptions. - QString elem_title = msg_element.namedItem(QSL("title")).toElement().text().simplified(); - QString elem_description = msg_element.namedItem(QSL("encoded")).toElement().text(); - QString elem_enclosure = msg_element.namedItem(QSL("enclosure")).toElement().attribute(QSL("url")); - QString elem_enclosure_type = msg_element.namedItem(QSL("enclosure")).toElement().attribute(QSL("type")); - - if (elem_description.isEmpty()) { - elem_description = msg_element.namedItem(QSL("description")).toElement().text(); - } - - // Now we obtained maximum of information for title & description. - if (elem_title.isEmpty()) { - if (elem_description.isEmpty()) { - // BOTH title and description are empty, skip this message. - throw new ApplicationException(QSL("Not enough data for the message.")); - } - else { - // Title is empty but description is not. - new_message.m_title = WebFactory::instance()->stripTags(elem_description.simplified()); - new_message.m_contents = elem_description; - } - } - else { - // Title is really not empty, description does not matter. - new_message.m_title = WebFactory::instance()->stripTags(elem_title); - new_message.m_contents = elem_description; - } - - if (!elem_enclosure.isEmpty()) { - new_message.m_enclosures.append(Enclosure(elem_enclosure, elem_enclosure_type)); - - qDebug("Adding enclosure '%s' for the message.", qPrintable(elem_enclosure)); - } - - // Deal with link and author. - new_message.m_url = msg_element.namedItem(QSL("link")).toElement().text(); - - if (new_message.m_url.isEmpty() && !new_message.m_enclosures.isEmpty()) { - new_message.m_url = new_message.m_enclosures.first().m_url; - } - - if (new_message.m_url.isEmpty()) { - // Try to get "href" attribute. - new_message.m_url = msg_element.namedItem(QSL("link")).toElement().attribute(QSL("href")); - } - - new_message.m_author = msg_element.namedItem(QSL("author")).toElement().text(); - - if (new_message.m_author.isEmpty()) { - new_message.m_author = msg_element.namedItem(QSL("creator")).toElement().text(); - } - - // Deal with creation date. - new_message.m_created = TextFactory::parseDateTime(msg_element.namedItem(QSL("pubDate")).toElement().text()); - - if (new_message.m_created.isNull()) { - new_message.m_created = TextFactory::parseDateTime(msg_element.namedItem(QSL("date")).toElement().text()); - } - - if (!(new_message.m_createdFromFeed = !new_message.m_created.isNull())) { - // Date was NOT obtained from the feed, - // set current date as creation date for the message. - new_message.m_created = current_time; - } - - if (new_message.m_author.isNull()) { - new_message.m_author = ""; - } - - if (new_message.m_url.isNull()) { - new_message.m_url = ""; - } - - return new_message; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/standard/rssparser.h" + +#include "miscellaneous/textfactory.h" +#include "network-web/webfactory.h" +#include "miscellaneous/iofactory.h" +#include "exceptions/applicationexception.h" + +#include + + +RssParser::RssParser(const QString& data) : FeedParser(data) { +} + +RssParser::~RssParser() { +} + +QDomNodeList RssParser::messageElements() { + QDomNode channel_elem = m_xml.namedItem(QSL("rss")).namedItem(QSL("channel")); + + if (channel_elem.isNull()) { + return QDomNodeList(); + } + + else { + return channel_elem.toElement().elementsByTagName(QSL("item")); + } +} + +Message RssParser::extractMessage(const QDomElement& msg_element, QDateTime current_time) const { + Message new_message; + // Deal with titles & descriptions. + QString elem_title = msg_element.namedItem(QSL("title")).toElement().text().simplified(); + QString elem_description = msg_element.namedItem(QSL("encoded")).toElement().text(); + QString elem_enclosure = msg_element.namedItem(QSL("enclosure")).toElement().attribute(QSL("url")); + QString elem_enclosure_type = msg_element.namedItem(QSL("enclosure")).toElement().attribute(QSL("type")); + + if (elem_description.isEmpty()) { + elem_description = msg_element.namedItem(QSL("description")).toElement().text(); + } + + // Now we obtained maximum of information for title & description. + if (elem_title.isEmpty()) { + if (elem_description.isEmpty()) { + // BOTH title and description are empty, skip this message. + throw new ApplicationException(QSL("Not enough data for the message.")); + } + + else { + // Title is empty but description is not. + new_message.m_title = WebFactory::instance()->stripTags(elem_description.simplified()); + new_message.m_contents = elem_description; + } + } + + else { + // Title is really not empty, description does not matter. + new_message.m_title = WebFactory::instance()->stripTags(elem_title); + new_message.m_contents = elem_description; + } + + if (!elem_enclosure.isEmpty()) { + new_message.m_enclosures.append(Enclosure(elem_enclosure, elem_enclosure_type)); + qDebug("Adding enclosure '%s' for the message.", qPrintable(elem_enclosure)); + } + + // Deal with link and author. + new_message.m_url = msg_element.namedItem(QSL("link")).toElement().text(); + + if (new_message.m_url.isEmpty() && !new_message.m_enclosures.isEmpty()) { + new_message.m_url = new_message.m_enclosures.first().m_url; + } + + if (new_message.m_url.isEmpty()) { + // Try to get "href" attribute. + new_message.m_url = msg_element.namedItem(QSL("link")).toElement().attribute(QSL("href")); + } + + new_message.m_author = msg_element.namedItem(QSL("author")).toElement().text(); + + if (new_message.m_author.isEmpty()) { + new_message.m_author = msg_element.namedItem(QSL("creator")).toElement().text(); + } + + // Deal with creation date. + new_message.m_created = TextFactory::parseDateTime(msg_element.namedItem(QSL("pubDate")).toElement().text()); + + if (new_message.m_created.isNull()) { + new_message.m_created = TextFactory::parseDateTime(msg_element.namedItem(QSL("date")).toElement().text()); + } + + if (!(new_message.m_createdFromFeed = !new_message.m_created.isNull())) { + // Date was NOT obtained from the feed, + // set current date as creation date for the message. + new_message.m_created = current_time; + } + + if (new_message.m_author.isNull()) { + new_message.m_author = ""; + } + + if (new_message.m_url.isNull()) { + new_message.m_url = ""; + } + + return new_message; +} diff --git a/src/services/standard/rssparser.h b/src/services/standard/rssparser.h old mode 100644 new mode 100755 index db8e9822e..ba7030ecc --- a/src/services/standard/rssparser.h +++ b/src/services/standard/rssparser.h @@ -1,38 +1,38 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef RSSPARSER_H -#define RSSPARSER_H - -#include "services/standard/feedparser.h" - -#include "core/message.h" - -#include - - -class RssParser : public FeedParser { - public: - explicit RssParser(const QString &data); - virtual ~RssParser(); - - private: - QDomNodeList messageElements(); - Message extractMessage(const QDomElement &msg_element, QDateTime current_time) const; -}; - -#endif // RSSPARSER_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef RSSPARSER_H +#define RSSPARSER_H + +#include "services/standard/feedparser.h" + +#include "core/message.h" + +#include + + +class RssParser : public FeedParser { + public: + explicit RssParser(const QString& data); + virtual ~RssParser(); + + private: + QDomNodeList messageElements(); + Message extractMessage(const QDomElement& msg_element, QDateTime current_time) const; +}; + +#endif // RSSPARSER_H diff --git a/src/services/standard/standardcategory.cpp b/src/services/standard/standardcategory.cpp index fff3c5601..ded3a076a 100755 --- a/src/services/standard/standardcategory.cpp +++ b/src/services/standard/standardcategory.cpp @@ -1,194 +1,197 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/standard/standardcategory.h" - -#include "definitions/definitions.h" -#include "miscellaneous/databasequeries.h" -#include "miscellaneous/textfactory.h" -#include "miscellaneous/settings.h" -#include "miscellaneous/iconfactory.h" -#include "core/feedsmodel.h" -#include "gui/feedmessageviewer.h" -#include "gui/feedsview.h" -#include "services/standard/gui/formstandardcategorydetails.h" -#include "services/standard/standardserviceroot.h" -#include "services/standard/standardfeed.h" - -#include - - -StandardCategory::StandardCategory(RootItem *parent_item) : Category(parent_item) { -} - -StandardCategory::StandardCategory(const StandardCategory &other) - : Category(nullptr) { - setId(other.id()); - setCustomId(other.customId()); - setTitle(other.title()); - setDescription(other.description()); - setIcon(other.icon()); - setCreationDate(other.creationDate()); - setChildItems(other.childItems()); - setParent(other.parent()); -} - -StandardCategory::~StandardCategory() { - qDebug("Destroying Category instance."); -} - -StandardServiceRoot *StandardCategory::serviceRoot() const { - return qobject_cast(getParentServiceRoot()); -} - -QVariant StandardCategory::data(int column, int role) const { - switch (role) { - case Qt::ToolTipRole: - if (column == FDS_MODEL_TITLE_INDEX) { - //: Tooltip for standard feed. - return tr("%1 (category)" - "%2%3").arg(title(), - description().isEmpty() ? QString() : QSL("\n") + description(), - childCount() == 0 ? - tr("\nThis category does not contain any nested items.") : - QString()); - } - else { - return Category::data(column, role); - } - - default: - return Category::data(column, role); - } -} - -Qt::ItemFlags StandardCategory::additionalFlags() const { - return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; -} - -bool StandardCategory::performDragDropChange(RootItem *target_item) { - StandardCategory *category_new = new StandardCategory(*this); - category_new->clearChildren(); - category_new->setParent(target_item); - - if (editItself(category_new)) { - serviceRoot()->requestItemReassignment(this, target_item); - delete category_new; - return true; - } - else { - delete category_new; - return false; - } -} - -bool StandardCategory::editViaGui() { - QScopedPointer form_pointer(new FormStandardCategoryDetails(serviceRoot(), qApp->mainFormWidget())); - - form_pointer.data()->addEditCategory(this, nullptr); - return false; -} - -bool StandardCategory::deleteViaGui() { - if (removeItself()) { - serviceRoot()->requestItemRemoval(this); - return true; - } - else { - return false; - } -} - -bool StandardCategory::markAsReadUnread(ReadStatus status) { - return serviceRoot()->markFeedsReadUnread(getSubTreeFeeds(), status); -} - -bool StandardCategory::cleanMessages(bool clean_read_only) { - return serviceRoot()->cleanFeeds(getSubTreeFeeds(), clean_read_only); -} - -bool StandardCategory::removeItself() { - bool children_removed = true; - - // Remove all child items (feeds and categories) - // from the database. - foreach (RootItem *child, childItems()) { - if (child->kind() == RootItemKind::Category) { - children_removed &= static_cast(child)->removeItself(); - } - else if (child->kind() == RootItemKind::Feed) { - children_removed &= static_cast(child)->removeItself(); - } - } - - if (children_removed) { - // Children are removed, remove this standard category too. - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - return DatabaseQueries::deleteCategory(database, id()); - } - else { - return false; - } -} - -bool StandardCategory::addItself(RootItem *parent) { - // Now, add category to persistent storage. - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - int new_id = DatabaseQueries::addCategory(database, parent->id(), parent->getParentServiceRoot()->accountId(), - title(), description(), creationDate(), icon()); - - if (new_id <= 0) { - return false; - } - else { - setId(new_id); - setCustomId(new_id); - return true; - } -} - -bool StandardCategory::editItself(StandardCategory *new_category_data) { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - StandardCategory *original_category = this; - RootItem *new_parent = new_category_data->parent(); - - if (DatabaseQueries::editCategory(database, new_parent->id(), original_category->id(), - new_category_data->title(), new_category_data->description(), - new_category_data->icon())) { - // Setup new model data for the original item. - original_category->setDescription(new_category_data->description()); - original_category->setIcon(new_category_data->icon()); - original_category->setTitle(new_category_data->title()); - - // Editing is done. - return true; - } - else { - return false; - } -} - -StandardCategory::StandardCategory(const QSqlRecord &record) : Category(nullptr) { - setId(record.value(CAT_DB_ID_INDEX).toInt()); - setCustomId(id()); - setTitle(record.value(CAT_DB_TITLE_INDEX).toString()); - setDescription(record.value(CAT_DB_DESCRIPTION_INDEX).toString()); - setCreationDate(TextFactory::parseDateTime(record.value(CAT_DB_DCREATED_INDEX).value()).toLocalTime()); - setIcon(qApp->icons()->fromByteArray(record.value(CAT_DB_ICON_INDEX).toByteArray())); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/standard/standardcategory.h" + +#include "definitions/definitions.h" +#include "miscellaneous/databasequeries.h" +#include "miscellaneous/textfactory.h" +#include "miscellaneous/settings.h" +#include "miscellaneous/iconfactory.h" +#include "core/feedsmodel.h" +#include "gui/feedmessageviewer.h" +#include "gui/feedsview.h" +#include "services/standard/gui/formstandardcategorydetails.h" +#include "services/standard/standardserviceroot.h" +#include "services/standard/standardfeed.h" + +#include + + +StandardCategory::StandardCategory(RootItem* parent_item) : Category(parent_item) { +} + +StandardCategory::StandardCategory(const StandardCategory& other) + : Category(nullptr) { + setId(other.id()); + setCustomId(other.customId()); + setTitle(other.title()); + setDescription(other.description()); + setIcon(other.icon()); + setCreationDate(other.creationDate()); + setChildItems(other.childItems()); + setParent(other.parent()); +} + +StandardCategory::~StandardCategory() { + qDebug("Destroying Category instance."); +} + +StandardServiceRoot* StandardCategory::serviceRoot() const { + return qobject_cast(getParentServiceRoot()); +} + +QVariant StandardCategory::data(int column, int role) const { + switch (role) { + case Qt::ToolTipRole: + if (column == FDS_MODEL_TITLE_INDEX) { + //: Tooltip for standard feed. + return tr("%1 (category)" + "%2%3").arg(title(), + description().isEmpty() ? QString() : QSL("\n") + description(), + childCount() == 0 ? + tr("\nThis category does not contain any nested items.") : + QString()); + } + + else { + return Category::data(column, role); + } + + default: + return Category::data(column, role); + } +} + +Qt::ItemFlags StandardCategory::additionalFlags() const { + return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; +} + +bool StandardCategory::performDragDropChange(RootItem* target_item) { + StandardCategory* category_new = new StandardCategory(*this); + category_new->clearChildren(); + category_new->setParent(target_item); + + if (editItself(category_new)) { + serviceRoot()->requestItemReassignment(this, target_item); + delete category_new; + return true; + } + + else { + delete category_new; + return false; + } +} + +bool StandardCategory::editViaGui() { + QScopedPointer form_pointer(new FormStandardCategoryDetails(serviceRoot(), qApp->mainFormWidget())); + form_pointer.data()->addEditCategory(this, nullptr); + return false; +} + +bool StandardCategory::deleteViaGui() { + if (removeItself()) { + serviceRoot()->requestItemRemoval(this); + return true; + } + + else { + return false; + } +} + +bool StandardCategory::markAsReadUnread(ReadStatus status) { + return serviceRoot()->markFeedsReadUnread(getSubTreeFeeds(), status); +} + +bool StandardCategory::cleanMessages(bool clean_read_only) { + return serviceRoot()->cleanFeeds(getSubTreeFeeds(), clean_read_only); +} + +bool StandardCategory::removeItself() { + bool children_removed = true; + + // Remove all child items (feeds and categories) + // from the database. + foreach (RootItem* child, childItems()) { + if (child->kind() == RootItemKind::Category) { + children_removed &= static_cast(child)->removeItself(); + } + + else if (child->kind() == RootItemKind::Feed) { + children_removed &= static_cast(child)->removeItself(); + } + } + + if (children_removed) { + // Children are removed, remove this standard category too. + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + return DatabaseQueries::deleteCategory(database, id()); + } + + else { + return false; + } +} + +bool StandardCategory::addItself(RootItem* parent) { + // Now, add category to persistent storage. + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + int new_id = DatabaseQueries::addCategory(database, parent->id(), parent->getParentServiceRoot()->accountId(), + title(), description(), creationDate(), icon()); + + if (new_id <= 0) { + return false; + } + + else { + setId(new_id); + setCustomId(new_id); + return true; + } +} + +bool StandardCategory::editItself(StandardCategory* new_category_data) { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + StandardCategory* original_category = this; + RootItem* new_parent = new_category_data->parent(); + + if (DatabaseQueries::editCategory(database, new_parent->id(), original_category->id(), + new_category_data->title(), new_category_data->description(), + new_category_data->icon())) { + // Setup new model data for the original item. + original_category->setDescription(new_category_data->description()); + original_category->setIcon(new_category_data->icon()); + original_category->setTitle(new_category_data->title()); + // Editing is done. + return true; + } + + else { + return false; + } +} + +StandardCategory::StandardCategory(const QSqlRecord& record) : Category(nullptr) { + setId(record.value(CAT_DB_ID_INDEX).toInt()); + setCustomId(id()); + setTitle(record.value(CAT_DB_TITLE_INDEX).toString()); + setDescription(record.value(CAT_DB_DESCRIPTION_INDEX).toString()); + setCreationDate(TextFactory::parseDateTime(record.value(CAT_DB_DCREATED_INDEX).value()).toLocalTime()); + setIcon(qApp->icons()->fromByteArray(record.value(CAT_DB_ICON_INDEX).toByteArray())); +} diff --git a/src/services/standard/standardcategory.h b/src/services/standard/standardcategory.h index 5a9fc216c..310d80d1d 100755 --- a/src/services/standard/standardcategory.h +++ b/src/services/standard/standardcategory.h @@ -1,72 +1,72 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FEEDSMODELCATEGORY_H -#define FEEDSMODELCATEGORY_H - -#include "services/abstract/category.h" - -#include -#include - - -class FeedsModel; -class StandardServiceRoot; - -// Base class for all categories contained in FeedsModel. -// NOTE: This class should be derived to create PARTICULAR category types. -// NOTE: This class should not be instantiated directly. -class StandardCategory : public Category { - Q_OBJECT - - public: - // Constructors and destructors - explicit StandardCategory(RootItem *parent_item = nullptr); - explicit StandardCategory(const StandardCategory &other); - explicit StandardCategory(const QSqlRecord &record); - virtual ~StandardCategory(); - - StandardServiceRoot *serviceRoot() const; - - // Returns the actual data representation of standard category. - QVariant data(int column, int role) const; - Qt::ItemFlags additionalFlags() const; - bool performDragDropChange(RootItem *target_item); - - bool canBeEdited() const { - return true; - } - - bool canBeDeleted() const { - return true; - } - - bool editViaGui(); - bool deleteViaGui(); - - bool markAsReadUnread(ReadStatus status); - bool cleanMessages(bool clean_read_only); - - // Removes category and all its children from persistent - // database. - bool removeItself(); - - bool addItself(RootItem *parent); - bool editItself(StandardCategory *new_category_data); -}; - -#endif // FEEDSMODELCLASSICCATEGORY_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FEEDSMODELCATEGORY_H +#define FEEDSMODELCATEGORY_H + +#include "services/abstract/category.h" + +#include +#include + + +class FeedsModel; +class StandardServiceRoot; + +// Base class for all categories contained in FeedsModel. +// NOTE: This class should be derived to create PARTICULAR category types. +// NOTE: This class should not be instantiated directly. +class StandardCategory : public Category { + Q_OBJECT + + public: + // Constructors and destructors + explicit StandardCategory(RootItem* parent_item = nullptr); + explicit StandardCategory(const StandardCategory& other); + explicit StandardCategory(const QSqlRecord& record); + virtual ~StandardCategory(); + + StandardServiceRoot* serviceRoot() const; + + // Returns the actual data representation of standard category. + QVariant data(int column, int role) const; + Qt::ItemFlags additionalFlags() const; + bool performDragDropChange(RootItem* target_item); + + bool canBeEdited() const { + return true; + } + + bool canBeDeleted() const { + return true; + } + + bool editViaGui(); + bool deleteViaGui(); + + bool markAsReadUnread(ReadStatus status); + bool cleanMessages(bool clean_read_only); + + // Removes category and all its children from persistent + // database. + bool removeItself(); + + bool addItself(RootItem* parent); + bool editItself(StandardCategory* new_category_data); +}; + +#endif // FEEDSMODELCLASSICCATEGORY_H diff --git a/src/services/standard/standardfeed.cpp b/src/services/standard/standardfeed.cpp index a95cb058c..56430d0e1 100755 --- a/src/services/standard/standardfeed.cpp +++ b/src/services/standard/standardfeed.cpp @@ -1,493 +1,489 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/standard/standardfeed.h" - -#include "definitions/definitions.h" -#include "services/standard/rssparser.h" -#include "services/standard/rdfparser.h" -#include "services/standard/atomparser.h" -#include "core/feedsmodel.h" -#include "miscellaneous/databasequeries.h" -#include "miscellaneous/textfactory.h" -#include "miscellaneous/settings.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/simplecrypt/simplecrypt.h" -#include "network-web/networkfactory.h" -#include "gui/feedmessageviewer.h" -#include "gui/feedsview.h" -#include "services/abstract/recyclebin.h" -#include "services/standard/gui/formstandardfeeddetails.h" -#include "services/standard/standardserviceroot.h" - -#include -#include -#include -#include -#include -#include -#include - - -StandardFeed::StandardFeed(RootItem *parent_item) - : Feed(parent_item) { - m_passwordProtected = false; - m_username = QString(); - m_password = QString(); - m_networkError = QNetworkReply::NoError; - m_type = Rss0X; - m_encoding = QString(); -} - -StandardFeed::StandardFeed(const StandardFeed &other) - : Feed(nullptr) { - m_passwordProtected = other.passwordProtected(); - m_username = other.username(); - m_password = other.password(); - m_networkError = other.networkError(); - m_type = other.type(); - m_encoding = other.encoding(); - - setCountOfAllMessages(other.countOfAllMessages()); - setCountOfUnreadMessages(other.countOfUnreadMessages()); - - setUrl(other.url()); - setStatus(other.status()); - setAutoUpdateType(other.autoUpdateType()); - setAutoUpdateInitialInterval(other.autoUpdateInitialInterval()); - setAutoUpdateRemainingInterval(other.autoUpdateRemainingInterval()); - - setTitle(other.title()); - setId(other.id()); - setCustomId(other.customId()); - setIcon(other.icon()); - setChildItems(other.childItems()); - setParent(other.parent()); - setCreationDate(other.creationDate()); - setDescription(other.description()); -} - -StandardFeed::~StandardFeed() { - qDebug("Destroying Feed instance."); -} - -QList StandardFeed::contextMenu() { - return serviceRoot()->getContextMenuForFeed(this); -} - -StandardServiceRoot *StandardFeed::serviceRoot() const { - return qobject_cast(getParentServiceRoot()); -} - -bool StandardFeed::editViaGui() { - QScopedPointer form_pointer(new FormStandardFeedDetails(serviceRoot(), qApp->mainFormWidget())); - form_pointer.data()->addEditFeed(this, nullptr); - return false; -} - -bool StandardFeed::deleteViaGui() { - if (removeItself()) { - serviceRoot()->requestItemRemoval(this); - return true; - } - else { - return false; - } -} - -bool StandardFeed::markAsReadUnread(ReadStatus status) { - return serviceRoot()->markFeedsReadUnread(QList() << this, status); -} - -bool StandardFeed::cleanMessages(bool clean_read_only) { - return serviceRoot()->cleanFeeds(QList() << this, clean_read_only); -} - -QVariant StandardFeed::data(int column, int role) const { - switch (role) { - case Qt::ToolTipRole: - if (column == FDS_MODEL_TITLE_INDEX) { - //: Tooltip for feed. - return tr("%1 (%2)" - "%3\n\n" - "Network status: %6\n" - "Encoding: %4\n" - "Auto-update status: %5").arg(title(), - StandardFeed::typeToString(type()), - description().isEmpty() ? QString() : QString('\n') + description(), - encoding(), - getAutoUpdateStatusDescription(), - NetworkFactory::networkErrorText(m_networkError)); - } - else { - return QVariant(); - } - - default: - return Feed::data(column, role); - } -} - -QString StandardFeed::typeToString(StandardFeed::Type type) { - switch (type) { - case Atom10: - return QSL("ATOM 1.0"); - - case Rdf: - return QSL("RDF (RSS 1.0)"); - - case Rss0X: - return QSL("RSS 0.91/0.92/0.93"); - - case Rss2X: - default: - return QSL("RSS 2.0/2.0.1"); - } -} - -void StandardFeed::fetchMetadataForItself() { - QPair metadata = guessFeed(url(), username(), password()); - - if (metadata.first != nullptr && metadata.second == QNetworkReply::NoError) { - // Some properties are not updated when new metadata are fetched. - metadata.first->setParent(parent()); - metadata.first->setUrl(url()); - metadata.first->setPasswordProtected(passwordProtected()); - metadata.first->setUsername(username()); - metadata.first->setPassword(password()); - metadata.first->setAutoUpdateType(autoUpdateType()); - metadata.first->setAutoUpdateInitialInterval(autoUpdateInitialInterval()); - - editItself(metadata.first); - delete metadata.first; - - // Notify the model about fact, that it needs to reload new information about - // this item, particularly the icon. - serviceRoot()->itemChanged(QList() << this); - } - else { - qApp->showGuiMessage(tr("Metadata not fetched"), - tr("Metadata was not fetched because: %1.").arg(NetworkFactory::networkErrorText(metadata.second)), - QSystemTrayIcon::Critical); - } -} - -QPair StandardFeed::guessFeed(const QString &url, - const QString &username, - const QString &password) { - QPair result; result.first = nullptr; - - QByteArray feed_contents; - NetworkResult network_result = NetworkFactory::downloadFeedFile(url, - qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - feed_contents, - !username.isEmpty(), - username, - password); - result.second = network_result.first; - - if (result.second == QNetworkReply::NoError || !feed_contents.isEmpty()) { - // Feed XML was obtained, now we need to try to guess - // its encoding before we can read further data. - QString xml_schema_encoding; - QString xml_contents_encoded; - QRegExp encoding_rexp(QSL("encoding=\"[^\"]\\S+\"")); - - if (encoding_rexp.indexIn(feed_contents) != -1 && - !(xml_schema_encoding = encoding_rexp.cap(0)).isEmpty()) { - // Some "encoding" attribute was found get the encoding - // out of it. - encoding_rexp.setPattern(QSL("[^\"]\\S+[^\"]")); - encoding_rexp.indexIn(xml_schema_encoding, 9); - xml_schema_encoding = encoding_rexp.cap(0); - } - - if (result.first == nullptr) { - result.first = new StandardFeed(); - } - - QTextCodec *custom_codec = QTextCodec::codecForName(xml_schema_encoding.toLocal8Bit()); - - if (custom_codec != nullptr) { - // Feed encoding was probably guessed. - xml_contents_encoded = custom_codec->toUnicode(feed_contents); - result.first->setEncoding(xml_schema_encoding); - } - else { - // Feed encoding probably not guessed, set it as - // default. - xml_contents_encoded = feed_contents; - result.first->setEncoding(DEFAULT_FEED_ENCODING); - } - - // Feed XML was obtained, guess it now. - QDomDocument xml_document; - QString error_msg; - int error_line, error_column; - - if (!xml_document.setContent(xml_contents_encoded, - &error_msg, - &error_line, - &error_column)) { - qDebug("XML of feed '%s' is not valid and cannot be loaded. Error: '%s' " - "(line %d, column %d).", - qPrintable(url), - qPrintable(error_msg), - error_line, error_column); - - result.second = QNetworkReply::UnknownContentError; - - // XML is invalid, exit. - return result; - } - - QDomElement root_element = xml_document.documentElement(); - QString root_tag_name = root_element.tagName(); - QList icon_possible_locations; - - icon_possible_locations.append(url); - - if (root_tag_name == QL1S("rdf:RDF")) { - // We found RDF feed. - QDomElement channel_element = root_element.namedItem(QSL("channel")).toElement(); - - result.first->setType(Rdf); - result.first->setTitle(channel_element.namedItem(QSL("title")).toElement().text()); - result.first->setDescription(channel_element.namedItem(QSL("description")).toElement().text()); - - QString source_link = channel_element.namedItem(QSL("link")).toElement().text(); - - if (!source_link.isEmpty()) { - icon_possible_locations.prepend(source_link); - } - } - else if (root_tag_name == QL1S("rss")) { - // We found RSS 0.91/0.92/0.93/2.0/2.0.1 feed. - QString rss_type = root_element.attribute("version", "2.0"); - - if (rss_type == QL1S("0.91") || rss_type == QL1S("0.92") || rss_type == QL1S("0.93")) { - result.first->setType(Rss0X); - } - else { - result.first->setType(Rss2X); - } - - QDomElement channel_element = root_element.namedItem(QSL("channel")).toElement(); - - result.first->setTitle(channel_element.namedItem(QSL("title")).toElement().text()); - result.first->setDescription(channel_element.namedItem(QSL("description")).toElement().text()); - - QString source_link = channel_element.namedItem(QSL("link")).toElement().text(); - - if (!source_link.isEmpty()) { - icon_possible_locations.prepend(source_link); - } - } - else if (root_tag_name == QL1S("feed")) { - // We found ATOM feed. - result.first->setType(Atom10); - result.first->setTitle(root_element.namedItem(QSL("title")).toElement().text()); - result.first->setDescription(root_element.namedItem(QSL("subtitle")).toElement().text()); - - QString source_link = root_element.namedItem(QSL("link")).toElement().text(); - - if (!source_link.isEmpty()) { - icon_possible_locations.prepend(source_link); - } - } - else { - // File was downloaded and it really was XML file - // but feed format was NOT recognized. - result.second = QNetworkReply::UnknownContentError; - } - - // Try to obtain icon. - QIcon icon_data; - - if ((result.second = NetworkFactory::downloadIcon(icon_possible_locations, - DOWNLOAD_TIMEOUT, - icon_data)) == QNetworkReply::NoError) { - // Icon for feed was downloaded and is stored now in _icon_data. - result.first->setIcon(icon_data); - } - } - - return result; -} - -Qt::ItemFlags StandardFeed::additionalFlags() const { - return Qt::ItemIsDragEnabled; -} - -bool StandardFeed::performDragDropChange(RootItem *target_item) { - StandardFeed *feed_new = new StandardFeed(*this); - feed_new->setParent(target_item); - - if (editItself(feed_new)) { - serviceRoot()->requestItemReassignment(this, target_item); - delete feed_new; - return true; - } - else { - delete feed_new; - return false; - } -} - -bool StandardFeed::removeItself() { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - return DatabaseQueries::deleteFeed(database, customId(), getParentServiceRoot()->accountId()); -} - -bool StandardFeed::addItself(RootItem *parent) { - // Now, add feed to persistent storage. - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - bool ok; - int new_id = DatabaseQueries::addFeed(database, parent->id(), parent->getParentServiceRoot()->accountId(), title(), - description(), creationDate(), icon(), encoding(), url(), passwordProtected(), - username(), password(), autoUpdateType(), autoUpdateInitialInterval(), type(), &ok); - - if (!ok) { - // Query failed. - return false; - } - else { - // New feed was added, fetch is primary id from the database. - setId(new_id); - setCustomId(new_id); - - return true; - } -} - -bool StandardFeed::editItself(StandardFeed *new_feed_data) { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - StandardFeed *original_feed = this; - RootItem *new_parent = new_feed_data->parent(); - - if (!DatabaseQueries::editFeed(database, new_parent->id(), original_feed->id(), new_feed_data->title(), - new_feed_data->description(), new_feed_data->icon(), - new_feed_data->encoding(), new_feed_data->url(), new_feed_data->passwordProtected(), - new_feed_data->username(), new_feed_data->password(), - new_feed_data->autoUpdateType(), new_feed_data->autoUpdateInitialInterval(), - new_feed_data->type())) { - // Persistent storage update failed, no way to continue now. - return false; - } - - // Setup new model data for the original item. - original_feed->setTitle(new_feed_data->title()); - original_feed->setDescription(new_feed_data->description()); - original_feed->setIcon(new_feed_data->icon()); - original_feed->setEncoding(new_feed_data->encoding()); - original_feed->setDescription(new_feed_data->description()); - original_feed->setUrl(new_feed_data->url()); - original_feed->setPasswordProtected(new_feed_data->passwordProtected()); - original_feed->setUsername(new_feed_data->username()); - original_feed->setPassword(new_feed_data->password()); - original_feed->setAutoUpdateType(new_feed_data->autoUpdateType()); - original_feed->setAutoUpdateInitialInterval(new_feed_data->autoUpdateInitialInterval()); - original_feed->setType(new_feed_data->type()); - - // Editing is done. - return true; -} - -QList StandardFeed::obtainNewMessages(bool *error_during_obtaining) { - QByteArray feed_contents; - int download_timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); - m_networkError = NetworkFactory::downloadFeedFile(url(), download_timeout, feed_contents, - passwordProtected(), username(), password()).first; - - if (m_networkError != QNetworkReply::NoError) { - qWarning("Error during fetching of new messages for feed '%s' (id %d).", qPrintable(url()), id()); - setStatus(NetworkError); - *error_during_obtaining = true; - return QList(); - } - else if (status() != NewMessages) { - setStatus(Normal); - *error_during_obtaining = false; - } - - // Encode downloaded data for further parsing. - QTextCodec *codec = QTextCodec::codecForName(encoding().toLocal8Bit()); - QString formatted_feed_contents; - - if (codec == nullptr) { - // No suitable codec for this encoding was found. - // Use non-converted data. - formatted_feed_contents = feed_contents; - } - else { - formatted_feed_contents = codec->toUnicode(feed_contents); - } - - // Feed data are downloaded and encoded. - // Parse data and obtain messages. - QList messages; - - switch (type()) { - case StandardFeed::Rss0X: - case StandardFeed::Rss2X: - messages = RssParser(formatted_feed_contents).messages(); - break; - - case StandardFeed::Rdf: - messages = RdfParser().parseXmlData(formatted_feed_contents); - break; - - case StandardFeed::Atom10: - messages = AtomParser(formatted_feed_contents).messages(); - - default: - break; - } - - return messages; -} - -QNetworkReply::NetworkError StandardFeed::networkError() const { - return m_networkError; -} - -StandardFeed::StandardFeed(const QSqlRecord &record) : Feed(nullptr) { - setTitle(QString::fromUtf8(record.value(FDS_DB_TITLE_INDEX).toByteArray())); - setId(record.value(FDS_DB_ID_INDEX).toInt()); - setCustomId(id()); - setDescription(QString::fromUtf8(record.value(FDS_DB_DESCRIPTION_INDEX).toByteArray())); - setCreationDate(TextFactory::parseDateTime(record.value(FDS_DB_DCREATED_INDEX).value()).toLocalTime()); - setIcon(qApp->icons()->fromByteArray(record.value(FDS_DB_ICON_INDEX).toByteArray())); - setEncoding(record.value(FDS_DB_ENCODING_INDEX).toString()); - setUrl(record.value(FDS_DB_URL_INDEX).toString()); - setPasswordProtected(record.value(FDS_DB_PROTECTED_INDEX).toBool()); - setUsername(record.value(FDS_DB_USERNAME_INDEX).toString()); - - if (record.value(FDS_DB_PASSWORD_INDEX).toString().isEmpty()) { - setPassword(record.value(FDS_DB_PASSWORD_INDEX).toString()); - } - else { - setPassword(TextFactory::decrypt(record.value(FDS_DB_PASSWORD_INDEX).toString())); - } - - setAutoUpdateType(static_cast(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt())); - setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt()); - - m_networkError = QNetworkReply::NoError; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/standard/standardfeed.h" + +#include "definitions/definitions.h" +#include "services/standard/rssparser.h" +#include "services/standard/rdfparser.h" +#include "services/standard/atomparser.h" +#include "core/feedsmodel.h" +#include "miscellaneous/databasequeries.h" +#include "miscellaneous/textfactory.h" +#include "miscellaneous/settings.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/simplecrypt/simplecrypt.h" +#include "network-web/networkfactory.h" +#include "gui/feedmessageviewer.h" +#include "gui/feedsview.h" +#include "services/abstract/recyclebin.h" +#include "services/standard/gui/formstandardfeeddetails.h" +#include "services/standard/standardserviceroot.h" + +#include +#include +#include +#include +#include +#include +#include + + +StandardFeed::StandardFeed(RootItem* parent_item) + : Feed(parent_item) { + m_passwordProtected = false; + m_username = QString(); + m_password = QString(); + m_networkError = QNetworkReply::NoError; + m_type = Rss0X; + m_encoding = QString(); +} + +StandardFeed::StandardFeed(const StandardFeed& other) + : Feed(nullptr) { + m_passwordProtected = other.passwordProtected(); + m_username = other.username(); + m_password = other.password(); + m_networkError = other.networkError(); + m_type = other.type(); + m_encoding = other.encoding(); + setCountOfAllMessages(other.countOfAllMessages()); + setCountOfUnreadMessages(other.countOfUnreadMessages()); + setUrl(other.url()); + setStatus(other.status()); + setAutoUpdateType(other.autoUpdateType()); + setAutoUpdateInitialInterval(other.autoUpdateInitialInterval()); + setAutoUpdateRemainingInterval(other.autoUpdateRemainingInterval()); + setTitle(other.title()); + setId(other.id()); + setCustomId(other.customId()); + setIcon(other.icon()); + setChildItems(other.childItems()); + setParent(other.parent()); + setCreationDate(other.creationDate()); + setDescription(other.description()); +} + +StandardFeed::~StandardFeed() { + qDebug("Destroying Feed instance."); +} + +QList StandardFeed::contextMenu() { + return serviceRoot()->getContextMenuForFeed(this); +} + +StandardServiceRoot* StandardFeed::serviceRoot() const { + return qobject_cast(getParentServiceRoot()); +} + +bool StandardFeed::editViaGui() { + QScopedPointer form_pointer(new FormStandardFeedDetails(serviceRoot(), qApp->mainFormWidget())); + form_pointer.data()->addEditFeed(this, nullptr); + return false; +} + +bool StandardFeed::deleteViaGui() { + if (removeItself()) { + serviceRoot()->requestItemRemoval(this); + return true; + } + + else { + return false; + } +} + +bool StandardFeed::markAsReadUnread(ReadStatus status) { + return serviceRoot()->markFeedsReadUnread(QList() << this, status); +} + +bool StandardFeed::cleanMessages(bool clean_read_only) { + return serviceRoot()->cleanFeeds(QList() << this, clean_read_only); +} + +QVariant StandardFeed::data(int column, int role) const { + switch (role) { + case Qt::ToolTipRole: + if (column == FDS_MODEL_TITLE_INDEX) { + //: Tooltip for feed. + return tr("%1 (%2)" + "%3\n\n" + "Network status: %6\n" + "Encoding: %4\n" + "Auto-update status: %5").arg(title(), + StandardFeed::typeToString(type()), + description().isEmpty() ? QString() : QString('\n') + description(), + encoding(), + getAutoUpdateStatusDescription(), + NetworkFactory::networkErrorText(m_networkError)); + } + + else { + return QVariant(); + } + + default: + return Feed::data(column, role); + } +} + +QString StandardFeed::typeToString(StandardFeed::Type type) { + switch (type) { + case Atom10: + return QSL("ATOM 1.0"); + + case Rdf: + return QSL("RDF (RSS 1.0)"); + + case Rss0X: + return QSL("RSS 0.91/0.92/0.93"); + + case Rss2X: + default: + return QSL("RSS 2.0/2.0.1"); + } +} + +void StandardFeed::fetchMetadataForItself() { + QPair metadata = guessFeed(url(), username(), password()); + + if (metadata.first != nullptr && metadata.second == QNetworkReply::NoError) { + // Some properties are not updated when new metadata are fetched. + metadata.first->setParent(parent()); + metadata.first->setUrl(url()); + metadata.first->setPasswordProtected(passwordProtected()); + metadata.first->setUsername(username()); + metadata.first->setPassword(password()); + metadata.first->setAutoUpdateType(autoUpdateType()); + metadata.first->setAutoUpdateInitialInterval(autoUpdateInitialInterval()); + editItself(metadata.first); + delete metadata.first; + // Notify the model about fact, that it needs to reload new information about + // this item, particularly the icon. + serviceRoot()->itemChanged(QList() << this); + } + + else { + qApp->showGuiMessage(tr("Metadata not fetched"), + tr("Metadata was not fetched because: %1.").arg(NetworkFactory::networkErrorText(metadata.second)), + QSystemTrayIcon::Critical); + } +} + +QPair StandardFeed::guessFeed(const QString& url, + const QString& username, + const QString& password) { + QPair result; + result.first = nullptr; + QByteArray feed_contents; + NetworkResult network_result = NetworkFactory::downloadFeedFile(url, + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + feed_contents, + !username.isEmpty(), + username, + password); + result.second = network_result.first; + + if (result.second == QNetworkReply::NoError || !feed_contents.isEmpty()) { + // Feed XML was obtained, now we need to try to guess + // its encoding before we can read further data. + QString xml_schema_encoding; + QString xml_contents_encoded; + QRegExp encoding_rexp(QSL("encoding=\"[^\"]\\S+\"")); + + if (encoding_rexp.indexIn(feed_contents) != -1 && + !(xml_schema_encoding = encoding_rexp.cap(0)).isEmpty()) { + // Some "encoding" attribute was found get the encoding + // out of it. + encoding_rexp.setPattern(QSL("[^\"]\\S+[^\"]")); + encoding_rexp.indexIn(xml_schema_encoding, 9); + xml_schema_encoding = encoding_rexp.cap(0); + } + + if (result.first == nullptr) { + result.first = new StandardFeed(); + } + + QTextCodec* custom_codec = QTextCodec::codecForName(xml_schema_encoding.toLocal8Bit()); + + if (custom_codec != nullptr) { + // Feed encoding was probably guessed. + xml_contents_encoded = custom_codec->toUnicode(feed_contents); + result.first->setEncoding(xml_schema_encoding); + } + + else { + // Feed encoding probably not guessed, set it as + // default. + xml_contents_encoded = feed_contents; + result.first->setEncoding(DEFAULT_FEED_ENCODING); + } + + // Feed XML was obtained, guess it now. + QDomDocument xml_document; + QString error_msg; + int error_line, error_column; + + if (!xml_document.setContent(xml_contents_encoded, + &error_msg, + &error_line, + &error_column)) { + qDebug("XML of feed '%s' is not valid and cannot be loaded. Error: '%s' " + "(line %d, column %d).", + qPrintable(url), + qPrintable(error_msg), + error_line, error_column); + result.second = QNetworkReply::UnknownContentError; + // XML is invalid, exit. + return result; + } + + QDomElement root_element = xml_document.documentElement(); + QString root_tag_name = root_element.tagName(); + QList icon_possible_locations; + icon_possible_locations.append(url); + + if (root_tag_name == QL1S("rdf:RDF")) { + // We found RDF feed. + QDomElement channel_element = root_element.namedItem(QSL("channel")).toElement(); + result.first->setType(Rdf); + result.first->setTitle(channel_element.namedItem(QSL("title")).toElement().text()); + result.first->setDescription(channel_element.namedItem(QSL("description")).toElement().text()); + QString source_link = channel_element.namedItem(QSL("link")).toElement().text(); + + if (!source_link.isEmpty()) { + icon_possible_locations.prepend(source_link); + } + } + + else if (root_tag_name == QL1S("rss")) { + // We found RSS 0.91/0.92/0.93/2.0/2.0.1 feed. + QString rss_type = root_element.attribute("version", "2.0"); + + if (rss_type == QL1S("0.91") || rss_type == QL1S("0.92") || rss_type == QL1S("0.93")) { + result.first->setType(Rss0X); + } + + else { + result.first->setType(Rss2X); + } + + QDomElement channel_element = root_element.namedItem(QSL("channel")).toElement(); + result.first->setTitle(channel_element.namedItem(QSL("title")).toElement().text()); + result.first->setDescription(channel_element.namedItem(QSL("description")).toElement().text()); + QString source_link = channel_element.namedItem(QSL("link")).toElement().text(); + + if (!source_link.isEmpty()) { + icon_possible_locations.prepend(source_link); + } + } + + else if (root_tag_name == QL1S("feed")) { + // We found ATOM feed. + result.first->setType(Atom10); + result.first->setTitle(root_element.namedItem(QSL("title")).toElement().text()); + result.first->setDescription(root_element.namedItem(QSL("subtitle")).toElement().text()); + QString source_link = root_element.namedItem(QSL("link")).toElement().text(); + + if (!source_link.isEmpty()) { + icon_possible_locations.prepend(source_link); + } + } + + else { + // File was downloaded and it really was XML file + // but feed format was NOT recognized. + result.second = QNetworkReply::UnknownContentError; + } + + // Try to obtain icon. + QIcon icon_data; + + if ((result.second = NetworkFactory::downloadIcon(icon_possible_locations, + DOWNLOAD_TIMEOUT, + icon_data)) == QNetworkReply::NoError) { + // Icon for feed was downloaded and is stored now in _icon_data. + result.first->setIcon(icon_data); + } + } + + return result; +} + +Qt::ItemFlags StandardFeed::additionalFlags() const { + return Qt::ItemIsDragEnabled; +} + +bool StandardFeed::performDragDropChange(RootItem* target_item) { + StandardFeed* feed_new = new StandardFeed(*this); + feed_new->setParent(target_item); + + if (editItself(feed_new)) { + serviceRoot()->requestItemReassignment(this, target_item); + delete feed_new; + return true; + } + + else { + delete feed_new; + return false; + } +} + +bool StandardFeed::removeItself() { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + return DatabaseQueries::deleteFeed(database, customId(), getParentServiceRoot()->accountId()); +} + +bool StandardFeed::addItself(RootItem* parent) { + // Now, add feed to persistent storage. + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + bool ok; + int new_id = DatabaseQueries::addFeed(database, parent->id(), parent->getParentServiceRoot()->accountId(), title(), + description(), creationDate(), icon(), encoding(), url(), passwordProtected(), + username(), password(), autoUpdateType(), autoUpdateInitialInterval(), type(), &ok); + + if (!ok) { + // Query failed. + return false; + } + + else { + // New feed was added, fetch is primary id from the database. + setId(new_id); + setCustomId(new_id); + return true; + } +} + +bool StandardFeed::editItself(StandardFeed* new_feed_data) { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + StandardFeed* original_feed = this; + RootItem* new_parent = new_feed_data->parent(); + + if (!DatabaseQueries::editFeed(database, new_parent->id(), original_feed->id(), new_feed_data->title(), + new_feed_data->description(), new_feed_data->icon(), + new_feed_data->encoding(), new_feed_data->url(), new_feed_data->passwordProtected(), + new_feed_data->username(), new_feed_data->password(), + new_feed_data->autoUpdateType(), new_feed_data->autoUpdateInitialInterval(), + new_feed_data->type())) { + // Persistent storage update failed, no way to continue now. + return false; + } + + // Setup new model data for the original item. + original_feed->setTitle(new_feed_data->title()); + original_feed->setDescription(new_feed_data->description()); + original_feed->setIcon(new_feed_data->icon()); + original_feed->setEncoding(new_feed_data->encoding()); + original_feed->setDescription(new_feed_data->description()); + original_feed->setUrl(new_feed_data->url()); + original_feed->setPasswordProtected(new_feed_data->passwordProtected()); + original_feed->setUsername(new_feed_data->username()); + original_feed->setPassword(new_feed_data->password()); + original_feed->setAutoUpdateType(new_feed_data->autoUpdateType()); + original_feed->setAutoUpdateInitialInterval(new_feed_data->autoUpdateInitialInterval()); + original_feed->setType(new_feed_data->type()); + // Editing is done. + return true; +} + +QList StandardFeed::obtainNewMessages(bool* error_during_obtaining) { + QByteArray feed_contents; + int download_timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); + m_networkError = NetworkFactory::downloadFeedFile(url(), download_timeout, feed_contents, + passwordProtected(), username(), password()).first; + + if (m_networkError != QNetworkReply::NoError) { + qWarning("Error during fetching of new messages for feed '%s' (id %d).", qPrintable(url()), id()); + setStatus(NetworkError); + *error_during_obtaining = true; + return QList(); + } + + else if (status() != NewMessages) { + setStatus(Normal); + *error_during_obtaining = false; + } + + // Encode downloaded data for further parsing. + QTextCodec* codec = QTextCodec::codecForName(encoding().toLocal8Bit()); + QString formatted_feed_contents; + + if (codec == nullptr) { + // No suitable codec for this encoding was found. + // Use non-converted data. + formatted_feed_contents = feed_contents; + } + + else { + formatted_feed_contents = codec->toUnicode(feed_contents); + } + + // Feed data are downloaded and encoded. + // Parse data and obtain messages. + QList messages; + + switch (type()) { + case StandardFeed::Rss0X: + case StandardFeed::Rss2X: + messages = RssParser(formatted_feed_contents).messages(); + break; + + case StandardFeed::Rdf: + messages = RdfParser().parseXmlData(formatted_feed_contents); + break; + + case StandardFeed::Atom10: + messages = AtomParser(formatted_feed_contents).messages(); + + default: + break; + } + + return messages; +} + +QNetworkReply::NetworkError StandardFeed::networkError() const { + return m_networkError; +} + +StandardFeed::StandardFeed(const QSqlRecord& record) : Feed(nullptr) { + setTitle(QString::fromUtf8(record.value(FDS_DB_TITLE_INDEX).toByteArray())); + setId(record.value(FDS_DB_ID_INDEX).toInt()); + setCustomId(id()); + setDescription(QString::fromUtf8(record.value(FDS_DB_DESCRIPTION_INDEX).toByteArray())); + setCreationDate(TextFactory::parseDateTime(record.value(FDS_DB_DCREATED_INDEX).value()).toLocalTime()); + setIcon(qApp->icons()->fromByteArray(record.value(FDS_DB_ICON_INDEX).toByteArray())); + setEncoding(record.value(FDS_DB_ENCODING_INDEX).toString()); + setUrl(record.value(FDS_DB_URL_INDEX).toString()); + setPasswordProtected(record.value(FDS_DB_PROTECTED_INDEX).toBool()); + setUsername(record.value(FDS_DB_USERNAME_INDEX).toString()); + + if (record.value(FDS_DB_PASSWORD_INDEX).toString().isEmpty()) { + setPassword(record.value(FDS_DB_PASSWORD_INDEX).toString()); + } + + else { + setPassword(TextFactory::decrypt(record.value(FDS_DB_PASSWORD_INDEX).toString())); + } + + setAutoUpdateType(static_cast(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt())); + setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt()); + m_networkError = QNetworkReply::NoError; +} diff --git a/src/services/standard/standardfeed.h b/src/services/standard/standardfeed.h index b2a9cf5bb..6f4c36a22 100755 --- a/src/services/standard/standardfeed.h +++ b/src/services/standard/standardfeed.h @@ -1,159 +1,159 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FEEDSMODELFEED_H -#define FEEDSMODELFEED_H - -#include "services/abstract/feed.h" - -#include -#include -#include -#include -#include -#include - - -class Message; -class FeedsModel; -class StandardServiceRoot; - -// Represents BASE class for feeds contained in FeedsModel. -// NOTE: This class should be derived to create PARTICULAR feed types. -class StandardFeed : public Feed { - Q_OBJECT - - public: - // Describes possible types of feeds. - // NOTE: This is equivalent to attribute Feeds(type). - enum Type { - Rss0X = 0, - Rss2X = 1, - Rdf = 2, // Sometimes denoted as RSS 1.0. - Atom10 = 3 - }; - - // Constructors and destructors. - explicit StandardFeed(RootItem *parent_item = nullptr); - explicit StandardFeed(const StandardFeed &other); - explicit StandardFeed(const QSqlRecord &record); - virtual ~StandardFeed(); - - StandardServiceRoot *serviceRoot() const; - QList contextMenu(); - - bool canBeEdited() const { - return true; - } - - bool canBeDeleted() const { - return true; - } - - bool editViaGui(); - bool deleteViaGui(); - - bool markAsReadUnread(ReadStatus status); - bool cleanMessages(bool clean_read_only); - - QVariant data(int column, int role) const; - - // Obtains data related to this feed. - Qt::ItemFlags additionalFlags() const; - bool performDragDropChange(RootItem *target_item); - - // Removes this standard feed from persistent - // storage. - bool removeItself(); - bool addItself(RootItem *parent); - bool editItself(StandardFeed *new_feed_data); - - // Other getters/setters. - inline Type type() const { - return m_type; - } - - inline void setType(Type type) { - m_type = type; - } - - inline bool passwordProtected() const { - return m_passwordProtected; - } - - inline void setPasswordProtected(bool passwordProtected) { - m_passwordProtected = passwordProtected; - } - - inline QString username() const { - return m_username; - } - - inline void setUsername(const QString &username) { - m_username = username; - } - - inline QString password() const { - return m_password; - } - - inline void setPassword(const QString &password) { - m_password = password; - } - - inline QString encoding() const { - return m_encoding; - } - - inline void setEncoding(const QString &encoding) { - m_encoding = encoding; - } - - QNetworkReply::NetworkError networkError() const; - - // Tries to guess feed hidden under given URL - // and uses given credentials. - // Returns pointer to guessed feed (if at least partially - // guessed) and retrieved error/status code from network layer - // or NULL feed. - static QPair guessFeed(const QString &url, - const QString &username = QString(), - const QString &password = QString()); - - // Converts particular feed type to string. - static QString typeToString(Type type); - - public slots: - // Fetches metadata for the feed. - void fetchMetadataForItself(); - - private: - QList obtainNewMessages(bool *error_during_obtaining); - - private: - bool m_passwordProtected; - QString m_username; - QString m_password; - - Type m_type; - QNetworkReply::NetworkError m_networkError; - QString m_encoding; -}; - -Q_DECLARE_METATYPE(StandardFeed::Type) - -#endif // FEEDSMODELFEED_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FEEDSMODELFEED_H +#define FEEDSMODELFEED_H + +#include "services/abstract/feed.h" + +#include +#include +#include +#include +#include +#include + + +class Message; +class FeedsModel; +class StandardServiceRoot; + +// Represents BASE class for feeds contained in FeedsModel. +// NOTE: This class should be derived to create PARTICULAR feed types. +class StandardFeed : public Feed { + Q_OBJECT + + public: + // Describes possible types of feeds. + // NOTE: This is equivalent to attribute Feeds(type). + enum Type { + Rss0X = 0, + Rss2X = 1, + Rdf = 2, // Sometimes denoted as RSS 1.0. + Atom10 = 3 + }; + + // Constructors and destructors. + explicit StandardFeed(RootItem* parent_item = nullptr); + explicit StandardFeed(const StandardFeed& other); + explicit StandardFeed(const QSqlRecord& record); + virtual ~StandardFeed(); + + StandardServiceRoot* serviceRoot() const; + QList contextMenu(); + + bool canBeEdited() const { + return true; + } + + bool canBeDeleted() const { + return true; + } + + bool editViaGui(); + bool deleteViaGui(); + + bool markAsReadUnread(ReadStatus status); + bool cleanMessages(bool clean_read_only); + + QVariant data(int column, int role) const; + + // Obtains data related to this feed. + Qt::ItemFlags additionalFlags() const; + bool performDragDropChange(RootItem* target_item); + + // Removes this standard feed from persistent + // storage. + bool removeItself(); + bool addItself(RootItem* parent); + bool editItself(StandardFeed* new_feed_data); + + // Other getters/setters. + inline Type type() const { + return m_type; + } + + inline void setType(Type type) { + m_type = type; + } + + inline bool passwordProtected() const { + return m_passwordProtected; + } + + inline void setPasswordProtected(bool passwordProtected) { + m_passwordProtected = passwordProtected; + } + + inline QString username() const { + return m_username; + } + + inline void setUsername(const QString& username) { + m_username = username; + } + + inline QString password() const { + return m_password; + } + + inline void setPassword(const QString& password) { + m_password = password; + } + + inline QString encoding() const { + return m_encoding; + } + + inline void setEncoding(const QString& encoding) { + m_encoding = encoding; + } + + QNetworkReply::NetworkError networkError() const; + + // Tries to guess feed hidden under given URL + // and uses given credentials. + // Returns pointer to guessed feed (if at least partially + // guessed) and retrieved error/status code from network layer + // or NULL feed. + static QPair guessFeed(const QString& url, + const QString& username = QString(), + const QString& password = QString()); + + // Converts particular feed type to string. + static QString typeToString(Type type); + + public slots: + // Fetches metadata for the feed. + void fetchMetadataForItself(); + + private: + QList obtainNewMessages(bool* error_during_obtaining); + + private: + bool m_passwordProtected; + QString m_username; + QString m_password; + + Type m_type; + QNetworkReply::NetworkError m_networkError; + QString m_encoding; +}; + +Q_DECLARE_METATYPE(StandardFeed::Type) + +#endif // FEEDSMODELFEED_H diff --git a/src/services/standard/standardfeedsimportexportmodel.cpp b/src/services/standard/standardfeedsimportexportmodel.cpp index 432ed561f..b9d4f7151 100755 --- a/src/services/standard/standardfeedsimportexportmodel.cpp +++ b/src/services/standard/standardfeedsimportexportmodel.cpp @@ -31,311 +31,309 @@ #include -FeedsImportExportModel::FeedsImportExportModel(QObject *parent) - : AccountCheckModel(parent), m_mode(Import) { +FeedsImportExportModel::FeedsImportExportModel(QObject* parent) + : AccountCheckModel(parent), m_mode(Import) { } FeedsImportExportModel::~FeedsImportExportModel() { - if (m_rootItem != nullptr && m_mode == Import) { - // Delete all model items, but only if we are in import mode. Export mode shares - // root item with main feed model, thus cannot be deleted from memory now. - delete m_rootItem; - } + if (m_rootItem != nullptr && m_mode == Import) { + // Delete all model items, but only if we are in import mode. Export mode shares + // root item with main feed model, thus cannot be deleted from memory now. + delete m_rootItem; + } } -bool FeedsImportExportModel::exportToOMPL20(QByteArray &result) { - QDomDocument opml_document; - QDomProcessingInstruction xml_declaration = opml_document.createProcessingInstruction(QSL("xml"), - QSL("version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"")); - opml_document.appendChild(xml_declaration); +bool FeedsImportExportModel::exportToOMPL20(QByteArray& result) { + QDomDocument opml_document; + QDomProcessingInstruction xml_declaration = opml_document.createProcessingInstruction(QSL("xml"), + QSL("version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"")); + opml_document.appendChild(xml_declaration); + // Added OPML 2.0 metadata. + opml_document.appendChild(opml_document.createElement(QSL("opml"))); + opml_document.documentElement().setAttribute(QSL("version"), QSL("version")); + opml_document.documentElement().setAttribute("xmlns:rssguard", APP_URL); + QDomElement elem_opml_head = opml_document.createElement(QSL("head")); + QDomElement elem_opml_title = opml_document.createElement(QSL("title")); + QDomText text_opml_title = opml_document.createTextNode(QString(APP_NAME)); + elem_opml_title.appendChild(text_opml_title); + elem_opml_head.appendChild(elem_opml_title); + QDomElement elem_opml_created = opml_document.createElement(QSL("dateCreated")); + QDomText text_opml_created = opml_document.createTextNode(QLocale::c().toString(QDateTime::currentDateTimeUtc(), + QSL("ddd, dd MMM yyyy hh:mm:ss")) + QL1S(" GMT")); + elem_opml_created.appendChild(text_opml_created); + elem_opml_head.appendChild(elem_opml_created); + opml_document.documentElement().appendChild(elem_opml_head); + QDomElement elem_opml_body = opml_document.createElement(QSL("body")); + QStack items_to_process; + items_to_process.push(m_rootItem); + QStack elements_to_use; + elements_to_use.push(elem_opml_body); - // Added OPML 2.0 metadata. - opml_document.appendChild(opml_document.createElement(QSL("opml"))); - opml_document.documentElement().setAttribute(QSL("version"), QSL("version")); - opml_document.documentElement().setAttribute("xmlns:rssguard", APP_URL); + // Process all unprocessed nodes. + while (!items_to_process.isEmpty()) { + QDomElement active_element = elements_to_use.pop(); + RootItem* active_item = items_to_process.pop(); - QDomElement elem_opml_head = opml_document.createElement(QSL("head")); + foreach (RootItem* child_item, active_item->childItems()) { + if (!isItemChecked(child_item)) { + continue; + } - QDomElement elem_opml_title = opml_document.createElement(QSL("title")); - QDomText text_opml_title = opml_document.createTextNode(QString(APP_NAME)); - elem_opml_title.appendChild(text_opml_title); - elem_opml_head.appendChild(elem_opml_title); + switch (child_item->kind()) { + case RootItemKind::Category: { + QDomElement outline_category = opml_document.createElement(QSL("outline")); + outline_category.setAttribute(QSL("text"), child_item->title()); + outline_category.setAttribute(QSL("description"), child_item->description()); + outline_category.setAttribute(QSL("rssguard:icon"), QString(qApp->icons()->toByteArray(child_item->icon()))); + active_element.appendChild(outline_category); + items_to_process.push(child_item); + elements_to_use.push(outline_category); + break; + } - QDomElement elem_opml_created = opml_document.createElement(QSL("dateCreated")); - QDomText text_opml_created = opml_document.createTextNode(QLocale::c().toString(QDateTime::currentDateTimeUtc(), - QSL("ddd, dd MMM yyyy hh:mm:ss")) + QL1S(" GMT")); - elem_opml_created.appendChild(text_opml_created); - elem_opml_head.appendChild(elem_opml_created); - opml_document.documentElement().appendChild(elem_opml_head); + case RootItemKind::Feed: { + StandardFeed* child_feed = static_cast(child_item); + QDomElement outline_feed = opml_document.createElement("outline"); + outline_feed.setAttribute(QSL("text"), child_feed->title()); + outline_feed.setAttribute(QSL("xmlUrl"), child_feed->url()); + outline_feed.setAttribute(QSL("description"), child_feed->description()); + outline_feed.setAttribute(QSL("encoding"), child_feed->encoding()); + outline_feed.setAttribute(QSL("title"), child_feed->title()); + outline_feed.setAttribute(QSL("rssguard:icon"), QString(qApp->icons()->toByteArray(child_feed->icon()))); - QDomElement elem_opml_body = opml_document.createElement(QSL("body")); - QStack items_to_process; items_to_process.push(m_rootItem); - QStack elements_to_use; elements_to_use.push(elem_opml_body); + switch (child_feed->type()) { + case StandardFeed::Rss0X: + case StandardFeed::Rss2X: + outline_feed.setAttribute(QSL("version"), QSL("RSS")); + break; - // Process all unprocessed nodes. - while (!items_to_process.isEmpty()) { - QDomElement active_element = elements_to_use.pop(); - RootItem *active_item = items_to_process.pop(); + case StandardFeed::Rdf: + outline_feed.setAttribute(QSL("version"), QSL("RSS1")); + break; - foreach (RootItem *child_item, active_item->childItems()) { - if (!isItemChecked(child_item)) { - continue; - } + case StandardFeed::Atom10: + outline_feed.setAttribute(QSL("version"), QSL("ATOM")); + break; - switch (child_item->kind()) { - case RootItemKind::Category: { - QDomElement outline_category = opml_document.createElement(QSL("outline")); - outline_category.setAttribute(QSL("text"), child_item->title()); - outline_category.setAttribute(QSL("description"), child_item->description()); - outline_category.setAttribute(QSL("rssguard:icon"), QString(qApp->icons()->toByteArray(child_item->icon()))); - active_element.appendChild(outline_category); - items_to_process.push(child_item); - elements_to_use.push(outline_category); - break; - } + default: + break; + } - case RootItemKind::Feed: { - StandardFeed *child_feed = static_cast(child_item); - QDomElement outline_feed = opml_document.createElement("outline"); - outline_feed.setAttribute(QSL("text"), child_feed->title()); - outline_feed.setAttribute(QSL("xmlUrl"), child_feed->url()); - outline_feed.setAttribute(QSL("description"), child_feed->description()); - outline_feed.setAttribute(QSL("encoding"), child_feed->encoding()); - outline_feed.setAttribute(QSL("title"), child_feed->title()); - outline_feed.setAttribute(QSL("rssguard:icon"), QString(qApp->icons()->toByteArray(child_feed->icon()))); + active_element.appendChild(outline_feed); + break; + } - switch (child_feed->type()) { - case StandardFeed::Rss0X: - case StandardFeed::Rss2X: - outline_feed.setAttribute(QSL("version"), QSL("RSS")); - break; + default: + break; + } + } + } - case StandardFeed::Rdf: - outline_feed.setAttribute(QSL("version"), QSL("RSS1")); - break; - - case StandardFeed::Atom10: - outline_feed.setAttribute(QSL("version"), QSL("ATOM")); - break; - - default: - break; - } - - active_element.appendChild(outline_feed); - break; - } - - default: - break; - } - } - } - - opml_document.documentElement().appendChild(elem_opml_body); - result = opml_document.toByteArray(2); - return true; + opml_document.documentElement().appendChild(elem_opml_body); + result = opml_document.toByteArray(2); + return true; } -void FeedsImportExportModel::importAsOPML20(const QByteArray &data, bool fetch_metadata_online) { - emit parsingStarted(); - emit layoutAboutToBeChanged(); - setRootItem(nullptr); - emit layoutChanged(); +void FeedsImportExportModel::importAsOPML20(const QByteArray& data, bool fetch_metadata_online) { + emit parsingStarted(); + emit layoutAboutToBeChanged(); + setRootItem(nullptr); + emit layoutChanged(); + QDomDocument opml_document; - QDomDocument opml_document; + if (!opml_document.setContent(data)) { + emit parsingFinished(0, 0, true); + } - if (!opml_document.setContent(data)) { - emit parsingFinished(0, 0, true); - } + if (opml_document.documentElement().isNull() || opml_document.documentElement().tagName() != QSL("opml") || + opml_document.documentElement().elementsByTagName(QSL("body")).size() != 1) { + // This really is not an OPML file. + emit parsingFinished(0, 0, true); + } - if (opml_document.documentElement().isNull() || opml_document.documentElement().tagName() != QSL("opml") || - opml_document.documentElement().elementsByTagName(QSL("body")).size() != 1) { - // This really is not an OPML file. - emit parsingFinished(0, 0, true); - } + int completed = 0, total = 0, succeded = 0, failed = 0; + StandardServiceRoot* root_item = new StandardServiceRoot(); + QStack model_items; + model_items.push(root_item); + QStack elements_to_process; + elements_to_process.push(opml_document.documentElement().elementsByTagName(QSL("body")).at(0).toElement()); - int completed = 0, total = 0, succeded = 0, failed = 0; - StandardServiceRoot *root_item = new StandardServiceRoot(); - QStack model_items; model_items.push(root_item); - QStack elements_to_process; elements_to_process.push(opml_document.documentElement().elementsByTagName(QSL("body")).at(0).toElement()); + while (!elements_to_process.isEmpty()) { + RootItem* active_model_item = model_items.pop(); + QDomElement active_element = elements_to_process.pop(); + int current_count = active_element.childNodes().size(); + total += current_count; - while (!elements_to_process.isEmpty()) { - RootItem *active_model_item = model_items.pop(); - QDomElement active_element = elements_to_process.pop(); + for (int i = 0; i < current_count; i++) { + QDomNode child = active_element.childNodes().at(i); - int current_count = active_element.childNodes().size(); - total += current_count; + if (child.isElement()) { + QDomElement child_element = child.toElement(); - for (int i = 0; i < current_count; i++) { - QDomNode child = active_element.childNodes().at(i); + // Now analyze if this element is category or feed. + // NOTE: All feeds must include xmlUrl attribute and text attribute. + if (child_element.attributes().contains(QSL("xmlUrl")) && child.attributes().contains(QSL("text"))) { + // This is FEED. + // Add feed and end this iteration. + QString feed_url = child_element.attribute(QSL("xmlUrl")); - if (child.isElement()) { - QDomElement child_element = child.toElement(); + if (!feed_url.isEmpty()) { + QPair guessed; - // Now analyze if this element is category or feed. - // NOTE: All feeds must include xmlUrl attribute and text attribute. - if (child_element.attributes().contains(QSL("xmlUrl")) && child.attributes().contains(QSL("text"))) { - // This is FEED. - // Add feed and end this iteration. - QString feed_url = child_element.attribute(QSL("xmlUrl")); + if (fetch_metadata_online && + (guessed = StandardFeed::guessFeed(feed_url)).second == QNetworkReply::NoError) { + // We should obtain fresh metadata from online feed source. + guessed.first->setUrl(feed_url); + active_model_item->appendChild(guessed.first); + succeded++; + } - if (!feed_url.isEmpty()) { - QPair guessed; + else { + QString feed_title = child_element.attribute(QSL("text")); + QString feed_encoding = child_element.attribute(QSL("encoding"), DEFAULT_FEED_ENCODING); + QString feed_type = child_element.attribute(QSL("version"), DEFAULT_FEED_TYPE).toUpper(); + QString feed_description = child_element.attribute(QSL("description")); + QIcon feed_icon = qApp->icons()->fromByteArray(child_element.attribute(QSL("rssguard:icon")).toLocal8Bit()); + StandardFeed* new_feed = new StandardFeed(active_model_item); + new_feed->setTitle(feed_title); + new_feed->setDescription(feed_description); + new_feed->setEncoding(feed_encoding); + new_feed->setUrl(feed_url); + new_feed->setCreationDate(QDateTime::currentDateTime()); + new_feed->setIcon(feed_icon.isNull() ? qApp->icons()->fromTheme(QSL("application-rss+xml")) : feed_icon); - if (fetch_metadata_online && - (guessed = StandardFeed::guessFeed(feed_url)).second == QNetworkReply::NoError) { - // We should obtain fresh metadata from online feed source. - guessed.first->setUrl(feed_url); - active_model_item->appendChild(guessed.first); + if (feed_type == QL1S("RSS1")) { + new_feed->setType(StandardFeed::Rdf); + } - succeded++; - } - else { - QString feed_title = child_element.attribute(QSL("text")); - QString feed_encoding = child_element.attribute(QSL("encoding"), DEFAULT_FEED_ENCODING); - QString feed_type = child_element.attribute(QSL("version"), DEFAULT_FEED_TYPE).toUpper(); - QString feed_description = child_element.attribute(QSL("description")); - QIcon feed_icon = qApp->icons()->fromByteArray(child_element.attribute(QSL("rssguard:icon")).toLocal8Bit()); + else if (feed_type == QL1S("ATOM")) { + new_feed->setType(StandardFeed::Atom10); + } - StandardFeed *new_feed = new StandardFeed(active_model_item); - new_feed->setTitle(feed_title); - new_feed->setDescription(feed_description); - new_feed->setEncoding(feed_encoding); - new_feed->setUrl(feed_url); - new_feed->setCreationDate(QDateTime::currentDateTime()); - new_feed->setIcon(feed_icon.isNull() ? qApp->icons()->fromTheme(QSL("application-rss+xml")) : feed_icon); + else { + new_feed->setType(StandardFeed::Rss2X); + } - if (feed_type == QL1S("RSS1")) { - new_feed->setType(StandardFeed::Rdf); - } - else if (feed_type == QL1S("ATOM")) { - new_feed->setType(StandardFeed::Atom10); - } - else { - new_feed->setType(StandardFeed::Rss2X); - } + active_model_item->appendChild(new_feed); - active_model_item->appendChild(new_feed); + if (fetch_metadata_online && guessed.second != QNetworkReply::NoError) { + failed++; + } - if (fetch_metadata_online && guessed.second != QNetworkReply::NoError) { - failed++; - } - else { - succeded++; - } - } - } - } - else { - // This must be CATEGORY. - // Add category and continue. - QString category_title = child_element.attribute(QSL("text")); - QString category_description = child_element.attribute(QSL("description")); - QIcon category_icon = qApp->icons()->fromByteArray(child_element.attribute(QSL("rssguard:icon")).toLocal8Bit()); + else { + succeded++; + } + } + } + } - if (category_title.isEmpty()) { - qWarning("Given OMPL file provided category without valid text attribute. Using fallback name."); + else { + // This must be CATEGORY. + // Add category and continue. + QString category_title = child_element.attribute(QSL("text")); + QString category_description = child_element.attribute(QSL("description")); + QIcon category_icon = qApp->icons()->fromByteArray(child_element.attribute(QSL("rssguard:icon")).toLocal8Bit()); - category_title = child_element.attribute(QSL("title")); + if (category_title.isEmpty()) { + qWarning("Given OMPL file provided category without valid text attribute. Using fallback name."); + category_title = child_element.attribute(QSL("title")); - if (category_title.isEmpty()) { - category_title = tr("Category ") + QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch()); - } - } + if (category_title.isEmpty()) { + category_title = tr("Category ") + QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch()); + } + } - StandardCategory *new_category = new StandardCategory(active_model_item); - new_category->setTitle(category_title); - new_category->setIcon(category_icon.isNull() ? qApp->icons()->fromTheme(QSL("folder")) : category_icon); - new_category->setCreationDate(QDateTime::currentDateTime()); - new_category->setDescription(category_description); + StandardCategory* new_category = new StandardCategory(active_model_item); + new_category->setTitle(category_title); + new_category->setIcon(category_icon.isNull() ? qApp->icons()->fromTheme(QSL("folder")) : category_icon); + new_category->setCreationDate(QDateTime::currentDateTime()); + new_category->setDescription(category_description); + active_model_item->appendChild(new_category); + // Children of this node must be processed later. + elements_to_process.push(child_element); + model_items.push(new_category); + } - active_model_item->appendChild(new_category); + emit parsingProgress(++completed, total); + } + } + } - // Children of this node must be processed later. - elements_to_process.push(child_element); - model_items.push(new_category); - } - - emit parsingProgress(++completed, total); - } - } - } - - // Now, XML is processed and we have result in form of pointer item structure. - emit layoutAboutToBeChanged(); - setRootItem(root_item); - emit layoutChanged(); - emit parsingFinished(failed, succeded, false); + // Now, XML is processed and we have result in form of pointer item structure. + emit layoutAboutToBeChanged(); + setRootItem(root_item); + emit layoutChanged(); + emit parsingFinished(failed, succeded, false); } -bool FeedsImportExportModel::exportToTxtURLPerLine(QByteArray &result) { - foreach (const Feed * const feed, m_rootItem->getSubTreeFeeds()) { - result += feed->url() + QL1S("\n"); - } +bool FeedsImportExportModel::exportToTxtURLPerLine(QByteArray& result) { + foreach (const Feed* const feed, m_rootItem->getSubTreeFeeds()) { + result += feed->url() + QL1S("\n"); + } - return true; + return true; } -void FeedsImportExportModel::importAsTxtURLPerLine(const QByteArray &data, bool fetch_metadata_online) { - emit parsingStarted(); - emit layoutAboutToBeChanged(); - setRootItem(nullptr); - emit layoutChanged(); +void FeedsImportExportModel::importAsTxtURLPerLine(const QByteArray& data, bool fetch_metadata_online) { + emit parsingStarted(); + emit layoutAboutToBeChanged(); + setRootItem(nullptr); + emit layoutChanged(); + int completed = 0, succeded = 0, failed = 0; + StandardServiceRoot* root_item = new StandardServiceRoot(); + QList urls = data.split('\n'); - int completed = 0, succeded = 0, failed = 0; - StandardServiceRoot *root_item = new StandardServiceRoot(); - QList urls = data.split('\n'); + foreach (const QByteArray& url, urls) { + if (!url.isEmpty()) { + QPair guessed; - foreach (const QByteArray &url, urls) { - if (!url.isEmpty()) { - QPair guessed; + if (fetch_metadata_online && + (guessed = StandardFeed::guessFeed(url)).second == QNetworkReply::NoError) { + guessed.first->setUrl(url); + root_item->appendChild(guessed.first); + succeded++; + } - if (fetch_metadata_online && - (guessed = StandardFeed::guessFeed(url)).second == QNetworkReply::NoError) { - guessed.first->setUrl(url); - root_item->appendChild(guessed.first); - succeded++; - } - else { - StandardFeed *feed = new StandardFeed(); + else { + StandardFeed* feed = new StandardFeed(); + feed->setUrl(url); + feed->setTitle(url); + feed->setCreationDate(QDateTime::currentDateTime()); + feed->setIcon(qApp->icons()->fromTheme(QSL("application-rss+xml"))); + feed->setEncoding(DEFAULT_FEED_ENCODING); + root_item->appendChild(feed); - feed->setUrl(url); - feed->setTitle(url); - feed->setCreationDate(QDateTime::currentDateTime()); - feed->setIcon(qApp->icons()->fromTheme(QSL("application-rss+xml"))); - feed->setEncoding(DEFAULT_FEED_ENCODING); - root_item->appendChild(feed); + if (fetch_metadata_online && guessed.second != QNetworkReply::NoError) { + failed++; + } - if (fetch_metadata_online && guessed.second != QNetworkReply::NoError) { - failed++; - } - else { - succeded++; - } - } + else { + succeded++; + } + } - qApp->processEvents(); - } - else { - qWarning("Detected empty URL when parsing input TXT [one URL per line] data."); - failed++; - } + qApp->processEvents(); + } - emit parsingProgress(++completed, urls.size()); - } + else { + qWarning("Detected empty URL when parsing input TXT [one URL per line] data."); + failed++; + } - // Now, XML is processed and we have result in form of pointer item structure. - emit layoutAboutToBeChanged(); - setRootItem(root_item); - emit layoutChanged(); - emit parsingFinished(failed, succeded, false); + emit parsingProgress(++completed, urls.size()); + } + + // Now, XML is processed and we have result in form of pointer item structure. + emit layoutAboutToBeChanged(); + setRootItem(root_item); + emit layoutChanged(); + emit parsingFinished(failed, succeded, false); } FeedsImportExportModel::Mode FeedsImportExportModel::mode() const { - return m_mode; + return m_mode; } -void FeedsImportExportModel::setMode(const FeedsImportExportModel::Mode &mode) { - m_mode = mode; +void FeedsImportExportModel::setMode(const FeedsImportExportModel::Mode& mode) { + m_mode = mode; } diff --git a/src/services/standard/standardfeedsimportexportmodel.h b/src/services/standard/standardfeedsimportexportmodel.h index bcdb6088a..bb440d9ca 100755 --- a/src/services/standard/standardfeedsimportexportmodel.h +++ b/src/services/standard/standardfeedsimportexportmodel.h @@ -22,40 +22,40 @@ class FeedsImportExportModel : public AccountCheckModel { - Q_OBJECT + Q_OBJECT - public: - enum Mode { - Import, - Export - }; + public: + enum Mode { + Import, + Export + }; - // Constructors and destructors. - explicit FeedsImportExportModel(QObject *parent = 0); - virtual ~FeedsImportExportModel(); + // Constructors and destructors. + explicit FeedsImportExportModel(QObject* parent = 0); + virtual ~FeedsImportExportModel(); - // Exports to OPML 2.0 - // NOTE: http://dev.opml.org/spec2.html - bool exportToOMPL20(QByteArray &result); - void importAsOPML20(const QByteArray &data, bool fetch_metadata_online); + // Exports to OPML 2.0 + // NOTE: http://dev.opml.org/spec2.html + bool exportToOMPL20(QByteArray& result); + void importAsOPML20(const QByteArray& data, bool fetch_metadata_online); - // Exports to plain text format - // where there is one feed URL per line. - bool exportToTxtURLPerLine(QByteArray &result); - void importAsTxtURLPerLine(const QByteArray &data, bool fetch_metadata_online); + // Exports to plain text format + // where there is one feed URL per line. + bool exportToTxtURLPerLine(QByteArray& result); + void importAsTxtURLPerLine(const QByteArray& data, bool fetch_metadata_online); - Mode mode() const; - void setMode(const Mode &mode); + Mode mode() const; + void setMode(const Mode& mode); - signals: - // These signals are emitted when user selects some data - // to be imported/parsed into the model. - void parsingStarted(); - void parsingProgress(int completed, int total); - void parsingFinished(int count_failed, int count_succeeded, bool parsing_error); + signals: + // These signals are emitted when user selects some data + // to be imported/parsed into the model. + void parsingStarted(); + void parsingProgress(int completed, int total); + void parsingFinished(int count_failed, int count_succeeded, bool parsing_error); - private: - Mode m_mode; + private: + Mode m_mode; }; #endif // STANDARDFEEDSIMPORTEXPORTMODEL_H diff --git a/src/services/standard/standardserviceentrypoint.cpp b/src/services/standard/standardserviceentrypoint.cpp index e226eda9c..351a01871 100755 --- a/src/services/standard/standardserviceentrypoint.cpp +++ b/src/services/standard/standardserviceentrypoint.cpp @@ -1,82 +1,82 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - - -#include "services/standard/standardserviceentrypoint.h" - -#include "definitions/definitions.h" -#include "miscellaneous/application.h" -#include "miscellaneous/databasequeries.h" -#include "services/standard/standardserviceroot.h" - - -StandardServiceEntryPoint::StandardServiceEntryPoint() { -} - -StandardServiceEntryPoint::~StandardServiceEntryPoint() { -} - -bool StandardServiceEntryPoint::isSingleInstanceService() const { - return true; -} - -QString StandardServiceEntryPoint::name() const { - return QObject::tr("Standard online feeds (RSS/RDF/ATOM)"); -} - -QString StandardServiceEntryPoint::description() const { - return QObject::tr("This service offers integration with standard online RSS/RDF/ATOM feeds and podcasts."); -} - -QString StandardServiceEntryPoint::version() const { - return APP_VERSION; -} - -QString StandardServiceEntryPoint::author() const { - return APP_AUTHOR; -} - -QIcon StandardServiceEntryPoint::icon() const { - return QIcon(APP_ICON_PATH); -} - -QString StandardServiceEntryPoint::code() const { - return SERVICE_CODE_STD_RSS; -} - -ServiceRoot *StandardServiceEntryPoint::createNewRoot() const { - // Switch DB. - QSqlDatabase database = qApp->database()->connection(QSL("StandardServiceEntryPoint"), DatabaseFactory::FromSettings); - bool ok; - int new_id = DatabaseQueries::createAccount(database, code(), &ok); - - if (ok) { - StandardServiceRoot *root = new StandardServiceRoot(); - root->setAccountId(new_id); - return root; - } - else { - return nullptr; - } -} - -QList StandardServiceEntryPoint::initializeSubtree() const { - // Check DB if standard account is enabled. - QSqlDatabase database = qApp->database()->connection(QSL("StandardServiceEntryPoint"), DatabaseFactory::FromSettings); - - return DatabaseQueries::getAccounts(database); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + + +#include "services/standard/standardserviceentrypoint.h" + +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "miscellaneous/databasequeries.h" +#include "services/standard/standardserviceroot.h" + + +StandardServiceEntryPoint::StandardServiceEntryPoint() { +} + +StandardServiceEntryPoint::~StandardServiceEntryPoint() { +} + +bool StandardServiceEntryPoint::isSingleInstanceService() const { + return true; +} + +QString StandardServiceEntryPoint::name() const { + return QObject::tr("Standard online feeds (RSS/RDF/ATOM)"); +} + +QString StandardServiceEntryPoint::description() const { + return QObject::tr("This service offers integration with standard online RSS/RDF/ATOM feeds and podcasts."); +} + +QString StandardServiceEntryPoint::version() const { + return APP_VERSION; +} + +QString StandardServiceEntryPoint::author() const { + return APP_AUTHOR; +} + +QIcon StandardServiceEntryPoint::icon() const { + return QIcon(APP_ICON_PATH); +} + +QString StandardServiceEntryPoint::code() const { + return SERVICE_CODE_STD_RSS; +} + +ServiceRoot* StandardServiceEntryPoint::createNewRoot() const { + // Switch DB. + QSqlDatabase database = qApp->database()->connection(QSL("StandardServiceEntryPoint"), DatabaseFactory::FromSettings); + bool ok; + int new_id = DatabaseQueries::createAccount(database, code(), &ok); + + if (ok) { + StandardServiceRoot* root = new StandardServiceRoot(); + root->setAccountId(new_id); + return root; + } + + else { + return nullptr; + } +} + +QList StandardServiceEntryPoint::initializeSubtree() const { + // Check DB if standard account is enabled. + QSqlDatabase database = qApp->database()->connection(QSL("StandardServiceEntryPoint"), DatabaseFactory::FromSettings); + return DatabaseQueries::getAccounts(database); +} diff --git a/src/services/standard/standardserviceentrypoint.h b/src/services/standard/standardserviceentrypoint.h index a0ee111aa..172cd2c02 100755 --- a/src/services/standard/standardserviceentrypoint.h +++ b/src/services/standard/standardserviceentrypoint.h @@ -1,41 +1,41 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef STANDARDSERVICEENTRYPOINT_H -#define STANDARDSERVICEENTRYPOINT_H - -#include "services/abstract/serviceentrypoint.h" - - -class StandardServiceEntryPoint : public ServiceEntryPoint { - public: - explicit StandardServiceEntryPoint(); - virtual ~StandardServiceEntryPoint(); - - bool isSingleInstanceService() const; - QString name() const; - QString description() const; - QString version() const; - QString author() const; - QIcon icon() const; - QString code() const; - - ServiceRoot *createNewRoot() const; - QList initializeSubtree() const; -}; - -#endif // STANDARDSERVICEENTRYPOINT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef STANDARDSERVICEENTRYPOINT_H +#define STANDARDSERVICEENTRYPOINT_H + +#include "services/abstract/serviceentrypoint.h" + + +class StandardServiceEntryPoint : public ServiceEntryPoint { + public: + explicit StandardServiceEntryPoint(); + virtual ~StandardServiceEntryPoint(); + + bool isSingleInstanceService() const; + QString name() const; + QString description() const; + QString version() const; + QString author() const; + QIcon icon() const; + QString code() const; + + ServiceRoot* createNewRoot() const; + QList initializeSubtree() const; +}; + +#endif // STANDARDSERVICEENTRYPOINT_H diff --git a/src/services/standard/standardserviceroot.cpp b/src/services/standard/standardserviceroot.cpp index 0c0dfd2cd..ef07d9ae1 100755 --- a/src/services/standard/standardserviceroot.cpp +++ b/src/services/standard/standardserviceroot.cpp @@ -1,360 +1,360 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/standard/standardserviceroot.h" - -#include "definitions/definitions.h" -#include "miscellaneous/application.h" -#include "miscellaneous/settings.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/databasequeries.h" -#include "miscellaneous/mutex.h" -#include "core/feedsmodel.h" -#include "gui/messagebox.h" -#include "exceptions/applicationexception.h" -#include "services/abstract/recyclebin.h" -#include "services/abstract/labelsrootitem.h" -#include "services/standard/standardserviceentrypoint.h" -#include "services/standard/standardfeed.h" -#include "services/standard/standardcategory.h" -#include "services/standard/standardfeedsimportexportmodel.h" -#include "services/standard/gui/formstandardcategorydetails.h" -#include "services/standard/gui/formstandardfeeddetails.h" -#include "services/standard/gui/formstandardimportexport.h" - -#include -#include -#include -#include - - -StandardServiceRoot::StandardServiceRoot(RootItem *parent) - : ServiceRoot(parent), m_recycleBin(new RecycleBin(this)), - m_actionExportFeeds(nullptr), m_actionImportFeeds(nullptr), m_serviceMenu(QList()), - m_feedContextMenu(QList()), m_actionFeedFetchMetadata(nullptr) { - - setTitle(qApp->system()->getUsername() + QL1S("@") + QL1S(APP_LOW_NAME)); - setIcon(StandardServiceEntryPoint().icon()); - setDescription(tr("This is obligatory service account for standard RSS/RDF/ATOM feeds.")); -} - -StandardServiceRoot::~StandardServiceRoot() { - qDeleteAll(m_serviceMenu); - qDeleteAll(m_feedContextMenu); -} - -void StandardServiceRoot::start(bool freshly_activated) { - loadFromDatabase(); - - if (freshly_activated) { - // In other words, if there are no feeds or categories added. - if (MessageBox::show(qApp->mainFormWidget(), QMessageBox::Question, QObject::tr("Load initial set of feeds"), - tr("This new account does not include any feeds. You can now add default set of feeds."), - tr("Do you want to load initial set of feeds?"), - QString(), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - QString target_opml_file = APP_INITIAL_FEEDS_PATH + QDir::separator() + FEED_INITIAL_OPML_PATTERN; - QString current_locale = qApp->localization()->loadedLanguage(); - QString file_to_load; - - if (QFile::exists(target_opml_file.arg(current_locale))) { - file_to_load = target_opml_file.arg(current_locale); - } - else if (QFile::exists(target_opml_file.arg(DEFAULT_LOCALE))) { - file_to_load = target_opml_file.arg(DEFAULT_LOCALE); - } - - FeedsImportExportModel model; - QString output_msg; - - try { - model.importAsOPML20(IOFactory::readTextFile(file_to_load), false); - model.checkAllItems(); - - if (mergeImportExportModel(&model, this, output_msg)) { - requestItemExpand(getSubTree(), true); - } - } - catch (ApplicationException &ex) { - MessageBox::show(qApp->mainFormWidget(), QMessageBox::Critical, tr("Error when loading initial feeds"), ex.message()); - } - } - } - - checkArgumentsForFeedAdding(); -} - -void StandardServiceRoot::stop() { - qDebug("Stopping StandardServiceRoot instance."); -} - -QString StandardServiceRoot::code() const { - return StandardServiceEntryPoint().code(); -} - -bool StandardServiceRoot::canBeEdited() const { - return false; -} - -bool StandardServiceRoot::canBeDeleted() const { - return true; -} - -bool StandardServiceRoot::deleteViaGui() { - return ServiceRoot::deleteViaGui(); -} - -bool StandardServiceRoot::supportsFeedAdding() const { - return true; -} - -bool StandardServiceRoot::supportsCategoryAdding() const { - return true; -} - -void StandardServiceRoot::addNewFeed(const QString &url) { - if (!qApp->feedUpdateLock()->tryLock()) { - // Lock was not obtained because - // it is used probably by feed updater or application - // is quitting. - qApp->showGuiMessage(tr("Cannot add item"), - tr("Cannot add feed because another critical operation is ongoing."), - QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); - // Thus, cannot delete and quit the method. - return; - } - - QScopedPointer form_pointer(new FormStandardFeedDetails(this, qApp->mainFormWidget())); - form_pointer.data()->addEditFeed(nullptr, nullptr, url); - - qApp->feedUpdateLock()->unlock(); -} - -QVariant StandardServiceRoot::data(int column, int role) const { - switch (role) { - case Qt::ToolTipRole: - if (column == FDS_MODEL_TITLE_INDEX) { - return tr("This is service account for standard RSS/RDF/ATOM feeds.\n\nAccount ID: %1").arg(accountId()); - } - else { - return ServiceRoot::data(column, role); - } - - default: - return ServiceRoot::data(column, role); - } -} - -Qt::ItemFlags StandardServiceRoot::additionalFlags() const { - return Qt::ItemIsDropEnabled; -} - -RecycleBin *StandardServiceRoot::recycleBin() const { - return m_recycleBin; -} - -void StandardServiceRoot::loadFromDatabase(){ - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - Assignment categories = DatabaseQueries::getCategories(database, accountId()); - Assignment feeds = DatabaseQueries::getFeeds(database, accountId()); - - // All data are now obtained, lets create the hierarchy. - assembleCategories(categories); - assembleFeeds(feeds); - - // As the last item, add recycle bin, which is needed. - appendChild(recycleBin()); - - //appendChild(new LabelsRootItem(this)); - - updateCounts(true); -} - -void StandardServiceRoot::checkArgumentsForFeedAdding() { - foreach (const QString &arg, qApp->arguments().mid(1)) { - checkArgumentForFeedAdding(arg); - } -} - -QMap StandardServiceRoot::storeCustomFeedsData() { - return QMap(); -} - -void StandardServiceRoot::restoreCustomFeedsData(const QMap &data, const QHash &feeds) { - Q_UNUSED(feeds) - Q_UNUSED(data) -} - -QString StandardServiceRoot::processFeedUrl(const QString &feed_url) { - if (feed_url.startsWith(QL1S(URI_SCHEME_FEED_SHORT))) { - QString without_feed_prefix = feed_url.mid(5); - - if (without_feed_prefix.startsWith(QL1S("https:")) || without_feed_prefix.startsWith(QL1S("http:"))) { - return without_feed_prefix; - } - else { - return feed_url; - } - } - else { - return feed_url; - } -} - -void StandardServiceRoot::checkArgumentForFeedAdding(const QString &argument) { - if (argument.startsWith(QL1S(URI_SCHEME_FEED_SHORT))) { - addNewFeed(processFeedUrl(argument)); - } -} - -QList StandardServiceRoot::getContextMenuForFeed(StandardFeed *feed) { - if (m_feedContextMenu.isEmpty()) { - // Initialize. - m_actionFeedFetchMetadata = new QAction(qApp->icons()->fromTheme(QSL("emblem-downloads")), tr("Fetch metadata"), nullptr); - m_feedContextMenu.append(m_actionFeedFetchMetadata); - } - - // Make connections. - disconnect(m_actionFeedFetchMetadata, &QAction::triggered, 0, 0); - connect(m_actionFeedFetchMetadata, &QAction::triggered, feed, &StandardFeed::fetchMetadataForItself); - - return m_feedContextMenu; -} - -bool StandardServiceRoot::mergeImportExportModel(FeedsImportExportModel *model, RootItem *target_root_node, QString &output_message) { - QStack original_parents; original_parents.push(target_root_node); - QStack new_parents; new_parents.push(model->rootItem()); - bool some_feed_category_error = false; - - // Iterate all new items we would like to merge into current model. - while (!new_parents.isEmpty()) { - RootItem *target_parent = original_parents.pop(); - RootItem *source_parent = new_parents.pop(); - - foreach (RootItem *source_item, source_parent->childItems()) { - if (!model->isItemChecked(source_item)) { - // We can skip this item, because it is not checked and should not be imported. - // NOTE: All descendants are thus skipped too. - continue; - } - - if (source_item->kind() == RootItemKind::Category) { - StandardCategory *source_category = static_cast(source_item); - StandardCategory *new_category = new StandardCategory(*source_category); - QString new_category_title = new_category->title(); - - // Add category to model. - new_category->clearChildren(); - - if (new_category->addItself(target_parent)) { - requestItemReassignment(new_category, target_parent); - - // Process all children of this category. - original_parents.push(new_category); - new_parents.push(source_category); - } - else { - delete new_category; - - // Add category failed, but this can mean that the same category (with same title) - // already exists. If such a category exists in current parent, then find it and - // add descendants to it. - RootItem *existing_category = nullptr; - foreach (RootItem *child, target_parent->childItems()) { - if (child->kind() == RootItemKind::Category && child->title() == new_category_title) { - existing_category = child; - } - } - - if (existing_category != nullptr) { - original_parents.push(existing_category); - new_parents.push(source_category); - } - else { - some_feed_category_error = true; - } - } - } - else if (source_item->kind() == RootItemKind::Feed) { - StandardFeed *source_feed = static_cast(source_item); - StandardFeed *new_feed = new StandardFeed(*source_feed); - - // Append this feed and end this iteration. - if (new_feed->addItself(target_parent)) { - requestItemReassignment(new_feed, target_parent); - } - else { - delete new_feed; - some_feed_category_error = true; - } - } - } - } - - if (some_feed_category_error) { - output_message = tr("Import successful, but some feeds/categories were not imported due to error."); - } - else { - output_message = tr("Import was completely successful."); - } - - return !some_feed_category_error; -} - -void StandardServiceRoot::addNewCategory() { - if (!qApp->feedUpdateLock()->tryLock()) { - // Lock was not obtained because - // it is used probably by feed updater or application - // is quitting. - qApp->showGuiMessage(tr("Cannot add category"), - tr("Cannot add category because another critical operation is ongoing."), - QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); - // Thus, cannot delete and quit the method. - return; - } - - QScopedPointer form_pointer(new FormStandardCategoryDetails(this, qApp->mainFormWidget())); - form_pointer.data()->addEditCategory(nullptr, nullptr); - - qApp->feedUpdateLock()->unlock(); -} - -void StandardServiceRoot::importFeeds() { - QScopedPointer form(new FormStandardImportExport(this, qApp->mainFormWidget())); - form.data()->setMode(FeedsImportExportModel::Import); - form.data()->exec(); -} - -void StandardServiceRoot::exportFeeds() { - QScopedPointer form(new FormStandardImportExport(this, qApp->mainFormWidget())); - form.data()->setMode(FeedsImportExportModel::Export); - form.data()->exec(); -} - -QList StandardServiceRoot::serviceMenu() { - if (m_serviceMenu.isEmpty()) { - m_actionExportFeeds = new QAction(qApp->icons()->fromTheme("document-export"), tr("Export feeds"), this); - m_actionImportFeeds = new QAction(qApp->icons()->fromTheme("document-import"), tr("Import feeds"), this); - - connect(m_actionExportFeeds, &QAction::triggered, this, &StandardServiceRoot::exportFeeds); - connect(m_actionImportFeeds, &QAction::triggered, this, &StandardServiceRoot::importFeeds); - - m_serviceMenu.append(m_actionExportFeeds); - m_serviceMenu.append(m_actionImportFeeds); - } - - return m_serviceMenu; -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/standard/standardserviceroot.h" + +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "miscellaneous/settings.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/databasequeries.h" +#include "miscellaneous/mutex.h" +#include "core/feedsmodel.h" +#include "gui/messagebox.h" +#include "exceptions/applicationexception.h" +#include "services/abstract/recyclebin.h" +#include "services/abstract/labelsrootitem.h" +#include "services/standard/standardserviceentrypoint.h" +#include "services/standard/standardfeed.h" +#include "services/standard/standardcategory.h" +#include "services/standard/standardfeedsimportexportmodel.h" +#include "services/standard/gui/formstandardcategorydetails.h" +#include "services/standard/gui/formstandardfeeddetails.h" +#include "services/standard/gui/formstandardimportexport.h" + +#include +#include +#include +#include + + +StandardServiceRoot::StandardServiceRoot(RootItem* parent) + : ServiceRoot(parent), m_recycleBin(new RecycleBin(this)), + m_actionExportFeeds(nullptr), m_actionImportFeeds(nullptr), m_serviceMenu(QList()), + m_feedContextMenu(QList()), m_actionFeedFetchMetadata(nullptr) { + setTitle(qApp->system()->getUsername() + QL1S("@") + QL1S(APP_LOW_NAME)); + setIcon(StandardServiceEntryPoint().icon()); + setDescription(tr("This is obligatory service account for standard RSS/RDF/ATOM feeds.")); +} + +StandardServiceRoot::~StandardServiceRoot() { + qDeleteAll(m_serviceMenu); + qDeleteAll(m_feedContextMenu); +} + +void StandardServiceRoot::start(bool freshly_activated) { + loadFromDatabase(); + + if (freshly_activated) { + // In other words, if there are no feeds or categories added. + if (MessageBox::show(qApp->mainFormWidget(), QMessageBox::Question, QObject::tr("Load initial set of feeds"), + tr("This new account does not include any feeds. You can now add default set of feeds."), + tr("Do you want to load initial set of feeds?"), + QString(), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + QString target_opml_file = APP_INITIAL_FEEDS_PATH + QDir::separator() + FEED_INITIAL_OPML_PATTERN; + QString current_locale = qApp->localization()->loadedLanguage(); + QString file_to_load; + + if (QFile::exists(target_opml_file.arg(current_locale))) { + file_to_load = target_opml_file.arg(current_locale); + } + + else if (QFile::exists(target_opml_file.arg(DEFAULT_LOCALE))) { + file_to_load = target_opml_file.arg(DEFAULT_LOCALE); + } + + FeedsImportExportModel model; + QString output_msg; + + try { + model.importAsOPML20(IOFactory::readTextFile(file_to_load), false); + model.checkAllItems(); + + if (mergeImportExportModel(&model, this, output_msg)) { + requestItemExpand(getSubTree(), true); + } + } + + catch (ApplicationException& ex) { + MessageBox::show(qApp->mainFormWidget(), QMessageBox::Critical, tr("Error when loading initial feeds"), ex.message()); + } + } + } + + checkArgumentsForFeedAdding(); +} + +void StandardServiceRoot::stop() { + qDebug("Stopping StandardServiceRoot instance."); +} + +QString StandardServiceRoot::code() const { + return StandardServiceEntryPoint().code(); +} + +bool StandardServiceRoot::canBeEdited() const { + return false; +} + +bool StandardServiceRoot::canBeDeleted() const { + return true; +} + +bool StandardServiceRoot::deleteViaGui() { + return ServiceRoot::deleteViaGui(); +} + +bool StandardServiceRoot::supportsFeedAdding() const { + return true; +} + +bool StandardServiceRoot::supportsCategoryAdding() const { + return true; +} + +void StandardServiceRoot::addNewFeed(const QString& url) { + if (!qApp->feedUpdateLock()->tryLock()) { + // Lock was not obtained because + // it is used probably by feed updater or application + // is quitting. + qApp->showGuiMessage(tr("Cannot add item"), + tr("Cannot add feed because another critical operation is ongoing."), + QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); + // Thus, cannot delete and quit the method. + return; + } + + QScopedPointer form_pointer(new FormStandardFeedDetails(this, qApp->mainFormWidget())); + form_pointer.data()->addEditFeed(nullptr, nullptr, url); + qApp->feedUpdateLock()->unlock(); +} + +QVariant StandardServiceRoot::data(int column, int role) const { + switch (role) { + case Qt::ToolTipRole: + if (column == FDS_MODEL_TITLE_INDEX) { + return tr("This is service account for standard RSS/RDF/ATOM feeds.\n\nAccount ID: %1").arg(accountId()); + } + + else { + return ServiceRoot::data(column, role); + } + + default: + return ServiceRoot::data(column, role); + } +} + +Qt::ItemFlags StandardServiceRoot::additionalFlags() const { + return Qt::ItemIsDropEnabled; +} + +RecycleBin* StandardServiceRoot::recycleBin() const { + return m_recycleBin; +} + +void StandardServiceRoot::loadFromDatabase() { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + Assignment categories = DatabaseQueries::getCategories(database, accountId()); + Assignment feeds = DatabaseQueries::getFeeds(database, accountId()); + // All data are now obtained, lets create the hierarchy. + assembleCategories(categories); + assembleFeeds(feeds); + // As the last item, add recycle bin, which is needed. + appendChild(recycleBin()); + //appendChild(new LabelsRootItem(this)); + updateCounts(true); +} + +void StandardServiceRoot::checkArgumentsForFeedAdding() { + foreach (const QString& arg, qApp->arguments().mid(1)) { + checkArgumentForFeedAdding(arg); + } +} + +QMap StandardServiceRoot::storeCustomFeedsData() { + return QMap(); +} + +void StandardServiceRoot::restoreCustomFeedsData(const QMap& data, const QHash& feeds) { + Q_UNUSED(feeds) + Q_UNUSED(data) +} + +QString StandardServiceRoot::processFeedUrl(const QString& feed_url) { + if (feed_url.startsWith(QL1S(URI_SCHEME_FEED_SHORT))) { + QString without_feed_prefix = feed_url.mid(5); + + if (without_feed_prefix.startsWith(QL1S("https:")) || without_feed_prefix.startsWith(QL1S("http:"))) { + return without_feed_prefix; + } + + else { + return feed_url; + } + } + + else { + return feed_url; + } +} + +void StandardServiceRoot::checkArgumentForFeedAdding(const QString& argument) { + if (argument.startsWith(QL1S(URI_SCHEME_FEED_SHORT))) { + addNewFeed(processFeedUrl(argument)); + } +} + +QList StandardServiceRoot::getContextMenuForFeed(StandardFeed* feed) { + if (m_feedContextMenu.isEmpty()) { + // Initialize. + m_actionFeedFetchMetadata = new QAction(qApp->icons()->fromTheme(QSL("emblem-downloads")), tr("Fetch metadata"), nullptr); + m_feedContextMenu.append(m_actionFeedFetchMetadata); + } + + // Make connections. + disconnect(m_actionFeedFetchMetadata, &QAction::triggered, 0, 0); + connect(m_actionFeedFetchMetadata, &QAction::triggered, feed, &StandardFeed::fetchMetadataForItself); + return m_feedContextMenu; +} + +bool StandardServiceRoot::mergeImportExportModel(FeedsImportExportModel* model, RootItem* target_root_node, QString& output_message) { + QStack original_parents; + original_parents.push(target_root_node); + QStack new_parents; + new_parents.push(model->rootItem()); + bool some_feed_category_error = false; + + // Iterate all new items we would like to merge into current model. + while (!new_parents.isEmpty()) { + RootItem* target_parent = original_parents.pop(); + RootItem* source_parent = new_parents.pop(); + + foreach (RootItem* source_item, source_parent->childItems()) { + if (!model->isItemChecked(source_item)) { + // We can skip this item, because it is not checked and should not be imported. + // NOTE: All descendants are thus skipped too. + continue; + } + + if (source_item->kind() == RootItemKind::Category) { + StandardCategory* source_category = static_cast(source_item); + StandardCategory* new_category = new StandardCategory(*source_category); + QString new_category_title = new_category->title(); + // Add category to model. + new_category->clearChildren(); + + if (new_category->addItself(target_parent)) { + requestItemReassignment(new_category, target_parent); + // Process all children of this category. + original_parents.push(new_category); + new_parents.push(source_category); + } + + else { + delete new_category; + // Add category failed, but this can mean that the same category (with same title) + // already exists. If such a category exists in current parent, then find it and + // add descendants to it. + RootItem* existing_category = nullptr; + + foreach (RootItem* child, target_parent->childItems()) { + if (child->kind() == RootItemKind::Category && child->title() == new_category_title) { + existing_category = child; + } + } + + if (existing_category != nullptr) { + original_parents.push(existing_category); + new_parents.push(source_category); + } + + else { + some_feed_category_error = true; + } + } + } + + else if (source_item->kind() == RootItemKind::Feed) { + StandardFeed* source_feed = static_cast(source_item); + StandardFeed* new_feed = new StandardFeed(*source_feed); + + // Append this feed and end this iteration. + if (new_feed->addItself(target_parent)) { + requestItemReassignment(new_feed, target_parent); + } + + else { + delete new_feed; + some_feed_category_error = true; + } + } + } + } + + if (some_feed_category_error) { + output_message = tr("Import successful, but some feeds/categories were not imported due to error."); + } + + else { + output_message = tr("Import was completely successful."); + } + + return !some_feed_category_error; +} + +void StandardServiceRoot::addNewCategory() { + if (!qApp->feedUpdateLock()->tryLock()) { + // Lock was not obtained because + // it is used probably by feed updater or application + // is quitting. + qApp->showGuiMessage(tr("Cannot add category"), + tr("Cannot add category because another critical operation is ongoing."), + QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); + // Thus, cannot delete and quit the method. + return; + } + + QScopedPointer form_pointer(new FormStandardCategoryDetails(this, qApp->mainFormWidget())); + form_pointer.data()->addEditCategory(nullptr, nullptr); + qApp->feedUpdateLock()->unlock(); +} + +void StandardServiceRoot::importFeeds() { + QScopedPointer form(new FormStandardImportExport(this, qApp->mainFormWidget())); + form.data()->setMode(FeedsImportExportModel::Import); + form.data()->exec(); +} + +void StandardServiceRoot::exportFeeds() { + QScopedPointer form(new FormStandardImportExport(this, qApp->mainFormWidget())); + form.data()->setMode(FeedsImportExportModel::Export); + form.data()->exec(); +} + +QList StandardServiceRoot::serviceMenu() { + if (m_serviceMenu.isEmpty()) { + m_actionExportFeeds = new QAction(qApp->icons()->fromTheme("document-export"), tr("Export feeds"), this); + m_actionImportFeeds = new QAction(qApp->icons()->fromTheme("document-import"), tr("Import feeds"), this); + connect(m_actionExportFeeds, &QAction::triggered, this, &StandardServiceRoot::exportFeeds); + connect(m_actionImportFeeds, &QAction::triggered, this, &StandardServiceRoot::importFeeds); + m_serviceMenu.append(m_actionExportFeeds); + m_serviceMenu.append(m_actionImportFeeds); + } + + return m_serviceMenu; +} diff --git a/src/services/standard/standardserviceroot.h b/src/services/standard/standardserviceroot.h index e62821a92..94efdd279 100755 --- a/src/services/standard/standardserviceroot.h +++ b/src/services/standard/standardserviceroot.h @@ -1,93 +1,93 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef STANDARDSERVICEROOT_H -#define STANDARDSERVICEROOT_H - -#include "services/abstract/serviceroot.h" - -#include -#include - - -class RecycleBin; -class StandardCategory; -class StandardFeed; -class FeedsImportExportModel; -class QMenu; - -class StandardServiceRoot : public ServiceRoot { - Q_OBJECT - - public: - explicit StandardServiceRoot(RootItem *parent = nullptr); - virtual ~StandardServiceRoot(); - - // Start/stop root. - void start(bool freshly_activated); - void stop(); - - QString code() const; - - bool canBeEdited() const; - bool canBeDeleted() const; - bool deleteViaGui(); - bool supportsFeedAdding() const; - bool supportsCategoryAdding() const; - - QVariant data(int column, int role) const; - Qt::ItemFlags additionalFlags() const; - - // Access to recycle bin. - RecycleBin *recycleBin() const; - - // Returns menu to be shown in "Services -> service" menu. - QList serviceMenu(); - - // Returns context specific menu actions for given feed. - QList getContextMenuForFeed(StandardFeed *feed); - - // Takes structure residing under given root item and adds feeds/categories from - // it to active structure. - // NOTE: This is used for import/export of the model. - bool mergeImportExportModel(FeedsImportExportModel *model, RootItem *target_root_node, QString &output_message); - - void loadFromDatabase(); - void checkArgumentForFeedAdding(const QString &argument); - - public slots: - void addNewFeed(const QString &url = QString()); - void addNewCategory(); - void importFeeds(); - void exportFeeds(); - - private: - QString processFeedUrl(const QString &feed_url); - void checkArgumentsForFeedAdding(); - - RecycleBin *m_recycleBin; - QAction *m_actionExportFeeds; - QAction *m_actionImportFeeds; - QList m_serviceMenu; - QList m_feedContextMenu; - QAction *m_actionFeedFetchMetadata; - - QMap storeCustomFeedsData(); - void restoreCustomFeedsData(const QMap &data, const QHash &feeds); -}; - -#endif // STANDARDSERVICEROOT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef STANDARDSERVICEROOT_H +#define STANDARDSERVICEROOT_H + +#include "services/abstract/serviceroot.h" + +#include +#include + + +class RecycleBin; +class StandardCategory; +class StandardFeed; +class FeedsImportExportModel; +class QMenu; + +class StandardServiceRoot : public ServiceRoot { + Q_OBJECT + + public: + explicit StandardServiceRoot(RootItem* parent = nullptr); + virtual ~StandardServiceRoot(); + + // Start/stop root. + void start(bool freshly_activated); + void stop(); + + QString code() const; + + bool canBeEdited() const; + bool canBeDeleted() const; + bool deleteViaGui(); + bool supportsFeedAdding() const; + bool supportsCategoryAdding() const; + + QVariant data(int column, int role) const; + Qt::ItemFlags additionalFlags() const; + + // Access to recycle bin. + RecycleBin* recycleBin() const; + + // Returns menu to be shown in "Services -> service" menu. + QList serviceMenu(); + + // Returns context specific menu actions for given feed. + QList getContextMenuForFeed(StandardFeed* feed); + + // Takes structure residing under given root item and adds feeds/categories from + // it to active structure. + // NOTE: This is used for import/export of the model. + bool mergeImportExportModel(FeedsImportExportModel* model, RootItem* target_root_node, QString& output_message); + + void loadFromDatabase(); + void checkArgumentForFeedAdding(const QString& argument); + + public slots: + void addNewFeed(const QString& url = QString()); + void addNewCategory(); + void importFeeds(); + void exportFeeds(); + + private: + QString processFeedUrl(const QString& feed_url); + void checkArgumentsForFeedAdding(); + + RecycleBin* m_recycleBin; + QAction* m_actionExportFeeds; + QAction* m_actionImportFeeds; + QList m_serviceMenu; + QList m_feedContextMenu; + QAction* m_actionFeedFetchMetadata; + + QMap storeCustomFeedsData(); + void restoreCustomFeedsData(const QMap& data, const QHash& feeds); +}; + +#endif // STANDARDSERVICEROOT_H diff --git a/src/services/tt-rss/definitions.h b/src/services/tt-rss/definitions.h index d23411d99..80db6e355 100755 --- a/src/services/tt-rss/definitions.h +++ b/src/services/tt-rss/definitions.h @@ -1,64 +1,64 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef TTRSS_DEFINITIONS_H -#define TTRSS_DEFINITIONS_H - -#define MINIMAL_API_LEVEL 9 -#define CONTENT_TYPE "application/json; charset=utf-8" - -/// -/// Errors. -/// -#define NOT_LOGGED_IN "NOT_LOGGED_IN" // Error when user needs to login before making an operation. -#define UNKNOWN_METHOD "UNKNOWN_METHOD" // Given "op" is not recognized. -#define INCORRECT_USAGE "INCORRECT_USAGE" // Given "op" was used with bad parameters. - -// Limitations -#define MAX_MESSAGES 200 - -// General return status codes. -#define API_STATUS_OK 0 -#define API_STATUS_ERR 1 -#define STATUS_OK "OK" - -#define CONTENT_NOT_LOADED -1 - -// Login. -#define API_DISABLED "API_DISABLED" // API is not enabled. -#define LOGIN_ERROR "LOGIN_ERROR" // Incorrect password/username. - -// Logout. -#define LOGOUT_OK "OK" - -// Get feed tree. -#define GFT_TYPE_CATEGORY "category" - -// Subscribe to feed. -#define STF_UNKNOWN -1 -#define STF_EXISTS 0 -#define STF_INVALID_URL 2 -#define STF_UNREACHABLE_URL 5 -#define STF_URL_NO_FEED 3 -#define STF_URL_MANY_FEEDS 4 -#define STF_INSERTED 1 - -// Unsubscribe from feed. -#define UFF_FEED_NOT_FOUND "FEED_NOT_FOUND" -#define UFF_OK "OK" - -#endif // TTRSS_DEFINITIONS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef TTRSS_DEFINITIONS_H +#define TTRSS_DEFINITIONS_H + +#define MINIMAL_API_LEVEL 9 +#define CONTENT_TYPE "application/json; charset=utf-8" + +/// +/// Errors. +/// +#define NOT_LOGGED_IN "NOT_LOGGED_IN" // Error when user needs to login before making an operation. +#define UNKNOWN_METHOD "UNKNOWN_METHOD" // Given "op" is not recognized. +#define INCORRECT_USAGE "INCORRECT_USAGE" // Given "op" was used with bad parameters. + +// Limitations +#define MAX_MESSAGES 200 + +// General return status codes. +#define API_STATUS_OK 0 +#define API_STATUS_ERR 1 +#define STATUS_OK "OK" + +#define CONTENT_NOT_LOADED -1 + +// Login. +#define API_DISABLED "API_DISABLED" // API is not enabled. +#define LOGIN_ERROR "LOGIN_ERROR" // Incorrect password/username. + +// Logout. +#define LOGOUT_OK "OK" + +// Get feed tree. +#define GFT_TYPE_CATEGORY "category" + +// Subscribe to feed. +#define STF_UNKNOWN -1 +#define STF_EXISTS 0 +#define STF_INVALID_URL 2 +#define STF_UNREACHABLE_URL 5 +#define STF_URL_NO_FEED 3 +#define STF_URL_MANY_FEEDS 4 +#define STF_INSERTED 1 + +// Unsubscribe from feed. +#define UFF_FEED_NOT_FOUND "FEED_NOT_FOUND" +#define UFF_OK "OK" + +#endif // TTRSS_DEFINITIONS_H diff --git a/src/services/tt-rss/gui/formeditttrssaccount.cpp b/src/services/tt-rss/gui/formeditttrssaccount.cpp index 406cd572c..7b65ce388 100755 --- a/src/services/tt-rss/gui/formeditttrssaccount.cpp +++ b/src/services/tt-rss/gui/formeditttrssaccount.cpp @@ -1,270 +1,268 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - - -#include "services/tt-rss/gui/formeditttrssaccount.h" - -#include "services/tt-rss/definitions.h" -#include "services/tt-rss/ttrssserviceroot.h" -#include "services/tt-rss/network/ttrssnetworkfactory.h" -#include "miscellaneous/iconfactory.h" -#include "network-web/networkfactory.h" - - -FormEditTtRssAccount::FormEditTtRssAccount(QWidget *parent) - : QDialog(parent), m_ui(new Ui::FormEditTtRssAccount), m_editableRoot(nullptr) { - m_ui->setupUi(this); - m_btnOk = m_ui->m_buttonBox->button(QDialogButtonBox::Ok); - - setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); - setWindowIcon(qApp->icons()->fromTheme(QSL("tinytinyrss"))); - - m_ui->m_lblTestResult->label()->setWordWrap(true); - m_ui->m_lblServerSideUpdateInformation->setText(tr("Leaving this option on causes that updates " - "of feeds will be probably much slower and may time-out often.")); - m_ui->m_lblDescription->setText(tr("Note that at least API level %1 is required.").arg(MINIMAL_API_LEVEL)); - m_ui->m_txtHttpUsername->lineEdit()->setPlaceholderText(tr("HTTP authentication username")); - m_ui->m_txtHttpPassword->lineEdit()->setPlaceholderText(tr("HTTP authentication password")); - m_ui->m_txtPassword->lineEdit()->setPlaceholderText(tr("Password for your TT-RSS account")); - m_ui->m_txtUsername->lineEdit()->setPlaceholderText(tr("Username for your TT-RSS account")); - m_ui->m_txtUrl->lineEdit()->setPlaceholderText(tr("URL of your TT-RSS instance WITHOUT trailing \"/api/\" string")); - m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Information, - tr("No test done yet."), - tr("Here, results of connection test are shown.")); - - setTabOrder(m_ui->m_txtUrl->lineEdit(), m_ui->m_checkServerSideUpdate); - setTabOrder(m_ui->m_checkServerSideUpdate, m_ui->m_txtUsername->lineEdit()); - setTabOrder(m_ui->m_txtUsername->lineEdit(), m_ui->m_txtPassword->lineEdit()); - setTabOrder(m_ui->m_txtPassword->lineEdit(), m_ui->m_checkShowPassword); - setTabOrder(m_ui->m_checkShowPassword, m_ui->m_gbHttpAuthentication); - setTabOrder(m_ui->m_gbHttpAuthentication, m_ui->m_txtHttpUsername->lineEdit()); - setTabOrder(m_ui->m_txtHttpUsername->lineEdit(), m_ui->m_txtHttpPassword->lineEdit()); - setTabOrder(m_ui->m_txtHttpPassword->lineEdit(), m_ui->m_checkShowHttpPassword); - setTabOrder(m_ui->m_checkShowHttpPassword, m_ui->m_btnTestSetup); - setTabOrder(m_ui->m_btnTestSetup, m_ui->m_buttonBox); - - connect(m_ui->m_checkShowPassword, &QCheckBox::toggled, this, &FormEditTtRssAccount::displayPassword); - connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &FormEditTtRssAccount::onClickedOk); - connect(m_ui->m_buttonBox, &QDialogButtonBox::rejected, this, &FormEditTtRssAccount::onClickedCancel); - connect(m_ui->m_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::onPasswordChanged); - connect(m_ui->m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::onUsernameChanged); - connect(m_ui->m_txtHttpPassword->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::onHttpPasswordChanged); - connect(m_ui->m_txtHttpUsername->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::onHttpUsernameChanged); - connect(m_ui->m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::onUrlChanged); - connect(m_ui->m_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::checkOkButton); - connect(m_ui->m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::checkOkButton); - connect(m_ui->m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::checkOkButton); - connect(m_ui->m_btnTestSetup, &QPushButton::clicked, this, &FormEditTtRssAccount::performTest); - connect(m_ui->m_gbHttpAuthentication, &QGroupBox::toggled, this, &FormEditTtRssAccount::onHttpPasswordChanged); - connect(m_ui->m_gbHttpAuthentication, &QGroupBox::toggled, this, &FormEditTtRssAccount::onHttpUsernameChanged); - connect(m_ui->m_checkShowHttpPassword, &QCheckBox::toggled, this, &FormEditTtRssAccount::displayHttpPassword); - - onPasswordChanged(); - onUsernameChanged(); - onUrlChanged(); - onHttpPasswordChanged(); - onHttpUsernameChanged(); - checkOkButton(); - displayPassword(false); - displayHttpPassword(false); -} - -FormEditTtRssAccount::~FormEditTtRssAccount() { -} - -TtRssServiceRoot *FormEditTtRssAccount::execForCreate() { - setWindowTitle(tr("Add new Tiny Tiny RSS account")); - exec(); - return m_editableRoot; -} - -void FormEditTtRssAccount::execForEdit(TtRssServiceRoot *existing_root) { - setWindowTitle(tr("Edit existing Tiny Tiny RSS account")); - m_editableRoot = existing_root; - - m_ui->m_gbHttpAuthentication->setChecked(existing_root->network()->authIsUsed()); - m_ui->m_txtHttpPassword->lineEdit()->setText(existing_root->network()->authPassword()); - m_ui->m_txtHttpUsername->lineEdit()->setText(existing_root->network()->authUsername()); - m_ui->m_txtUsername->lineEdit()->setText(existing_root->network()->username()); - m_ui->m_txtPassword->lineEdit()->setText(existing_root->network()->password()); - m_ui->m_txtUrl->lineEdit()->setText(existing_root->network()->url()); - m_ui->m_checkServerSideUpdate->setChecked(existing_root->network()->forceServerSideUpdate()); - - exec(); -} - -void FormEditTtRssAccount::displayPassword(bool display) { - m_ui->m_txtPassword->lineEdit()->setEchoMode(display ? QLineEdit::Normal : QLineEdit::Password); -} - -void FormEditTtRssAccount::displayHttpPassword(bool display) { - m_ui->m_txtHttpPassword->lineEdit()->setEchoMode(display ? QLineEdit::Normal : QLineEdit::Password); -} - -void FormEditTtRssAccount::performTest() { - TtRssNetworkFactory factory; - - factory.setUsername(m_ui->m_txtUsername->lineEdit()->text()); - factory.setPassword(m_ui->m_txtPassword->lineEdit()->text()); - factory.setUrl(m_ui->m_txtUrl->lineEdit()->text()); - factory.setAuthIsUsed(m_ui->m_gbHttpAuthentication->isChecked()); - factory.setAuthUsername(m_ui->m_txtHttpUsername->lineEdit()->text()); - factory.setAuthPassword(m_ui->m_txtHttpPassword->lineEdit()->text()); - factory.setForceServerSideUpdate(m_ui->m_checkServerSideUpdate->isChecked()); - - TtRssLoginResponse result = factory.login(); - - if (result.isLoaded()) { - if (result.hasError()) { - QString error = result.error(); - - if (error == API_DISABLED) { - m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, - tr("API access on selected server is not enabled."), - tr("API access on selected server is not enabled.")); - } - else if (error == LOGIN_ERROR) { - m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, - tr("Entered credentials are incorrect."), - tr("Entered credentials are incorrect.")); - } - else { - m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, - tr("Other error occurred, contact developers."), - tr("Other error occurred, contact developers.")); - } - } - else if (result.apiLevel() < MINIMAL_API_LEVEL) { - m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, - tr("Selected Tiny Tiny RSS server is running unsupported version of API (%1). At least API level %2 is required.").arg(QString::number(result.apiLevel()), - QString::number(MINIMAL_API_LEVEL)), - tr("Selected Tiny Tiny RSS server is running unsupported version of API.")); - } - else { - m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Ok, - tr("Tiny Tiny RSS server is okay, running with API level %1, while at least API level %2 is required.").arg(QString::number(result.apiLevel()), - QString::number(MINIMAL_API_LEVEL)), - tr("Tiny Tiny RSS server is okay.")); - } - } - else if (factory.lastError() != QNetworkReply::NoError ) { - m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, - tr("Network error: '%1'.").arg(NetworkFactory::networkErrorText(factory.lastError())), - tr("Network error, have you entered correct Tiny Tiny RSS API endpoint and password?")); - } - else { - m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, - tr("Unspecified error, did you enter correct URL?"), - tr("Unspecified error, did you enter correct URL?")); - } -} - -void FormEditTtRssAccount::onClickedOk() { - bool editing_account = true; - - if (m_editableRoot == nullptr) { - // We want to confirm newly created account. - // So save new account into DB, setup its properties. - m_editableRoot = new TtRssServiceRoot(); - editing_account = false; - } - - m_editableRoot->network()->setUrl(m_ui->m_txtUrl->lineEdit()->text()); - m_editableRoot->network()->setUsername(m_ui->m_txtUsername->lineEdit()->text()); - m_editableRoot->network()->setPassword(m_ui->m_txtPassword->lineEdit()->text()); - m_editableRoot->network()->setAuthIsUsed(m_ui->m_gbHttpAuthentication->isChecked()); - m_editableRoot->network()->setAuthUsername(m_ui->m_txtHttpUsername->lineEdit()->text()); - m_editableRoot->network()->setAuthPassword(m_ui->m_txtHttpPassword->lineEdit()->text()); - m_editableRoot->network()->setForceServerSideUpdate(m_ui->m_checkServerSideUpdate->isChecked()); - m_editableRoot->saveAccountDataToDatabase(); - - accept(); - - if (editing_account) { - m_editableRoot->network()->logout(); - m_editableRoot->completelyRemoveAllData(); - m_editableRoot->syncIn(); - } -} - -void FormEditTtRssAccount::onClickedCancel() { - reject(); -} - -void FormEditTtRssAccount::onUsernameChanged() { - const QString username = m_ui->m_txtUsername->lineEdit()->text(); - - if (username.isEmpty()) { - m_ui->m_txtUsername->setStatus(WidgetWithStatus::Error, tr("Username cannot be empty.")); - } - else { - m_ui->m_txtUsername->setStatus(WidgetWithStatus::Ok, tr("Username is okay.")); - } -} - -void FormEditTtRssAccount::onPasswordChanged() { - const QString password = m_ui->m_txtPassword->lineEdit()->text(); - - if (password.isEmpty()) { - m_ui->m_txtPassword->setStatus(WidgetWithStatus::Error, tr("Password cannot be empty.")); - } - else { - m_ui->m_txtPassword->setStatus(WidgetWithStatus::Ok, tr("Password is okay.")); - } -} - -void FormEditTtRssAccount::onHttpUsernameChanged() { - const bool is_username_ok = !m_ui->m_gbHttpAuthentication->isChecked() || !m_ui->m_txtHttpUsername->lineEdit()->text().isEmpty(); - - m_ui->m_txtHttpUsername->setStatus(is_username_ok ? - LineEditWithStatus::Ok : - LineEditWithStatus::Warning, - is_username_ok ? - tr("Username is ok or it is not needed.") : - tr("Username is empty.")); -} - -void FormEditTtRssAccount::onHttpPasswordChanged() { - const bool is_username_ok = !m_ui->m_gbHttpAuthentication->isChecked() || !m_ui->m_txtHttpPassword->lineEdit()->text().isEmpty(); - - m_ui->m_txtHttpPassword->setStatus(is_username_ok ? - LineEditWithStatus::Ok : - LineEditWithStatus::Warning, - is_username_ok ? - tr("Password is ok or it is not needed.") : - tr("Password is empty.")); -} - -void FormEditTtRssAccount::onUrlChanged() { - const QString url = m_ui->m_txtUrl->lineEdit()->text(); - - if (url.isEmpty()) { - m_ui->m_txtUrl->setStatus(WidgetWithStatus::Error, tr("URL cannot be empty.")); - } - else if (url.endsWith(QL1S("/api/")) || url.endsWith(QL1S("/api"))) { - m_ui->m_txtUrl->setStatus(WidgetWithStatus::Warning, tr("URL should NOT end with \"/api/\".")); - } - else { - m_ui->m_txtUrl->setStatus(WidgetWithStatus::Ok, tr("URL is okay.")); - } -} - -void FormEditTtRssAccount::checkOkButton() { - m_btnOk->setEnabled(!m_ui->m_txtUsername->lineEdit()->text().isEmpty() && - !m_ui->m_txtPassword->lineEdit()->text().isEmpty() && - !m_ui->m_txtUrl->lineEdit()->text().isEmpty()); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + + +#include "services/tt-rss/gui/formeditttrssaccount.h" + +#include "services/tt-rss/definitions.h" +#include "services/tt-rss/ttrssserviceroot.h" +#include "services/tt-rss/network/ttrssnetworkfactory.h" +#include "miscellaneous/iconfactory.h" +#include "network-web/networkfactory.h" + + +FormEditTtRssAccount::FormEditTtRssAccount(QWidget* parent) + : QDialog(parent), m_ui(new Ui::FormEditTtRssAccount), m_editableRoot(nullptr) { + m_ui->setupUi(this); + m_btnOk = m_ui->m_buttonBox->button(QDialogButtonBox::Ok); + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); + setWindowIcon(qApp->icons()->fromTheme(QSL("tinytinyrss"))); + m_ui->m_lblTestResult->label()->setWordWrap(true); + m_ui->m_lblServerSideUpdateInformation->setText(tr("Leaving this option on causes that updates " + "of feeds will be probably much slower and may time-out often.")); + m_ui->m_lblDescription->setText(tr("Note that at least API level %1 is required.").arg(MINIMAL_API_LEVEL)); + m_ui->m_txtHttpUsername->lineEdit()->setPlaceholderText(tr("HTTP authentication username")); + m_ui->m_txtHttpPassword->lineEdit()->setPlaceholderText(tr("HTTP authentication password")); + m_ui->m_txtPassword->lineEdit()->setPlaceholderText(tr("Password for your TT-RSS account")); + m_ui->m_txtUsername->lineEdit()->setPlaceholderText(tr("Username for your TT-RSS account")); + m_ui->m_txtUrl->lineEdit()->setPlaceholderText(tr("URL of your TT-RSS instance WITHOUT trailing \"/api/\" string")); + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Information, + tr("No test done yet."), + tr("Here, results of connection test are shown.")); + setTabOrder(m_ui->m_txtUrl->lineEdit(), m_ui->m_checkServerSideUpdate); + setTabOrder(m_ui->m_checkServerSideUpdate, m_ui->m_txtUsername->lineEdit()); + setTabOrder(m_ui->m_txtUsername->lineEdit(), m_ui->m_txtPassword->lineEdit()); + setTabOrder(m_ui->m_txtPassword->lineEdit(), m_ui->m_checkShowPassword); + setTabOrder(m_ui->m_checkShowPassword, m_ui->m_gbHttpAuthentication); + setTabOrder(m_ui->m_gbHttpAuthentication, m_ui->m_txtHttpUsername->lineEdit()); + setTabOrder(m_ui->m_txtHttpUsername->lineEdit(), m_ui->m_txtHttpPassword->lineEdit()); + setTabOrder(m_ui->m_txtHttpPassword->lineEdit(), m_ui->m_checkShowHttpPassword); + setTabOrder(m_ui->m_checkShowHttpPassword, m_ui->m_btnTestSetup); + setTabOrder(m_ui->m_btnTestSetup, m_ui->m_buttonBox); + connect(m_ui->m_checkShowPassword, &QCheckBox::toggled, this, &FormEditTtRssAccount::displayPassword); + connect(m_ui->m_buttonBox, &QDialogButtonBox::accepted, this, &FormEditTtRssAccount::onClickedOk); + connect(m_ui->m_buttonBox, &QDialogButtonBox::rejected, this, &FormEditTtRssAccount::onClickedCancel); + connect(m_ui->m_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::onPasswordChanged); + connect(m_ui->m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::onUsernameChanged); + connect(m_ui->m_txtHttpPassword->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::onHttpPasswordChanged); + connect(m_ui->m_txtHttpUsername->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::onHttpUsernameChanged); + connect(m_ui->m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::onUrlChanged); + connect(m_ui->m_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::checkOkButton); + connect(m_ui->m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::checkOkButton); + connect(m_ui->m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &FormEditTtRssAccount::checkOkButton); + connect(m_ui->m_btnTestSetup, &QPushButton::clicked, this, &FormEditTtRssAccount::performTest); + connect(m_ui->m_gbHttpAuthentication, &QGroupBox::toggled, this, &FormEditTtRssAccount::onHttpPasswordChanged); + connect(m_ui->m_gbHttpAuthentication, &QGroupBox::toggled, this, &FormEditTtRssAccount::onHttpUsernameChanged); + connect(m_ui->m_checkShowHttpPassword, &QCheckBox::toggled, this, &FormEditTtRssAccount::displayHttpPassword); + onPasswordChanged(); + onUsernameChanged(); + onUrlChanged(); + onHttpPasswordChanged(); + onHttpUsernameChanged(); + checkOkButton(); + displayPassword(false); + displayHttpPassword(false); +} + +FormEditTtRssAccount::~FormEditTtRssAccount() { +} + +TtRssServiceRoot* FormEditTtRssAccount::execForCreate() { + setWindowTitle(tr("Add new Tiny Tiny RSS account")); + exec(); + return m_editableRoot; +} + +void FormEditTtRssAccount::execForEdit(TtRssServiceRoot* existing_root) { + setWindowTitle(tr("Edit existing Tiny Tiny RSS account")); + m_editableRoot = existing_root; + m_ui->m_gbHttpAuthentication->setChecked(existing_root->network()->authIsUsed()); + m_ui->m_txtHttpPassword->lineEdit()->setText(existing_root->network()->authPassword()); + m_ui->m_txtHttpUsername->lineEdit()->setText(existing_root->network()->authUsername()); + m_ui->m_txtUsername->lineEdit()->setText(existing_root->network()->username()); + m_ui->m_txtPassword->lineEdit()->setText(existing_root->network()->password()); + m_ui->m_txtUrl->lineEdit()->setText(existing_root->network()->url()); + m_ui->m_checkServerSideUpdate->setChecked(existing_root->network()->forceServerSideUpdate()); + exec(); +} + +void FormEditTtRssAccount::displayPassword(bool display) { + m_ui->m_txtPassword->lineEdit()->setEchoMode(display ? QLineEdit::Normal : QLineEdit::Password); +} + +void FormEditTtRssAccount::displayHttpPassword(bool display) { + m_ui->m_txtHttpPassword->lineEdit()->setEchoMode(display ? QLineEdit::Normal : QLineEdit::Password); +} + +void FormEditTtRssAccount::performTest() { + TtRssNetworkFactory factory; + factory.setUsername(m_ui->m_txtUsername->lineEdit()->text()); + factory.setPassword(m_ui->m_txtPassword->lineEdit()->text()); + factory.setUrl(m_ui->m_txtUrl->lineEdit()->text()); + factory.setAuthIsUsed(m_ui->m_gbHttpAuthentication->isChecked()); + factory.setAuthUsername(m_ui->m_txtHttpUsername->lineEdit()->text()); + factory.setAuthPassword(m_ui->m_txtHttpPassword->lineEdit()->text()); + factory.setForceServerSideUpdate(m_ui->m_checkServerSideUpdate->isChecked()); + TtRssLoginResponse result = factory.login(); + + if (result.isLoaded()) { + if (result.hasError()) { + QString error = result.error(); + + if (error == API_DISABLED) { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("API access on selected server is not enabled."), + tr("API access on selected server is not enabled.")); + } + + else if (error == LOGIN_ERROR) { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("Entered credentials are incorrect."), + tr("Entered credentials are incorrect.")); + } + + else { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("Other error occurred, contact developers."), + tr("Other error occurred, contact developers.")); + } + } + + else if (result.apiLevel() < MINIMAL_API_LEVEL) { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("Selected Tiny Tiny RSS server is running unsupported version of API (%1). At least API level %2 is required.").arg(QString::number(result.apiLevel()), + QString::number(MINIMAL_API_LEVEL)), + tr("Selected Tiny Tiny RSS server is running unsupported version of API.")); + } + + else { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Ok, + tr("Tiny Tiny RSS server is okay, running with API level %1, while at least API level %2 is required.").arg(QString::number(result.apiLevel()), + QString::number(MINIMAL_API_LEVEL)), + tr("Tiny Tiny RSS server is okay.")); + } + } + + else if (factory.lastError() != QNetworkReply::NoError) { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("Network error: '%1'.").arg(NetworkFactory::networkErrorText(factory.lastError())), + tr("Network error, have you entered correct Tiny Tiny RSS API endpoint and password?")); + } + + else { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("Unspecified error, did you enter correct URL?"), + tr("Unspecified error, did you enter correct URL?")); + } +} + +void FormEditTtRssAccount::onClickedOk() { + bool editing_account = true; + + if (m_editableRoot == nullptr) { + // We want to confirm newly created account. + // So save new account into DB, setup its properties. + m_editableRoot = new TtRssServiceRoot(); + editing_account = false; + } + + m_editableRoot->network()->setUrl(m_ui->m_txtUrl->lineEdit()->text()); + m_editableRoot->network()->setUsername(m_ui->m_txtUsername->lineEdit()->text()); + m_editableRoot->network()->setPassword(m_ui->m_txtPassword->lineEdit()->text()); + m_editableRoot->network()->setAuthIsUsed(m_ui->m_gbHttpAuthentication->isChecked()); + m_editableRoot->network()->setAuthUsername(m_ui->m_txtHttpUsername->lineEdit()->text()); + m_editableRoot->network()->setAuthPassword(m_ui->m_txtHttpPassword->lineEdit()->text()); + m_editableRoot->network()->setForceServerSideUpdate(m_ui->m_checkServerSideUpdate->isChecked()); + m_editableRoot->saveAccountDataToDatabase(); + accept(); + + if (editing_account) { + m_editableRoot->network()->logout(); + m_editableRoot->completelyRemoveAllData(); + m_editableRoot->syncIn(); + } +} + +void FormEditTtRssAccount::onClickedCancel() { + reject(); +} + +void FormEditTtRssAccount::onUsernameChanged() { + const QString username = m_ui->m_txtUsername->lineEdit()->text(); + + if (username.isEmpty()) { + m_ui->m_txtUsername->setStatus(WidgetWithStatus::Error, tr("Username cannot be empty.")); + } + + else { + m_ui->m_txtUsername->setStatus(WidgetWithStatus::Ok, tr("Username is okay.")); + } +} + +void FormEditTtRssAccount::onPasswordChanged() { + const QString password = m_ui->m_txtPassword->lineEdit()->text(); + + if (password.isEmpty()) { + m_ui->m_txtPassword->setStatus(WidgetWithStatus::Error, tr("Password cannot be empty.")); + } + + else { + m_ui->m_txtPassword->setStatus(WidgetWithStatus::Ok, tr("Password is okay.")); + } +} + +void FormEditTtRssAccount::onHttpUsernameChanged() { + const bool is_username_ok = !m_ui->m_gbHttpAuthentication->isChecked() || !m_ui->m_txtHttpUsername->lineEdit()->text().isEmpty(); + m_ui->m_txtHttpUsername->setStatus(is_username_ok ? + LineEditWithStatus::Ok : + LineEditWithStatus::Warning, + is_username_ok ? + tr("Username is ok or it is not needed.") : + tr("Username is empty.")); +} + +void FormEditTtRssAccount::onHttpPasswordChanged() { + const bool is_username_ok = !m_ui->m_gbHttpAuthentication->isChecked() || !m_ui->m_txtHttpPassword->lineEdit()->text().isEmpty(); + m_ui->m_txtHttpPassword->setStatus(is_username_ok ? + LineEditWithStatus::Ok : + LineEditWithStatus::Warning, + is_username_ok ? + tr("Password is ok or it is not needed.") : + tr("Password is empty.")); +} + +void FormEditTtRssAccount::onUrlChanged() { + const QString url = m_ui->m_txtUrl->lineEdit()->text(); + + if (url.isEmpty()) { + m_ui->m_txtUrl->setStatus(WidgetWithStatus::Error, tr("URL cannot be empty.")); + } + + else if (url.endsWith(QL1S("/api/")) || url.endsWith(QL1S("/api"))) { + m_ui->m_txtUrl->setStatus(WidgetWithStatus::Warning, tr("URL should NOT end with \"/api/\".")); + } + + else { + m_ui->m_txtUrl->setStatus(WidgetWithStatus::Ok, tr("URL is okay.")); + } +} + +void FormEditTtRssAccount::checkOkButton() { + m_btnOk->setEnabled(!m_ui->m_txtUsername->lineEdit()->text().isEmpty() && + !m_ui->m_txtPassword->lineEdit()->text().isEmpty() && + !m_ui->m_txtUrl->lineEdit()->text().isEmpty()); +} diff --git a/src/services/tt-rss/gui/formeditttrssaccount.h b/src/services/tt-rss/gui/formeditttrssaccount.h index 827cd4a9c..990d1664c 100755 --- a/src/services/tt-rss/gui/formeditttrssaccount.h +++ b/src/services/tt-rss/gui/formeditttrssaccount.h @@ -1,62 +1,62 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FORMEDITACCOUNT_H -#define FORMEDITACCOUNT_H - -#include - -#include "ui_formeditttrssaccount.h" - - -namespace Ui { - class FormEditTtRssAccount; -} - -class TtRssServiceRoot; - -class FormEditTtRssAccount : public QDialog { - Q_OBJECT - - public: - explicit FormEditTtRssAccount(QWidget *parent = 0); - virtual ~FormEditTtRssAccount(); - - TtRssServiceRoot *execForCreate(); - void execForEdit(TtRssServiceRoot *existing_root); - - private slots: - void displayPassword(bool display); - void displayHttpPassword(bool display); - void performTest(); - void onClickedOk(); - void onClickedCancel(); - - void onUsernameChanged(); - void onPasswordChanged(); - void onHttpUsernameChanged(); - void onHttpPasswordChanged(); - void onUrlChanged(); - void checkOkButton(); - - private: - QScopedPointer m_ui; - TtRssServiceRoot *m_editableRoot; - QPushButton *m_btnOk; -}; - -#endif // FORMEDITACCOUNT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FORMEDITACCOUNT_H +#define FORMEDITACCOUNT_H + +#include + +#include "ui_formeditttrssaccount.h" + + +namespace Ui { + class FormEditTtRssAccount; +} + +class TtRssServiceRoot; + +class FormEditTtRssAccount : public QDialog { + Q_OBJECT + + public: + explicit FormEditTtRssAccount(QWidget* parent = 0); + virtual ~FormEditTtRssAccount(); + + TtRssServiceRoot* execForCreate(); + void execForEdit(TtRssServiceRoot* existing_root); + + private slots: + void displayPassword(bool display); + void displayHttpPassword(bool display); + void performTest(); + void onClickedOk(); + void onClickedCancel(); + + void onUsernameChanged(); + void onPasswordChanged(); + void onHttpUsernameChanged(); + void onHttpPasswordChanged(); + void onUrlChanged(); + void checkOkButton(); + + private: + QScopedPointer m_ui; + TtRssServiceRoot* m_editableRoot; + QPushButton* m_btnOk; +}; + +#endif // FORMEDITACCOUNT_H diff --git a/src/services/tt-rss/gui/formttrssfeeddetails.cpp b/src/services/tt-rss/gui/formttrssfeeddetails.cpp index 829d7f200..1fc8a6e7a 100755 --- a/src/services/tt-rss/gui/formttrssfeeddetails.cpp +++ b/src/services/tt-rss/gui/formttrssfeeddetails.cpp @@ -1,96 +1,93 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/tt-rss/gui/formttrssfeeddetails.h" - -#include "services/tt-rss/definitions.h" -#include "services/tt-rss/ttrssserviceroot.h" -#include "services/tt-rss/ttrsscategory.h" -#include "services/tt-rss/ttrssfeed.h" -#include "services/tt-rss/network/ttrssnetworkfactory.h" -#include "miscellaneous/application.h" - -#include - - -FormTtRssFeedDetails::FormTtRssFeedDetails(ServiceRoot *service_root, QWidget *parent) - : FormFeedDetails(service_root, parent) { - m_ui->m_spinAutoUpdateInterval->setEnabled(false); - m_ui->m_cmbAutoUpdateType->setEnabled(false); - m_ui->m_cmbType->setEnabled(false); - m_ui->m_cmbEncoding->setEnabled(false); - m_ui->m_btnFetchMetadata->setEnabled(false); - m_ui->m_btnIcon->setEnabled(false); - m_ui->m_txtTitle->setEnabled(false); - m_ui->m_txtDescription->setEnabled(false); -} - -void FormTtRssFeedDetails::apply() { - if (m_editableFeed != nullptr) { - // User edited auto-update status. Save it. - TtRssFeed *new_feed_data = new TtRssFeed(); - - new_feed_data->setAutoUpdateType(static_cast(m_ui->m_cmbAutoUpdateType->itemData(m_ui->m_cmbAutoUpdateType->currentIndex()).toInt())); - new_feed_data->setAutoUpdateInitialInterval(m_ui->m_spinAutoUpdateInterval->value()); - - qobject_cast(m_editableFeed)->editItself(new_feed_data); - - delete new_feed_data; - } - else { - RootItem *parent = static_cast(m_ui->m_cmbParentCategory->itemData(m_ui->m_cmbParentCategory->currentIndex()).value()); - TtRssServiceRoot *root = parent->kind() == RootItemKind::Category ? - qobject_cast(parent)->serviceRoot() : - qobject_cast(parent); - const int category_id = parent->kind() == RootItemKind::ServiceRoot ? - 0 : - qobject_cast(parent)->customId(); - const TtRssSubscribeToFeedResponse response = root->network()->subscribeToFeed(m_ui->m_txtUrl->lineEdit()->text(), - category_id, - m_ui->m_gbAuthentication->isChecked(), - m_ui->m_txtUsername->lineEdit()->text(), - m_ui->m_txtPassword->lineEdit()->text()); - - if (response.code() == STF_INSERTED) { - // Feed was added online. - accept(); - qApp->showGuiMessage(tr("Feed added"), tr("Feed was added, triggering sync in now."), QSystemTrayIcon::Information); - QTimer::singleShot(100, root, SLOT(syncIn())); - } - else { - reject(); - qApp->showGuiMessage(tr("Cannot add feed"), - tr("Feed was not added due to error."), - QSystemTrayIcon::Critical, qApp->mainFormWidget(), true); - } - } - - accept(); -} - -void FormTtRssFeedDetails::setEditableFeed(Feed *editable_feed) { - m_ui->m_cmbAutoUpdateType->setEnabled(true); - - FormFeedDetails::setEditableFeed(editable_feed); - - // Tiny Tiny RSS does not support editing of these features. - // User can edit only individual auto-update statuses. - m_ui->m_gbAuthentication->setEnabled(false); - m_ui->m_txtUrl->setEnabled(false); - m_ui->m_lblParentCategory->setEnabled(false); - m_ui->m_cmbParentCategory->setEnabled(false); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/tt-rss/gui/formttrssfeeddetails.h" + +#include "services/tt-rss/definitions.h" +#include "services/tt-rss/ttrssserviceroot.h" +#include "services/tt-rss/ttrsscategory.h" +#include "services/tt-rss/ttrssfeed.h" +#include "services/tt-rss/network/ttrssnetworkfactory.h" +#include "miscellaneous/application.h" + +#include + + +FormTtRssFeedDetails::FormTtRssFeedDetails(ServiceRoot* service_root, QWidget* parent) + : FormFeedDetails(service_root, parent) { + m_ui->m_spinAutoUpdateInterval->setEnabled(false); + m_ui->m_cmbAutoUpdateType->setEnabled(false); + m_ui->m_cmbType->setEnabled(false); + m_ui->m_cmbEncoding->setEnabled(false); + m_ui->m_btnFetchMetadata->setEnabled(false); + m_ui->m_btnIcon->setEnabled(false); + m_ui->m_txtTitle->setEnabled(false); + m_ui->m_txtDescription->setEnabled(false); +} + +void FormTtRssFeedDetails::apply() { + if (m_editableFeed != nullptr) { + // User edited auto-update status. Save it. + TtRssFeed* new_feed_data = new TtRssFeed(); + new_feed_data->setAutoUpdateType(static_cast(m_ui->m_cmbAutoUpdateType->itemData(m_ui->m_cmbAutoUpdateType->currentIndex()).toInt())); + new_feed_data->setAutoUpdateInitialInterval(m_ui->m_spinAutoUpdateInterval->value()); + qobject_cast(m_editableFeed)->editItself(new_feed_data); + delete new_feed_data; + } + + else { + RootItem* parent = static_cast(m_ui->m_cmbParentCategory->itemData(m_ui->m_cmbParentCategory->currentIndex()).value()); + TtRssServiceRoot* root = parent->kind() == RootItemKind::Category ? + qobject_cast(parent)->serviceRoot() : + qobject_cast(parent); + const int category_id = parent->kind() == RootItemKind::ServiceRoot ? + 0 : + qobject_cast(parent)->customId(); + const TtRssSubscribeToFeedResponse response = root->network()->subscribeToFeed(m_ui->m_txtUrl->lineEdit()->text(), + category_id, + m_ui->m_gbAuthentication->isChecked(), + m_ui->m_txtUsername->lineEdit()->text(), + m_ui->m_txtPassword->lineEdit()->text()); + + if (response.code() == STF_INSERTED) { + // Feed was added online. + accept(); + qApp->showGuiMessage(tr("Feed added"), tr("Feed was added, triggering sync in now."), QSystemTrayIcon::Information); + QTimer::singleShot(100, root, SLOT(syncIn())); + } + + else { + reject(); + qApp->showGuiMessage(tr("Cannot add feed"), + tr("Feed was not added due to error."), + QSystemTrayIcon::Critical, qApp->mainFormWidget(), true); + } + } + + accept(); +} + +void FormTtRssFeedDetails::setEditableFeed(Feed* editable_feed) { + m_ui->m_cmbAutoUpdateType->setEnabled(true); + FormFeedDetails::setEditableFeed(editable_feed); + // Tiny Tiny RSS does not support editing of these features. + // User can edit only individual auto-update statuses. + m_ui->m_gbAuthentication->setEnabled(false); + m_ui->m_txtUrl->setEnabled(false); + m_ui->m_lblParentCategory->setEnabled(false); + m_ui->m_cmbParentCategory->setEnabled(false); +} diff --git a/src/services/tt-rss/gui/formttrssfeeddetails.h b/src/services/tt-rss/gui/formttrssfeeddetails.h old mode 100644 new mode 100755 index a7b12db91..7aa262ad8 --- a/src/services/tt-rss/gui/formttrssfeeddetails.h +++ b/src/services/tt-rss/gui/formttrssfeeddetails.h @@ -1,38 +1,38 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef FORMTTRSSFEEDDETAILS_H -#define FORMTTRSSFEEDDETAILS_H - -#include "services/abstract/gui/formfeeddetails.h" - - -class FormTtRssFeedDetails : public FormFeedDetails { - Q_OBJECT - - public: - explicit FormTtRssFeedDetails(ServiceRoot *service_root, QWidget *parent = 0); - - // FormFeedDetails interface - protected slots: - void apply(); - - protected: - void setEditableFeed(Feed *editable_feed); -}; - -#endif // FORMTTRSSFEEDDETAILS_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef FORMTTRSSFEEDDETAILS_H +#define FORMTTRSSFEEDDETAILS_H + +#include "services/abstract/gui/formfeeddetails.h" + + +class FormTtRssFeedDetails : public FormFeedDetails { + Q_OBJECT + + public: + explicit FormTtRssFeedDetails(ServiceRoot* service_root, QWidget* parent = 0); + + // FormFeedDetails interface + protected slots: + void apply(); + + protected: + void setEditableFeed(Feed* editable_feed); +}; + +#endif // FORMTTRSSFEEDDETAILS_H diff --git a/src/services/tt-rss/network/ttrssnetworkfactory.cpp b/src/services/tt-rss/network/ttrssnetworkfactory.cpp index 9f0bcbe0e..0d01d0399 100755 --- a/src/services/tt-rss/network/ttrssnetworkfactory.cpp +++ b/src/services/tt-rss/network/ttrssnetworkfactory.cpp @@ -1,649 +1,642 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/tt-rss/network/ttrssnetworkfactory.h" - -#include "definitions/definitions.h" -#include "services/abstract/rootitem.h" -#include "services/tt-rss/definitions.h" -#include "services/tt-rss/ttrssfeed.h" -#include "services/tt-rss/ttrsscategory.h" -#include "miscellaneous/application.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/textfactory.h" -#include "network-web/networkfactory.h" - -#include -#include -#include -#include - - -TtRssNetworkFactory::TtRssNetworkFactory() - : m_bareUrl(QString()), m_fullUrl(QString()), m_username(QString()), m_password(QString()), m_forceServerSideUpdate(false), m_authIsUsed(false), - m_authUsername(QString()), m_authPassword(QString()), m_sessionId(QString()), - m_lastLoginTime(QDateTime()), m_lastError(QNetworkReply::NoError) { -} - -TtRssNetworkFactory::~TtRssNetworkFactory() { -} - -QString TtRssNetworkFactory::url() const { - return m_bareUrl; -} - -void TtRssNetworkFactory::setUrl(const QString &url) { - m_bareUrl = url; - - if (!m_bareUrl.endsWith(QSL("/"))) { - m_bareUrl = m_bareUrl + QSL("/"); - } - - if (!m_bareUrl.endsWith(QSL("api/"))) { - m_fullUrl = m_bareUrl + QSL("api/"); - } - else { - m_fullUrl = m_bareUrl; - } -} - -QString TtRssNetworkFactory::username() const { - return m_username; -} - -void TtRssNetworkFactory::setUsername(const QString &username) { - m_username = username; -} - -QString TtRssNetworkFactory::password() const { - return m_password; -} - -void TtRssNetworkFactory::setPassword(const QString &password) { - m_password = password; -} - -QDateTime TtRssNetworkFactory::lastLoginTime() const { - return m_lastLoginTime; -} - -QNetworkReply::NetworkError TtRssNetworkFactory::lastError() const { - return m_lastError; -} - -TtRssLoginResponse TtRssNetworkFactory::login() { - if (!m_sessionId.isEmpty()) { - qDebug("TT-RSS: Session ID is not empty before login, logging out first."); - - logout(); - } - - QJsonObject json; - json["op"] = QSL("login"); - json["user"] = m_username; - json["password"] = m_password; - - QByteArray result_raw; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - QJsonDocument(json).toJson(QJsonDocument::Compact), CONTENT_TYPE, result_raw, - QNetworkAccessManager::PostOperation, - m_authIsUsed, m_authUsername, m_authPassword); - TtRssLoginResponse login_response(QString::fromUtf8(result_raw)); - - if (network_reply.first == QNetworkReply::NoError) { - m_sessionId = login_response.sessionId(); - m_lastLoginTime = QDateTime::currentDateTime(); - } - else { - qWarning("TT-RSS: Login failed with error %d.", network_reply.first); - } - - m_lastError = network_reply.first; - return login_response; -} - -TtRssResponse TtRssNetworkFactory::logout() { - if (!m_sessionId.isEmpty()) { - - QJsonObject json; - json["op"] = QSL("logout"); - json["sid"] = m_sessionId; - - QByteArray result_raw; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, qApp->settings()->value(GROUP(Feeds), - SETTING(Feeds::UpdateTimeout)).toInt(), - QJsonDocument(json).toJson(QJsonDocument::Compact), CONTENT_TYPE, result_raw, - QNetworkAccessManager::PostOperation, - m_authIsUsed, m_authUsername, m_authPassword); - - m_lastError = network_reply.first; - - if (m_lastError == QNetworkReply::NoError) { - m_sessionId.clear(); - } - else { - qWarning("TT-RSS: Logout failed with error %d.", network_reply.first); - } - - return TtRssResponse(QString::fromUtf8(result_raw)); - } - else { - qWarning("TT-RSS: Cannot logout because session ID is empty."); - - m_lastError = QNetworkReply::NoError; - return TtRssResponse(); - } -} - -TtRssGetFeedsCategoriesResponse TtRssNetworkFactory::getFeedsCategories() { - QJsonObject json; - json["op"] = QSL("getFeedTree"); - json["sid"] = m_sessionId; - json["include_empty"] = true; - - const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); - QByteArray result_raw; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), - CONTENT_TYPE, result_raw, - QNetworkAccessManager::PostOperation, - m_authIsUsed, m_authUsername, m_authPassword); - TtRssGetFeedsCategoriesResponse result(QString::fromUtf8(result_raw)); - - if (result.isNotLoggedIn()) { - // We are not logged in. - login(); - json["sid"] = m_sessionId; - - network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), CONTENT_TYPE, result_raw, - QNetworkAccessManager::PostOperation, - m_authIsUsed, m_authUsername, m_authPassword); - result = TtRssGetFeedsCategoriesResponse(QString::fromUtf8(result_raw)); - } - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("TT-RSS: getFeedTree failed with error %d.", network_reply.first); - } - - m_lastError = network_reply.first; - return result; -} - -TtRssGetHeadlinesResponse TtRssNetworkFactory::getHeadlines(int feed_id, int limit, int skip, - bool show_content, bool include_attachments, - bool sanitize) { - QJsonObject json; - json["op"] = QSL("getHeadlines"); - json["sid"] = m_sessionId; - json["feed_id"] = feed_id; - json["force_update"] = m_forceServerSideUpdate; - json["limit"] = limit; - json["skip"] = skip; - json["show_content"] = show_content; - json["include_attachments"] = include_attachments; - json["sanitize"] = sanitize; - - const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); - QByteArray result_raw; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), - CONTENT_TYPE, result_raw, - QNetworkAccessManager::PostOperation, - m_authIsUsed, m_authUsername, m_authPassword); - TtRssGetHeadlinesResponse result(QString::fromUtf8(result_raw)); - - if (result.isNotLoggedIn()) { - // We are not logged in. - login(); - json["sid"] = m_sessionId; - - network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), CONTENT_TYPE, result_raw, - QNetworkAccessManager::PostOperation, - m_authIsUsed, m_authUsername, m_authPassword); - result = TtRssGetHeadlinesResponse(QString::fromUtf8(result_raw)); - } - - IOFactory::writeTextFile("aaa", result_raw); - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("TT-RSS: getHeadlines failed with error %d.", network_reply.first); - } - - m_lastError = network_reply.first; - return result; -} - -TtRssUpdateArticleResponse TtRssNetworkFactory::updateArticles(const QStringList &ids, - UpdateArticle::OperatingField field, - UpdateArticle::Mode mode) { - QJsonObject json; - json["op"] = QSL("updateArticle"); - json["sid"] = m_sessionId; - json["article_ids"] = ids.join(QSL(",")); - json["mode"] = (int) mode; - json["field"] = (int) field; - - const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); - QByteArray result_raw; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), - CONTENT_TYPE, result_raw, - QNetworkAccessManager::PostOperation, - m_authIsUsed, m_authUsername, m_authPassword); - TtRssUpdateArticleResponse result(QString::fromUtf8(result_raw)); - - if (result.isNotLoggedIn()) { - // We are not logged in. - login(); - json["sid"] = m_sessionId; - - network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), - CONTENT_TYPE, result_raw, - QNetworkAccessManager::PostOperation, - m_authIsUsed, m_authUsername, m_authPassword); - result = TtRssUpdateArticleResponse(QString::fromUtf8(result_raw)); - } - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("TT-RSS: updateArticle failed with error %d.", network_reply.first); - } - - m_lastError = network_reply.first; - return result; -} - -TtRssSubscribeToFeedResponse TtRssNetworkFactory::subscribeToFeed(const QString &url, int category_id, - bool protectd, const QString &username, - const QString &password) { - QJsonObject json; - json["op"] = QSL("subscribeToFeed"); - json["sid"] = m_sessionId; - json["feed_url"] = url; - json["category_id"] = category_id; - - if (protectd) { - json["login"] = username; - json["password"] = password; - } - - const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); - QByteArray result_raw; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), - CONTENT_TYPE, result_raw, - QNetworkAccessManager::PostOperation, - m_authIsUsed, m_authUsername, m_authPassword); - TtRssSubscribeToFeedResponse result(QString::fromUtf8(result_raw)); - - if (result.isNotLoggedIn()) { - // We are not logged in. - login(); - json["sid"] = m_sessionId; - - network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), - CONTENT_TYPE, result_raw, - QNetworkAccessManager::PostOperation, - m_authIsUsed, m_authUsername, m_authPassword); - result = TtRssSubscribeToFeedResponse(QString::fromUtf8(result_raw)); - } - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("TT-RSS: updateArticle failed with error %d.", network_reply.first); - } - - m_lastError = network_reply.first; - return result; -} - -TtRssUnsubscribeFeedResponse TtRssNetworkFactory::unsubscribeFeed(int feed_id) { - QJsonObject json; - json["op"] = QSL("unsubscribeFeed"); - json["sid"] = m_sessionId; - json["feed_id"] = feed_id; - - const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); - QByteArray result_raw; - NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), CONTENT_TYPE, result_raw, - QNetworkAccessManager::PostOperation, - m_authIsUsed, m_authUsername, m_authPassword); - TtRssUnsubscribeFeedResponse result(QString::fromUtf8(result_raw)); - - if (result.isNotLoggedIn()) { - // We are not logged in. - login(); - json["sid"] = m_sessionId; - - network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), CONTENT_TYPE, result_raw, - QNetworkAccessManager::PostOperation, - m_authIsUsed, m_authUsername, m_authPassword); - result = TtRssUnsubscribeFeedResponse(QString::fromUtf8(result_raw)); - } - - if (network_reply.first != QNetworkReply::NoError) { - qWarning("TT-RSS: getFeeds failed with error %d.", network_reply.first); - } - - m_lastError = network_reply.first; - return result; -} - -bool TtRssNetworkFactory::forceServerSideUpdate() const { - return m_forceServerSideUpdate; -} - -void TtRssNetworkFactory::setForceServerSideUpdate(bool force_server_side_update) { - m_forceServerSideUpdate = force_server_side_update; -} - -bool TtRssNetworkFactory::authIsUsed() const { - return m_authIsUsed; -} - -void TtRssNetworkFactory::setAuthIsUsed(bool auth_is_used) { - m_authIsUsed = auth_is_used; -} - -QString TtRssNetworkFactory::authUsername() const { - return m_authUsername; -} - -void TtRssNetworkFactory::setAuthUsername(const QString &auth_username) { - m_authUsername = auth_username; -} - -QString TtRssNetworkFactory::authPassword() const { - return m_authPassword; -} - -void TtRssNetworkFactory::setAuthPassword(const QString &auth_password) { - m_authPassword = auth_password; -} - -TtRssResponse::TtRssResponse(const QString &raw_content) { - m_rawContent = QJsonDocument::fromJson(raw_content.toUtf8()).object(); -} - -TtRssResponse::~TtRssResponse() { -} - -bool TtRssResponse::isLoaded() const { - return !m_rawContent.isEmpty(); -} - -int TtRssResponse::seq() const { - if (!isLoaded()) { - return CONTENT_NOT_LOADED; - } - else { - return m_rawContent["seq"].toInt(); - } -} - -int TtRssResponse::status() const { - if (!isLoaded()) { - return CONTENT_NOT_LOADED; - } - else { - return m_rawContent["status"].toInt(); - } -} - -bool TtRssResponse::isNotLoggedIn() const { - return status() == API_STATUS_ERR && hasError() && error() == NOT_LOGGED_IN; -} - -QString TtRssResponse::toString() const { - return QJsonDocument(m_rawContent).toJson(QJsonDocument::Compact); -} - -TtRssLoginResponse::TtRssLoginResponse(const QString &raw_content) : TtRssResponse(raw_content) { -} - -TtRssLoginResponse::~TtRssLoginResponse() { -} - -int TtRssLoginResponse::apiLevel() const { - if (!isLoaded()) { - return CONTENT_NOT_LOADED; - } - else { - return m_rawContent["content"].toObject()["api_level"].toInt(); - } -} - -QString TtRssLoginResponse::sessionId() const { - if (!isLoaded()) { - return QString(); - } - else { - return m_rawContent["content"].toObject()["session_id"].toString(); - } -} - -QString TtRssResponse::error() const { - if (!isLoaded()) { - return QString(); - } - else { - return m_rawContent["content"].toObject()["error"].toString(); - } -} - -bool TtRssResponse::hasError() const { - if (!isLoaded()) { - return false; - } - else { - return m_rawContent["content"].toObject().contains("error"); - } -} - - -TtRssGetFeedsCategoriesResponse::TtRssGetFeedsCategoriesResponse(const QString &raw_content) : TtRssResponse(raw_content) { - -} - -TtRssGetFeedsCategoriesResponse::~TtRssGetFeedsCategoriesResponse() { -} - -RootItem *TtRssGetFeedsCategoriesResponse::feedsCategories(bool obtain_icons, QString base_address) const { - RootItem *parent = new RootItem(); - - // Chop the "api/" from the end of the address. - base_address.chop(4); - - qDebug("TT-RSS: Chopped base address to '%s' to get feed icons.", qPrintable(base_address)); - - if (status() == API_STATUS_OK) { - // We have data, construct object tree according to data. - QJsonArray items_to_process = m_rawContent["content"].toObject()["categories"].toObject()["items"].toArray(); - QVector > pairs; - - foreach (const QJsonValue &item, items_to_process) { - pairs.append(QPair(parent, item)); - } - - while (!pairs.isEmpty()) { - QPair pair = pairs.takeFirst(); - RootItem *act_parent = pair.first; - QJsonObject item = pair.second.toObject(); - - int item_id = item["bare_id"].toInt(); - bool is_category = item.contains("type") && item["type"].toString() == GFT_TYPE_CATEGORY; - - if (item_id >= 0) { - if (is_category) { - if (item_id == 0) { - // This is "Uncategorized" category, all its feeds belong to top-level root. - if (item.contains("items")) { - foreach (const QJsonValue &child_feed, item["items"].toArray()) { - pairs.append(QPair(parent, child_feed)); - } - } - } - else { - TtRssCategory *category = new TtRssCategory(); - - category->setTitle(item["name"].toString()); - category->setCustomId(item_id); - act_parent->appendChild(category); - - if (item.contains("items")) { - foreach (const QJsonValue &child, item["items"].toArray()) { - pairs.append(QPair(category, child)); - } - } - } - } - else { - // We have feed. - TtRssFeed *feed = new TtRssFeed(); - - if (obtain_icons) { - QString icon_path = item["icon"].type() == QJsonValue::String ? item["icon"].toString() : QString(); - - if (!icon_path.isEmpty()) { - // Chop the "api/" suffix out and append - QString full_icon_address = base_address + QL1C('/') + icon_path; - QByteArray icon_data; - - if (NetworkFactory::performNetworkOperation(full_icon_address, DOWNLOAD_TIMEOUT, - QByteArray(), QString(), icon_data, - QNetworkAccessManager::GetOperation).first == QNetworkReply::NoError) { - // Icon downloaded, set it up. - QPixmap icon_pixmap; - icon_pixmap.loadFromData(icon_data); - feed->setIcon(QIcon(icon_pixmap)); - } - } - } - - feed->setTitle(item["name"].toString()); - feed->setCustomId(item_id); - act_parent->appendChild(feed); - } - } - } - } - - return parent; -} - - -TtRssGetHeadlinesResponse::TtRssGetHeadlinesResponse(const QString &raw_content) : TtRssResponse(raw_content) { -} - -TtRssGetHeadlinesResponse::~TtRssGetHeadlinesResponse() { -} - -QList TtRssGetHeadlinesResponse::messages() const { - QList messages; - - foreach (const QJsonValue &item, m_rawContent["content"].toArray()) { - QJsonObject mapped = item.toObject(); - Message message; - - message.m_author = mapped["author"].toString(); - message.m_isRead = !mapped["unread"].toBool(); - message.m_isImportant = mapped["marked"].toBool(); - message.m_contents = mapped["content"].toString(); - - // Multiply by 1000 because Tiny Tiny RSS API does not include miliseconds in Unix - // date/time number. - message.m_created = TextFactory::parseDateTime(mapped["updated"].toDouble() * 1000); - message.m_createdFromFeed = true; - message.m_customId = QString::number(mapped["id"].toInt()); - message.m_feedId = mapped["feed_id"].toString(); - message.m_title = mapped["title"].toString(); - message.m_url = mapped["link"].toString(); - - if (mapped.contains(QSL("attachments"))) { - // Process enclosures. - foreach (const QJsonValue &attachment, mapped["attachments"].toArray()) { - QJsonObject mapped_attachemnt = attachment.toObject(); - Enclosure enclosure; - - enclosure.m_mimeType = mapped_attachemnt["content_type"].toString(); - enclosure.m_url = mapped_attachemnt["content_url"].toString(); - message.m_enclosures.append(enclosure); - } - } - - messages.append(message); - } - - return messages; -} - - -TtRssUpdateArticleResponse::TtRssUpdateArticleResponse(const QString &raw_content) : TtRssResponse(raw_content) { -} - -TtRssUpdateArticleResponse::~TtRssUpdateArticleResponse() { -} - -QString TtRssUpdateArticleResponse::updateStatus() const { - if (m_rawContent.contains(QSL("content"))) { - return m_rawContent["content"].toObject()["status"].toString(); - } - else { - return QString(); - } -} - -int TtRssUpdateArticleResponse::articlesUpdated() const { - if (m_rawContent.contains(QSL("content"))) { - return m_rawContent["content"].toObject()["updated"].toInt(); - } - else { - return 0; - } -} - -TtRssSubscribeToFeedResponse::TtRssSubscribeToFeedResponse(const QString &raw_content) : TtRssResponse(raw_content) { -} - -TtRssSubscribeToFeedResponse::~TtRssSubscribeToFeedResponse() { -} - -int TtRssSubscribeToFeedResponse::code() const { - if (m_rawContent.contains(QSL("content"))) { - return m_rawContent["content"].toObject()["status"].toObject()["code"].toInt(); - } - else { - return STF_UNKNOWN; - } -} - - -TtRssUnsubscribeFeedResponse::TtRssUnsubscribeFeedResponse(const QString &raw_content) : TtRssResponse(raw_content) { -} - -TtRssUnsubscribeFeedResponse::~TtRssUnsubscribeFeedResponse() { -} - -QString TtRssUnsubscribeFeedResponse::code() const { - if (m_rawContent.contains(QSL("content"))) { - QJsonObject map = m_rawContent["content"].toObject(); - - if (map.contains(QSL("error"))) { - return map["error"].toString(); - } - else if (map.contains(QSL("status"))) { - return map["status"].toString(); - } - } - - return QString(); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/tt-rss/network/ttrssnetworkfactory.h" + +#include "definitions/definitions.h" +#include "services/abstract/rootitem.h" +#include "services/tt-rss/definitions.h" +#include "services/tt-rss/ttrssfeed.h" +#include "services/tt-rss/ttrsscategory.h" +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/textfactory.h" +#include "network-web/networkfactory.h" + +#include +#include +#include +#include + + +TtRssNetworkFactory::TtRssNetworkFactory() + : m_bareUrl(QString()), m_fullUrl(QString()), m_username(QString()), m_password(QString()), m_forceServerSideUpdate(false), m_authIsUsed(false), + m_authUsername(QString()), m_authPassword(QString()), m_sessionId(QString()), + m_lastLoginTime(QDateTime()), m_lastError(QNetworkReply::NoError) { +} + +TtRssNetworkFactory::~TtRssNetworkFactory() { +} + +QString TtRssNetworkFactory::url() const { + return m_bareUrl; +} + +void TtRssNetworkFactory::setUrl(const QString& url) { + m_bareUrl = url; + + if (!m_bareUrl.endsWith(QSL("/"))) { + m_bareUrl = m_bareUrl + QSL("/"); + } + + if (!m_bareUrl.endsWith(QSL("api/"))) { + m_fullUrl = m_bareUrl + QSL("api/"); + } + + else { + m_fullUrl = m_bareUrl; + } +} + +QString TtRssNetworkFactory::username() const { + return m_username; +} + +void TtRssNetworkFactory::setUsername(const QString& username) { + m_username = username; +} + +QString TtRssNetworkFactory::password() const { + return m_password; +} + +void TtRssNetworkFactory::setPassword(const QString& password) { + m_password = password; +} + +QDateTime TtRssNetworkFactory::lastLoginTime() const { + return m_lastLoginTime; +} + +QNetworkReply::NetworkError TtRssNetworkFactory::lastError() const { + return m_lastError; +} + +TtRssLoginResponse TtRssNetworkFactory::login() { + if (!m_sessionId.isEmpty()) { + qDebug("TT-RSS: Session ID is not empty before login, logging out first."); + logout(); + } + + QJsonObject json; + json["op"] = QSL("login"); + json["user"] = m_username; + json["password"] = m_password; + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + QJsonDocument(json).toJson(QJsonDocument::Compact), CONTENT_TYPE, result_raw, + QNetworkAccessManager::PostOperation, + m_authIsUsed, m_authUsername, m_authPassword); + TtRssLoginResponse login_response(QString::fromUtf8(result_raw)); + + if (network_reply.first == QNetworkReply::NoError) { + m_sessionId = login_response.sessionId(); + m_lastLoginTime = QDateTime::currentDateTime(); + } + + else { + qWarning("TT-RSS: Login failed with error %d.", network_reply.first); + } + + m_lastError = network_reply.first; + return login_response; +} + +TtRssResponse TtRssNetworkFactory::logout() { + if (!m_sessionId.isEmpty()) { + QJsonObject json; + json["op"] = QSL("logout"); + json["sid"] = m_sessionId; + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + QJsonDocument(json).toJson(QJsonDocument::Compact), CONTENT_TYPE, result_raw, + QNetworkAccessManager::PostOperation, + m_authIsUsed, m_authUsername, m_authPassword); + m_lastError = network_reply.first; + + if (m_lastError == QNetworkReply::NoError) { + m_sessionId.clear(); + } + + else { + qWarning("TT-RSS: Logout failed with error %d.", network_reply.first); + } + + return TtRssResponse(QString::fromUtf8(result_raw)); + } + + else { + qWarning("TT-RSS: Cannot logout because session ID is empty."); + m_lastError = QNetworkReply::NoError; + return TtRssResponse(); + } +} + +TtRssGetFeedsCategoriesResponse TtRssNetworkFactory::getFeedsCategories() { + QJsonObject json; + json["op"] = QSL("getFeedTree"); + json["sid"] = m_sessionId; + json["include_empty"] = true; + const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), + CONTENT_TYPE, result_raw, + QNetworkAccessManager::PostOperation, + m_authIsUsed, m_authUsername, m_authPassword); + TtRssGetFeedsCategoriesResponse result(QString::fromUtf8(result_raw)); + + if (result.isNotLoggedIn()) { + // We are not logged in. + login(); + json["sid"] = m_sessionId; + network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), CONTENT_TYPE, result_raw, + QNetworkAccessManager::PostOperation, + m_authIsUsed, m_authUsername, m_authPassword); + result = TtRssGetFeedsCategoriesResponse(QString::fromUtf8(result_raw)); + } + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("TT-RSS: getFeedTree failed with error %d.", network_reply.first); + } + + m_lastError = network_reply.first; + return result; +} + +TtRssGetHeadlinesResponse TtRssNetworkFactory::getHeadlines(int feed_id, int limit, int skip, + bool show_content, bool include_attachments, + bool sanitize) { + QJsonObject json; + json["op"] = QSL("getHeadlines"); + json["sid"] = m_sessionId; + json["feed_id"] = feed_id; + json["force_update"] = m_forceServerSideUpdate; + json["limit"] = limit; + json["skip"] = skip; + json["show_content"] = show_content; + json["include_attachments"] = include_attachments; + json["sanitize"] = sanitize; + const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), + CONTENT_TYPE, result_raw, + QNetworkAccessManager::PostOperation, + m_authIsUsed, m_authUsername, m_authPassword); + TtRssGetHeadlinesResponse result(QString::fromUtf8(result_raw)); + + if (result.isNotLoggedIn()) { + // We are not logged in. + login(); + json["sid"] = m_sessionId; + network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), CONTENT_TYPE, result_raw, + QNetworkAccessManager::PostOperation, + m_authIsUsed, m_authUsername, m_authPassword); + result = TtRssGetHeadlinesResponse(QString::fromUtf8(result_raw)); + } + + IOFactory::writeTextFile("aaa", result_raw); + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("TT-RSS: getHeadlines failed with error %d.", network_reply.first); + } + + m_lastError = network_reply.first; + return result; +} + +TtRssUpdateArticleResponse TtRssNetworkFactory::updateArticles(const QStringList& ids, + UpdateArticle::OperatingField field, + UpdateArticle::Mode mode) { + QJsonObject json; + json["op"] = QSL("updateArticle"); + json["sid"] = m_sessionId; + json["article_ids"] = ids.join(QSL(",")); + json["mode"] = (int) mode; + json["field"] = (int) field; + const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), + CONTENT_TYPE, result_raw, + QNetworkAccessManager::PostOperation, + m_authIsUsed, m_authUsername, m_authPassword); + TtRssUpdateArticleResponse result(QString::fromUtf8(result_raw)); + + if (result.isNotLoggedIn()) { + // We are not logged in. + login(); + json["sid"] = m_sessionId; + network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), + CONTENT_TYPE, result_raw, + QNetworkAccessManager::PostOperation, + m_authIsUsed, m_authUsername, m_authPassword); + result = TtRssUpdateArticleResponse(QString::fromUtf8(result_raw)); + } + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("TT-RSS: updateArticle failed with error %d.", network_reply.first); + } + + m_lastError = network_reply.first; + return result; +} + +TtRssSubscribeToFeedResponse TtRssNetworkFactory::subscribeToFeed(const QString& url, int category_id, + bool protectd, const QString& username, + const QString& password) { + QJsonObject json; + json["op"] = QSL("subscribeToFeed"); + json["sid"] = m_sessionId; + json["feed_url"] = url; + json["category_id"] = category_id; + + if (protectd) { + json["login"] = username; + json["password"] = password; + } + + const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), + CONTENT_TYPE, result_raw, + QNetworkAccessManager::PostOperation, + m_authIsUsed, m_authUsername, m_authPassword); + TtRssSubscribeToFeedResponse result(QString::fromUtf8(result_raw)); + + if (result.isNotLoggedIn()) { + // We are not logged in. + login(); + json["sid"] = m_sessionId; + network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), + CONTENT_TYPE, result_raw, + QNetworkAccessManager::PostOperation, + m_authIsUsed, m_authUsername, m_authPassword); + result = TtRssSubscribeToFeedResponse(QString::fromUtf8(result_raw)); + } + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("TT-RSS: updateArticle failed with error %d.", network_reply.first); + } + + m_lastError = network_reply.first; + return result; +} + +TtRssUnsubscribeFeedResponse TtRssNetworkFactory::unsubscribeFeed(int feed_id) { + QJsonObject json; + json["op"] = QSL("unsubscribeFeed"); + json["sid"] = m_sessionId; + json["feed_id"] = feed_id; + const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), CONTENT_TYPE, result_raw, + QNetworkAccessManager::PostOperation, + m_authIsUsed, m_authUsername, m_authPassword); + TtRssUnsubscribeFeedResponse result(QString::fromUtf8(result_raw)); + + if (result.isNotLoggedIn()) { + // We are not logged in. + login(); + json["sid"] = m_sessionId; + network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout, QJsonDocument(json).toJson(QJsonDocument::Compact), CONTENT_TYPE, result_raw, + QNetworkAccessManager::PostOperation, + m_authIsUsed, m_authUsername, m_authPassword); + result = TtRssUnsubscribeFeedResponse(QString::fromUtf8(result_raw)); + } + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("TT-RSS: getFeeds failed with error %d.", network_reply.first); + } + + m_lastError = network_reply.first; + return result; +} + +bool TtRssNetworkFactory::forceServerSideUpdate() const { + return m_forceServerSideUpdate; +} + +void TtRssNetworkFactory::setForceServerSideUpdate(bool force_server_side_update) { + m_forceServerSideUpdate = force_server_side_update; +} + +bool TtRssNetworkFactory::authIsUsed() const { + return m_authIsUsed; +} + +void TtRssNetworkFactory::setAuthIsUsed(bool auth_is_used) { + m_authIsUsed = auth_is_used; +} + +QString TtRssNetworkFactory::authUsername() const { + return m_authUsername; +} + +void TtRssNetworkFactory::setAuthUsername(const QString& auth_username) { + m_authUsername = auth_username; +} + +QString TtRssNetworkFactory::authPassword() const { + return m_authPassword; +} + +void TtRssNetworkFactory::setAuthPassword(const QString& auth_password) { + m_authPassword = auth_password; +} + +TtRssResponse::TtRssResponse(const QString& raw_content) { + m_rawContent = QJsonDocument::fromJson(raw_content.toUtf8()).object(); +} + +TtRssResponse::~TtRssResponse() { +} + +bool TtRssResponse::isLoaded() const { + return !m_rawContent.isEmpty(); +} + +int TtRssResponse::seq() const { + if (!isLoaded()) { + return CONTENT_NOT_LOADED; + } + + else { + return m_rawContent["seq"].toInt(); + } +} + +int TtRssResponse::status() const { + if (!isLoaded()) { + return CONTENT_NOT_LOADED; + } + + else { + return m_rawContent["status"].toInt(); + } +} + +bool TtRssResponse::isNotLoggedIn() const { + return status() == API_STATUS_ERR && hasError() && error() == NOT_LOGGED_IN; +} + +QString TtRssResponse::toString() const { + return QJsonDocument(m_rawContent).toJson(QJsonDocument::Compact); +} + +TtRssLoginResponse::TtRssLoginResponse(const QString& raw_content) : TtRssResponse(raw_content) { +} + +TtRssLoginResponse::~TtRssLoginResponse() { +} + +int TtRssLoginResponse::apiLevel() const { + if (!isLoaded()) { + return CONTENT_NOT_LOADED; + } + + else { + return m_rawContent["content"].toObject()["api_level"].toInt(); + } +} + +QString TtRssLoginResponse::sessionId() const { + if (!isLoaded()) { + return QString(); + } + + else { + return m_rawContent["content"].toObject()["session_id"].toString(); + } +} + +QString TtRssResponse::error() const { + if (!isLoaded()) { + return QString(); + } + + else { + return m_rawContent["content"].toObject()["error"].toString(); + } +} + +bool TtRssResponse::hasError() const { + if (!isLoaded()) { + return false; + } + + else { + return m_rawContent["content"].toObject().contains("error"); + } +} + + +TtRssGetFeedsCategoriesResponse::TtRssGetFeedsCategoriesResponse(const QString& raw_content) : TtRssResponse(raw_content) { +} + +TtRssGetFeedsCategoriesResponse::~TtRssGetFeedsCategoriesResponse() { +} + +RootItem* TtRssGetFeedsCategoriesResponse::feedsCategories(bool obtain_icons, QString base_address) const { + RootItem* parent = new RootItem(); + // Chop the "api/" from the end of the address. + base_address.chop(4); + qDebug("TT-RSS: Chopped base address to '%s' to get feed icons.", qPrintable(base_address)); + + if (status() == API_STATUS_OK) { + // We have data, construct object tree according to data. + QJsonArray items_to_process = m_rawContent["content"].toObject()["categories"].toObject()["items"].toArray(); + QVector> pairs; + + foreach (const QJsonValue& item, items_to_process) { + pairs.append(QPair(parent, item)); + } + + while (!pairs.isEmpty()) { + QPair pair = pairs.takeFirst(); + RootItem* act_parent = pair.first; + QJsonObject item = pair.second.toObject(); + int item_id = item["bare_id"].toInt(); + bool is_category = item.contains("type") && item["type"].toString() == GFT_TYPE_CATEGORY; + + if (item_id >= 0) { + if (is_category) { + if (item_id == 0) { + // This is "Uncategorized" category, all its feeds belong to top-level root. + if (item.contains("items")) { + foreach (const QJsonValue& child_feed, item["items"].toArray()) { + pairs.append(QPair(parent, child_feed)); + } + } + } + + else { + TtRssCategory* category = new TtRssCategory(); + category->setTitle(item["name"].toString()); + category->setCustomId(item_id); + act_parent->appendChild(category); + + if (item.contains("items")) { + foreach (const QJsonValue& child, item["items"].toArray()) { + pairs.append(QPair(category, child)); + } + } + } + } + + else { + // We have feed. + TtRssFeed* feed = new TtRssFeed(); + + if (obtain_icons) { + QString icon_path = item["icon"].type() == QJsonValue::String ? item["icon"].toString() : QString(); + + if (!icon_path.isEmpty()) { + // Chop the "api/" suffix out and append + QString full_icon_address = base_address + QL1C('/') + icon_path; + QByteArray icon_data; + + if (NetworkFactory::performNetworkOperation(full_icon_address, DOWNLOAD_TIMEOUT, + QByteArray(), QString(), icon_data, + QNetworkAccessManager::GetOperation).first == QNetworkReply::NoError) { + // Icon downloaded, set it up. + QPixmap icon_pixmap; + icon_pixmap.loadFromData(icon_data); + feed->setIcon(QIcon(icon_pixmap)); + } + } + } + + feed->setTitle(item["name"].toString()); + feed->setCustomId(item_id); + act_parent->appendChild(feed); + } + } + } + } + + return parent; +} + + +TtRssGetHeadlinesResponse::TtRssGetHeadlinesResponse(const QString& raw_content) : TtRssResponse(raw_content) { +} + +TtRssGetHeadlinesResponse::~TtRssGetHeadlinesResponse() { +} + +QList TtRssGetHeadlinesResponse::messages() const { + QList messages; + + foreach (const QJsonValue& item, m_rawContent["content"].toArray()) { + QJsonObject mapped = item.toObject(); + Message message; + message.m_author = mapped["author"].toString(); + message.m_isRead = !mapped["unread"].toBool(); + message.m_isImportant = mapped["marked"].toBool(); + message.m_contents = mapped["content"].toString(); + // Multiply by 1000 because Tiny Tiny RSS API does not include miliseconds in Unix + // date/time number. + message.m_created = TextFactory::parseDateTime(mapped["updated"].toDouble() * 1000); + message.m_createdFromFeed = true; + message.m_customId = QString::number(mapped["id"].toInt()); + message.m_feedId = mapped["feed_id"].toString(); + message.m_title = mapped["title"].toString(); + message.m_url = mapped["link"].toString(); + + if (mapped.contains(QSL("attachments"))) { + // Process enclosures. + foreach (const QJsonValue& attachment, mapped["attachments"].toArray()) { + QJsonObject mapped_attachemnt = attachment.toObject(); + Enclosure enclosure; + enclosure.m_mimeType = mapped_attachemnt["content_type"].toString(); + enclosure.m_url = mapped_attachemnt["content_url"].toString(); + message.m_enclosures.append(enclosure); + } + } + + messages.append(message); + } + + return messages; +} + + +TtRssUpdateArticleResponse::TtRssUpdateArticleResponse(const QString& raw_content) : TtRssResponse(raw_content) { +} + +TtRssUpdateArticleResponse::~TtRssUpdateArticleResponse() { +} + +QString TtRssUpdateArticleResponse::updateStatus() const { + if (m_rawContent.contains(QSL("content"))) { + return m_rawContent["content"].toObject()["status"].toString(); + } + + else { + return QString(); + } +} + +int TtRssUpdateArticleResponse::articlesUpdated() const { + if (m_rawContent.contains(QSL("content"))) { + return m_rawContent["content"].toObject()["updated"].toInt(); + } + + else { + return 0; + } +} + +TtRssSubscribeToFeedResponse::TtRssSubscribeToFeedResponse(const QString& raw_content) : TtRssResponse(raw_content) { +} + +TtRssSubscribeToFeedResponse::~TtRssSubscribeToFeedResponse() { +} + +int TtRssSubscribeToFeedResponse::code() const { + if (m_rawContent.contains(QSL("content"))) { + return m_rawContent["content"].toObject()["status"].toObject()["code"].toInt(); + } + + else { + return STF_UNKNOWN; + } +} + + +TtRssUnsubscribeFeedResponse::TtRssUnsubscribeFeedResponse(const QString& raw_content) : TtRssResponse(raw_content) { +} + +TtRssUnsubscribeFeedResponse::~TtRssUnsubscribeFeedResponse() { +} + +QString TtRssUnsubscribeFeedResponse::code() const { + if (m_rawContent.contains(QSL("content"))) { + QJsonObject map = m_rawContent["content"].toObject(); + + if (map.contains(QSL("error"))) { + return map["error"].toString(); + } + + else if (map.contains(QSL("status"))) { + return map["status"].toString(); + } + } + + return QString(); +} diff --git a/src/services/tt-rss/network/ttrssnetworkfactory.h b/src/services/tt-rss/network/ttrssnetworkfactory.h index f7eded0c4..a14716fa5 100755 --- a/src/services/tt-rss/network/ttrssnetworkfactory.h +++ b/src/services/tt-rss/network/ttrssnetworkfactory.h @@ -1,187 +1,187 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef TTRSSNETWORKFACTORY_H -#define TTRSSNETWORKFACTORY_H - -#include "core/message.h" - -#include -#include -#include -#include - - -class RootItem; -class TtRssFeed; - -class TtRssResponse { - public: - explicit TtRssResponse(const QString &raw_content = QString()); - virtual ~TtRssResponse(); - - bool isLoaded() const; - - int seq() const; - int status() const; - QString error() const; - bool hasError() const; - bool isNotLoggedIn() const; - QString toString() const; - - protected: - QJsonObject m_rawContent; -}; - -class TtRssLoginResponse : public TtRssResponse { - public: - explicit TtRssLoginResponse(const QString &raw_content = QString()); - virtual ~TtRssLoginResponse(); - - int apiLevel() const; - QString sessionId() const; -}; - -class TtRssGetFeedsCategoriesResponse : public TtRssResponse { - public: - explicit TtRssGetFeedsCategoriesResponse(const QString &raw_content = QString()); - virtual ~TtRssGetFeedsCategoriesResponse(); - - // Returns tree of feeds/categories. - // Top-level root of the tree is not needed here. - // Returned items do not have primary IDs assigned. - RootItem *feedsCategories(bool obtain_icons, QString base_address = QString()) const; -}; - -class TtRssGetHeadlinesResponse : public TtRssResponse { - public: - explicit TtRssGetHeadlinesResponse(const QString &raw_content = QString()); - virtual ~TtRssGetHeadlinesResponse(); - - QList messages() const; -}; - -class TtRssUpdateArticleResponse : public TtRssResponse { - public: - explicit TtRssUpdateArticleResponse(const QString &raw_content = QString()); - virtual ~TtRssUpdateArticleResponse(); - - QString updateStatus() const; - int articlesUpdated() const; -}; - -class TtRssSubscribeToFeedResponse : public TtRssResponse { - public: - explicit TtRssSubscribeToFeedResponse(const QString &raw_content = QString()); - virtual ~TtRssSubscribeToFeedResponse(); - - int code() const; -}; - -class TtRssUnsubscribeFeedResponse : public TtRssResponse { - public: - explicit TtRssUnsubscribeFeedResponse(const QString &raw_content = QString()); - virtual ~TtRssUnsubscribeFeedResponse(); - - QString code() const; -}; - -namespace UpdateArticle { - enum Mode { - SetToFalse = 0, - SetToTrue = 1, - Togggle = 2 - }; - - enum OperatingField { - Starred = 0, - Published = 1, - Unread = 2 - }; -} - -class TtRssNetworkFactory { - public: - explicit TtRssNetworkFactory(); - virtual ~TtRssNetworkFactory(); - - QString url() const; - void setUrl(const QString &url); - - QString username() const; - void setUsername(const QString &username); - - QString password() const; - void setPassword(const QString &password); - - bool authIsUsed() const; - void setAuthIsUsed(bool auth_is_used); - - QString authUsername() const; - void setAuthUsername(const QString &auth_username); - - QString authPassword() const; - void setAuthPassword(const QString &auth_password); - - bool forceServerSideUpdate() const; - void setForceServerSideUpdate(bool force_server_side_update); - - // Metadata. - QDateTime lastLoginTime() const; - QNetworkReply::NetworkError lastError() const; - - // Operations. - - // Logs user in. - TtRssLoginResponse login(); - - // Logs user out. - TtRssResponse logout(); - - // Gets feeds from the server. - TtRssGetFeedsCategoriesResponse getFeedsCategories(); - - // Gets headlines (messages) from the server. - TtRssGetHeadlinesResponse getHeadlines(int feed_id, int limit, int skip, - bool show_content, bool include_attachments, - bool sanitize); - - TtRssUpdateArticleResponse updateArticles(const QStringList &ids, UpdateArticle::OperatingField field, - UpdateArticle::Mode mode); - - TtRssSubscribeToFeedResponse subscribeToFeed(const QString &url, int category_id, bool protectd = false, - const QString &username = QString(), const QString &password = QString()); - - TtRssUnsubscribeFeedResponse unsubscribeFeed(int feed_id); - - //TtRssGetConfigResponse getConfig(); - - private: - QString m_bareUrl; - QString m_fullUrl; - QString m_username; - QString m_password; - bool m_forceServerSideUpdate; - bool m_authIsUsed; - QString m_authUsername; - QString m_authPassword; - QString m_sessionId; - QDateTime m_lastLoginTime; - QNetworkReply::NetworkError m_lastError; -}; - -#endif // TTRSSNETWORKFACTORY_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef TTRSSNETWORKFACTORY_H +#define TTRSSNETWORKFACTORY_H + +#include "core/message.h" + +#include +#include +#include +#include + + +class RootItem; +class TtRssFeed; + +class TtRssResponse { + public: + explicit TtRssResponse(const QString& raw_content = QString()); + virtual ~TtRssResponse(); + + bool isLoaded() const; + + int seq() const; + int status() const; + QString error() const; + bool hasError() const; + bool isNotLoggedIn() const; + QString toString() const; + + protected: + QJsonObject m_rawContent; +}; + +class TtRssLoginResponse : public TtRssResponse { + public: + explicit TtRssLoginResponse(const QString& raw_content = QString()); + virtual ~TtRssLoginResponse(); + + int apiLevel() const; + QString sessionId() const; +}; + +class TtRssGetFeedsCategoriesResponse : public TtRssResponse { + public: + explicit TtRssGetFeedsCategoriesResponse(const QString& raw_content = QString()); + virtual ~TtRssGetFeedsCategoriesResponse(); + + // Returns tree of feeds/categories. + // Top-level root of the tree is not needed here. + // Returned items do not have primary IDs assigned. + RootItem* feedsCategories(bool obtain_icons, QString base_address = QString()) const; +}; + +class TtRssGetHeadlinesResponse : public TtRssResponse { + public: + explicit TtRssGetHeadlinesResponse(const QString& raw_content = QString()); + virtual ~TtRssGetHeadlinesResponse(); + + QList messages() const; +}; + +class TtRssUpdateArticleResponse : public TtRssResponse { + public: + explicit TtRssUpdateArticleResponse(const QString& raw_content = QString()); + virtual ~TtRssUpdateArticleResponse(); + + QString updateStatus() const; + int articlesUpdated() const; +}; + +class TtRssSubscribeToFeedResponse : public TtRssResponse { + public: + explicit TtRssSubscribeToFeedResponse(const QString& raw_content = QString()); + virtual ~TtRssSubscribeToFeedResponse(); + + int code() const; +}; + +class TtRssUnsubscribeFeedResponse : public TtRssResponse { + public: + explicit TtRssUnsubscribeFeedResponse(const QString& raw_content = QString()); + virtual ~TtRssUnsubscribeFeedResponse(); + + QString code() const; +}; + +namespace UpdateArticle { + enum Mode { + SetToFalse = 0, + SetToTrue = 1, + Togggle = 2 + }; + + enum OperatingField { + Starred = 0, + Published = 1, + Unread = 2 + }; +} + +class TtRssNetworkFactory { + public: + explicit TtRssNetworkFactory(); + virtual ~TtRssNetworkFactory(); + + QString url() const; + void setUrl(const QString& url); + + QString username() const; + void setUsername(const QString& username); + + QString password() const; + void setPassword(const QString& password); + + bool authIsUsed() const; + void setAuthIsUsed(bool auth_is_used); + + QString authUsername() const; + void setAuthUsername(const QString& auth_username); + + QString authPassword() const; + void setAuthPassword(const QString& auth_password); + + bool forceServerSideUpdate() const; + void setForceServerSideUpdate(bool force_server_side_update); + + // Metadata. + QDateTime lastLoginTime() const; + QNetworkReply::NetworkError lastError() const; + + // Operations. + + // Logs user in. + TtRssLoginResponse login(); + + // Logs user out. + TtRssResponse logout(); + + // Gets feeds from the server. + TtRssGetFeedsCategoriesResponse getFeedsCategories(); + + // Gets headlines (messages) from the server. + TtRssGetHeadlinesResponse getHeadlines(int feed_id, int limit, int skip, + bool show_content, bool include_attachments, + bool sanitize); + + TtRssUpdateArticleResponse updateArticles(const QStringList& ids, UpdateArticle::OperatingField field, + UpdateArticle::Mode mode); + + TtRssSubscribeToFeedResponse subscribeToFeed(const QString& url, int category_id, bool protectd = false, + const QString& username = QString(), const QString& password = QString()); + + TtRssUnsubscribeFeedResponse unsubscribeFeed(int feed_id); + + //TtRssGetConfigResponse getConfig(); + + private: + QString m_bareUrl; + QString m_fullUrl; + QString m_username; + QString m_password; + bool m_forceServerSideUpdate; + bool m_authIsUsed; + QString m_authUsername; + QString m_authPassword; + QString m_sessionId; + QDateTime m_lastLoginTime; + QNetworkReply::NetworkError m_lastError; +}; + +#endif // TTRSSNETWORKFACTORY_H diff --git a/src/services/tt-rss/ttrsscategory.cpp b/src/services/tt-rss/ttrsscategory.cpp index 8f5c6ad26..7f237bda1 100755 --- a/src/services/tt-rss/ttrsscategory.cpp +++ b/src/services/tt-rss/ttrsscategory.cpp @@ -27,39 +27,40 @@ #include -TtRssCategory::TtRssCategory(RootItem *parent) : Category(parent) { - setIcon(qApp->icons()->fromTheme(QSL("folder"))); +TtRssCategory::TtRssCategory(RootItem* parent) : Category(parent) { + setIcon(qApp->icons()->fromTheme(QSL("folder"))); } -TtRssCategory::TtRssCategory(const QSqlRecord &record) : Category(nullptr) { - setIcon(qApp->icons()->fromTheme(QSL("folder"))); - setId(record.value(CAT_DB_ID_INDEX).toInt()); - setTitle(record.value(CAT_DB_TITLE_INDEX).toString()); - setCustomId(record.value(CAT_DB_CUSTOM_ID_INDEX).toInt()); +TtRssCategory::TtRssCategory(const QSqlRecord& record) : Category(nullptr) { + setIcon(qApp->icons()->fromTheme(QSL("folder"))); + setId(record.value(CAT_DB_ID_INDEX).toInt()); + setTitle(record.value(CAT_DB_TITLE_INDEX).toString()); + setCustomId(record.value(CAT_DB_CUSTOM_ID_INDEX).toInt()); } TtRssCategory::~TtRssCategory() { } -TtRssServiceRoot *TtRssCategory::serviceRoot() const { - return qobject_cast(getParentServiceRoot()); +TtRssServiceRoot* TtRssCategory::serviceRoot() const { + return qobject_cast(getParentServiceRoot()); } bool TtRssCategory::markAsReadUnread(RootItem::ReadStatus status) { - const QStringList ids = serviceRoot()->customIDSOfMessagesForItem(this); - TtRssUpdateArticleResponse response = serviceRoot()->network()->updateArticles(ids, UpdateArticle::Unread, - status == RootItem::Unread ? - UpdateArticle::SetToTrue : - UpdateArticle::SetToFalse); + const QStringList ids = serviceRoot()->customIDSOfMessagesForItem(this); + TtRssUpdateArticleResponse response = serviceRoot()->network()->updateArticles(ids, UpdateArticle::Unread, + status == RootItem::Unread ? + UpdateArticle::SetToTrue : + UpdateArticle::SetToFalse); - if (serviceRoot()->network()->lastError() != QNetworkReply::NoError || response.updateStatus() != STATUS_OK) { - return false; - } - else { - return serviceRoot()->markFeedsReadUnread(getSubTreeFeeds(), status); - } + if (serviceRoot()->network()->lastError() != QNetworkReply::NoError || response.updateStatus() != STATUS_OK) { + return false; + } + + else { + return serviceRoot()->markFeedsReadUnread(getSubTreeFeeds(), status); + } } bool TtRssCategory::cleanMessages(bool clear_only_read) { - return serviceRoot()->cleanFeeds(getSubTreeFeeds(), clear_only_read); + return serviceRoot()->cleanFeeds(getSubTreeFeeds(), clear_only_read); } diff --git a/src/services/tt-rss/ttrsscategory.h b/src/services/tt-rss/ttrsscategory.h index 1c810af2d..3428f0d21 100755 --- a/src/services/tt-rss/ttrsscategory.h +++ b/src/services/tt-rss/ttrsscategory.h @@ -26,16 +26,16 @@ class TtRssServiceRoot; class TtRssCategory : public Category { - Q_OBJECT + Q_OBJECT - public: - explicit TtRssCategory(RootItem *parent = nullptr); - explicit TtRssCategory(const QSqlRecord &record); - virtual ~TtRssCategory(); + public: + explicit TtRssCategory(RootItem* parent = nullptr); + explicit TtRssCategory(const QSqlRecord& record); + virtual ~TtRssCategory(); - TtRssServiceRoot *serviceRoot() const; - bool markAsReadUnread(ReadStatus status); - bool cleanMessages(bool clear_only_read); + TtRssServiceRoot* serviceRoot() const; + bool markAsReadUnread(ReadStatus status); + bool cleanMessages(bool clear_only_read); }; #endif // TTRSSCATEGORY_H diff --git a/src/services/tt-rss/ttrssfeed.cpp b/src/services/tt-rss/ttrssfeed.cpp index 06279b61b..85cc362ba 100755 --- a/src/services/tt-rss/ttrssfeed.cpp +++ b/src/services/tt-rss/ttrssfeed.cpp @@ -31,155 +31,155 @@ #include -TtRssFeed::TtRssFeed(RootItem *parent) - : Feed(parent) { +TtRssFeed::TtRssFeed(RootItem* parent) + : Feed(parent) { } -TtRssFeed::TtRssFeed(const QSqlRecord &record) : Feed(nullptr) { - setTitle(record.value(FDS_DB_TITLE_INDEX).toString()); - setId(record.value(FDS_DB_ID_INDEX).toInt()); - setIcon(qApp->icons()->fromByteArray(record.value(FDS_DB_ICON_INDEX).toByteArray())); - setAutoUpdateType(static_cast(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt())); - setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt()); - setCustomId(record.value(FDS_DB_CUSTOM_ID_INDEX).toInt()); - - qDebug("Custom ID of TT-RSS feed when loading from DB is '%s'.", qPrintable(record.value(FDS_DB_CUSTOM_ID_INDEX).toString())); +TtRssFeed::TtRssFeed(const QSqlRecord& record) : Feed(nullptr) { + setTitle(record.value(FDS_DB_TITLE_INDEX).toString()); + setId(record.value(FDS_DB_ID_INDEX).toInt()); + setIcon(qApp->icons()->fromByteArray(record.value(FDS_DB_ICON_INDEX).toByteArray())); + setAutoUpdateType(static_cast(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt())); + setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt()); + setCustomId(record.value(FDS_DB_CUSTOM_ID_INDEX).toInt()); + qDebug("Custom ID of TT-RSS feed when loading from DB is '%s'.", qPrintable(record.value(FDS_DB_CUSTOM_ID_INDEX).toString())); } TtRssFeed::~TtRssFeed() { } -TtRssServiceRoot *TtRssFeed::serviceRoot() const { - return qobject_cast(getParentServiceRoot()); +TtRssServiceRoot* TtRssFeed::serviceRoot() const { + return qobject_cast(getParentServiceRoot()); } QVariant TtRssFeed::data(int column, int role) const { - switch (role) { - case Qt::ToolTipRole: - if (column == FDS_MODEL_TITLE_INDEX) { - QString auto_update_string; + switch (role) { + case Qt::ToolTipRole: + if (column == FDS_MODEL_TITLE_INDEX) { + QString auto_update_string; - switch (autoUpdateType()) { - case DontAutoUpdate: - //: Describes feed auto-update status. - auto_update_string = tr("does not use auto-update"); - break; + switch (autoUpdateType()) { + case DontAutoUpdate: + //: Describes feed auto-update status. + auto_update_string = tr("does not use auto-update"); + break; - case DefaultAutoUpdate: - //: Describes feed auto-update status. - auto_update_string = tr("uses global settings"); - break; + case DefaultAutoUpdate: + //: Describes feed auto-update status. + auto_update_string = tr("uses global settings"); + break; - case SpecificAutoUpdate: - default: - //: Describes feed auto-update status. - auto_update_string = tr("uses specific settings " - "(%n minute(s) to next auto-update)", - 0, - autoUpdateRemainingInterval()); - break; - } + case SpecificAutoUpdate: + default: + //: Describes feed auto-update status. + auto_update_string = tr("uses specific settings " + "(%n minute(s) to next auto-update)", + 0, + autoUpdateRemainingInterval()); + break; + } - //: Tooltip for feed. - return tr("%1" - "%2\n\n" - "Auto-update status: %3").arg(title(), - description().isEmpty() ? QString() : QString('\n') + description(), - auto_update_string); - } - else { - return Feed::data(column, role); - } + //: Tooltip for feed. + return tr("%1" + "%2\n\n" + "Auto-update status: %3").arg(title(), + description().isEmpty() ? QString() : QString('\n') + description(), + auto_update_string); + } - default: - return Feed::data(column, role); - } + else { + return Feed::data(column, role); + } + + default: + return Feed::data(column, role); + } } bool TtRssFeed::canBeEdited() const { - return true; + return true; } bool TtRssFeed::editViaGui() { - QPointer form_pointer = new FormTtRssFeedDetails(serviceRoot(), qApp->mainFormWidget()); - - form_pointer.data()->addEditFeed(this, nullptr); - delete form_pointer.data(); - return false; + QPointer form_pointer = new FormTtRssFeedDetails(serviceRoot(), qApp->mainFormWidget()); + form_pointer.data()->addEditFeed(this, nullptr); + delete form_pointer.data(); + return false; } bool TtRssFeed::canBeDeleted() const { - return true; + return true; } bool TtRssFeed::deleteViaGui() { - TtRssUnsubscribeFeedResponse response = serviceRoot()->network()->unsubscribeFeed(customId()); + TtRssUnsubscribeFeedResponse response = serviceRoot()->network()->unsubscribeFeed(customId()); - if (response.code() == UFF_OK && removeItself()) { - serviceRoot()->requestItemRemoval(this); - return true; - } - else { - qWarning("TT-RSS: Unsubscribing from feed failed, received JSON: '%s'", qPrintable(response.toString())); - return false; - } + if (response.code() == UFF_OK && removeItself()) { + serviceRoot()->requestItemRemoval(this); + return true; + } + + else { + qWarning("TT-RSS: Unsubscribing from feed failed, received JSON: '%s'", qPrintable(response.toString())); + return false; + } } bool TtRssFeed::markAsReadUnread(RootItem::ReadStatus status) { - serviceRoot()->addMessageStatesToCache(getParentServiceRoot()->customIDSOfMessagesForItem(this), status); - return getParentServiceRoot()->markFeedsReadUnread(QList() << this, status); + serviceRoot()->addMessageStatesToCache(getParentServiceRoot()->customIDSOfMessagesForItem(this), status); + return getParentServiceRoot()->markFeedsReadUnread(QList() << this, status); } bool TtRssFeed::cleanMessages(bool clear_only_read) { - return getParentServiceRoot()->cleanFeeds(QList() << this, clear_only_read); + return getParentServiceRoot()->cleanFeeds(QList() << this, clear_only_read); } -bool TtRssFeed::editItself(TtRssFeed *new_feed_data) { - QSqlDatabase database = qApp->database()->connection("aa", DatabaseFactory::FromSettings); +bool TtRssFeed::editItself(TtRssFeed* new_feed_data) { + QSqlDatabase database = qApp->database()->connection("aa", DatabaseFactory::FromSettings); - if (DatabaseQueries::editBaseFeed(database, id(), new_feed_data->autoUpdateType(), - new_feed_data->autoUpdateInitialInterval())) { - setAutoUpdateType(new_feed_data->autoUpdateType()); - setAutoUpdateInitialInterval(new_feed_data->autoUpdateInitialInterval()); - return true; - } - else { - return false; - } + if (DatabaseQueries::editBaseFeed(database, id(), new_feed_data->autoUpdateType(), + new_feed_data->autoUpdateInitialInterval())) { + setAutoUpdateType(new_feed_data->autoUpdateType()); + setAutoUpdateInitialInterval(new_feed_data->autoUpdateInitialInterval()); + return true; + } + + else { + return false; + } } -QList TtRssFeed::obtainNewMessages(bool *error_during_obtaining) { - QList messages; - int newly_added_messages = 0; - int limit = MAX_MESSAGES; - int skip = 0; +QList TtRssFeed::obtainNewMessages(bool* error_during_obtaining) { + QList messages; + int newly_added_messages = 0; + int limit = MAX_MESSAGES; + int skip = 0; - do { - TtRssGetHeadlinesResponse headlines = serviceRoot()->network()->getHeadlines(customId(), limit, skip, - true, true, false); + do { + TtRssGetHeadlinesResponse headlines = serviceRoot()->network()->getHeadlines(customId(), limit, skip, + true, true, false); - if (serviceRoot()->network()->lastError() != QNetworkReply::NoError) { - setStatus(Feed::NetworkError); - *error_during_obtaining = true; - serviceRoot()->itemChanged(QList() << this); - return QList(); - } - else { - QList new_messages = headlines.messages(); + if (serviceRoot()->network()->lastError() != QNetworkReply::NoError) { + setStatus(Feed::NetworkError); + *error_during_obtaining = true; + serviceRoot()->itemChanged(QList() << this); + return QList(); + } - messages.append(new_messages); - newly_added_messages = new_messages.size(); - skip += newly_added_messages; - } - } - while (newly_added_messages > 0); + else { + QList new_messages = headlines.messages(); + messages.append(new_messages); + newly_added_messages = new_messages.size(); + skip += newly_added_messages; + } + } + while (newly_added_messages > 0); - *error_during_obtaining = false; - return messages; + *error_during_obtaining = false; + return messages; } bool TtRssFeed::removeItself() { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - return DatabaseQueries::deleteFeed(database, customId(), serviceRoot()->accountId()); + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + return DatabaseQueries::deleteFeed(database, customId(), serviceRoot()->accountId()); } diff --git a/src/services/tt-rss/ttrssfeed.h b/src/services/tt-rss/ttrssfeed.h index 98628a2b5..73059f872 100755 --- a/src/services/tt-rss/ttrssfeed.h +++ b/src/services/tt-rss/ttrssfeed.h @@ -26,28 +26,28 @@ class TtRssServiceRoot; class TtRssFeed : public Feed { - Q_OBJECT + Q_OBJECT - public: - explicit TtRssFeed(RootItem *parent = nullptr); - explicit TtRssFeed(const QSqlRecord &record); - virtual ~TtRssFeed(); + public: + explicit TtRssFeed(RootItem* parent = nullptr); + explicit TtRssFeed(const QSqlRecord& record); + virtual ~TtRssFeed(); - TtRssServiceRoot *serviceRoot() const; - QVariant data(int column, int role) const; - bool canBeEdited() const; - bool editViaGui(); - bool canBeDeleted() const; - bool deleteViaGui(); + TtRssServiceRoot* serviceRoot() const; + QVariant data(int column, int role) const; + bool canBeEdited() const; + bool editViaGui(); + bool canBeDeleted() const; + bool deleteViaGui(); - bool markAsReadUnread(ReadStatus status); - bool cleanMessages(bool clear_only_read); + bool markAsReadUnread(ReadStatus status); + bool cleanMessages(bool clear_only_read); - bool editItself(TtRssFeed *new_feed_data); - bool removeItself(); + bool editItself(TtRssFeed* new_feed_data); + bool removeItself(); - private: - QList obtainNewMessages(bool *error_during_obtaining); + private: + QList obtainNewMessages(bool* error_during_obtaining); }; #endif // TTRSSFEED_H diff --git a/src/services/tt-rss/ttrssrecyclebin.cpp b/src/services/tt-rss/ttrssrecyclebin.cpp index 39b8e7923..febc97b69 100755 --- a/src/services/tt-rss/ttrssrecyclebin.cpp +++ b/src/services/tt-rss/ttrssrecyclebin.cpp @@ -22,17 +22,17 @@ #include "services/tt-rss/ttrssserviceroot.h" -TtRssRecycleBin::TtRssRecycleBin(RootItem *parent) : RecycleBin(parent) { +TtRssRecycleBin::TtRssRecycleBin(RootItem* parent) : RecycleBin(parent) { } TtRssRecycleBin::~TtRssRecycleBin() { } -TtRssServiceRoot *TtRssRecycleBin::serviceRoot() { - return qobject_cast(getParentServiceRoot()); +TtRssServiceRoot* TtRssRecycleBin::serviceRoot() { + return qobject_cast(getParentServiceRoot()); } bool TtRssRecycleBin::markAsReadUnread(RootItem::ReadStatus status) { - serviceRoot()->addMessageStatesToCache(getParentServiceRoot()->customIDSOfMessagesForItem(this), status); - return RecycleBin::markAsReadUnread(status); + serviceRoot()->addMessageStatesToCache(getParentServiceRoot()->customIDSOfMessagesForItem(this), status); + return RecycleBin::markAsReadUnread(status); } diff --git a/src/services/tt-rss/ttrssrecyclebin.h b/src/services/tt-rss/ttrssrecyclebin.h index 8bd4881c1..1065cecd8 100755 --- a/src/services/tt-rss/ttrssrecyclebin.h +++ b/src/services/tt-rss/ttrssrecyclebin.h @@ -24,14 +24,14 @@ class TtRssServiceRoot; class TtRssRecycleBin : public RecycleBin { - Q_OBJECT + Q_OBJECT - public: - explicit TtRssRecycleBin(RootItem *parent = nullptr); - virtual ~TtRssRecycleBin(); + public: + explicit TtRssRecycleBin(RootItem* parent = nullptr); + virtual ~TtRssRecycleBin(); - TtRssServiceRoot *serviceRoot(); - bool markAsReadUnread(ReadStatus status); + TtRssServiceRoot* serviceRoot(); + bool markAsReadUnread(ReadStatus status); }; #endif // TTRSSRECYCLEBIN_H diff --git a/src/services/tt-rss/ttrssserviceentrypoint.cpp b/src/services/tt-rss/ttrssserviceentrypoint.cpp index 577402d03..427c692b5 100755 --- a/src/services/tt-rss/ttrssserviceentrypoint.cpp +++ b/src/services/tt-rss/ttrssserviceentrypoint.cpp @@ -1,78 +1,77 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/tt-rss/ttrssserviceentrypoint.h" - -#include "definitions/definitions.h" -#include "miscellaneous/iconfactory.h" -#include "miscellaneous/databasequeries.h" -#include "services/tt-rss/definitions.h" -#include "services/tt-rss/ttrssserviceroot.h" -#include "services/tt-rss/gui/formeditttrssaccount.h" - -#include - - -TtRssServiceEntryPoint::TtRssServiceEntryPoint(){ -} - - -TtRssServiceEntryPoint::~TtRssServiceEntryPoint() { -} - -bool TtRssServiceEntryPoint::isSingleInstanceService() const { - return false; -} - -QString TtRssServiceEntryPoint::name() const { - return QSL("Tiny Tiny RSS"); -} - -QString TtRssServiceEntryPoint::description() const { - return QObject::tr("This service offers integration with Tiny Tiny RSS.\n\n" - "Tiny Tiny RSS is an open source web-based news feed (RSS/Atom) reader and aggregator, " - "designed to allow you to read news from any location, while feeling as close to a real " - "desktop application as possible.\n\nAt least API level %1 is required.").arg(MINIMAL_API_LEVEL); -} - -QString TtRssServiceEntryPoint::version() const { - return APP_VERSION; -} - -QString TtRssServiceEntryPoint::author() const { - return APP_AUTHOR; -} - -QIcon TtRssServiceEntryPoint::icon() const { - return qApp->icons()->miscIcon(QSL("tt-rss")); -} - -QString TtRssServiceEntryPoint::code() const { - return SERVICE_CODE_TT_RSS; -} - -ServiceRoot *TtRssServiceEntryPoint::createNewRoot() const { - QScopedPointer form_acc(new FormEditTtRssAccount(qApp->mainFormWidget())); - return form_acc->execForCreate(); -} - -QList TtRssServiceEntryPoint::initializeSubtree() const { - // Check DB if standard account is enabled. - QSqlDatabase database = qApp->database()->connection(QSL("TtRssServiceEntryPoint"), DatabaseFactory::FromSettings); - - return DatabaseQueries::getTtRssAccounts(database); -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/tt-rss/ttrssserviceentrypoint.h" + +#include "definitions/definitions.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/databasequeries.h" +#include "services/tt-rss/definitions.h" +#include "services/tt-rss/ttrssserviceroot.h" +#include "services/tt-rss/gui/formeditttrssaccount.h" + +#include + + +TtRssServiceEntryPoint::TtRssServiceEntryPoint() { +} + + +TtRssServiceEntryPoint::~TtRssServiceEntryPoint() { +} + +bool TtRssServiceEntryPoint::isSingleInstanceService() const { + return false; +} + +QString TtRssServiceEntryPoint::name() const { + return QSL("Tiny Tiny RSS"); +} + +QString TtRssServiceEntryPoint::description() const { + return QObject::tr("This service offers integration with Tiny Tiny RSS.\n\n" + "Tiny Tiny RSS is an open source web-based news feed (RSS/Atom) reader and aggregator, " + "designed to allow you to read news from any location, while feeling as close to a real " + "desktop application as possible.\n\nAt least API level %1 is required.").arg(MINIMAL_API_LEVEL); +} + +QString TtRssServiceEntryPoint::version() const { + return APP_VERSION; +} + +QString TtRssServiceEntryPoint::author() const { + return APP_AUTHOR; +} + +QIcon TtRssServiceEntryPoint::icon() const { + return qApp->icons()->miscIcon(QSL("tt-rss")); +} + +QString TtRssServiceEntryPoint::code() const { + return SERVICE_CODE_TT_RSS; +} + +ServiceRoot* TtRssServiceEntryPoint::createNewRoot() const { + QScopedPointer form_acc(new FormEditTtRssAccount(qApp->mainFormWidget())); + return form_acc->execForCreate(); +} + +QList TtRssServiceEntryPoint::initializeSubtree() const { + // Check DB if standard account is enabled. + QSqlDatabase database = qApp->database()->connection(QSL("TtRssServiceEntryPoint"), DatabaseFactory::FromSettings); + return DatabaseQueries::getTtRssAccounts(database); +} diff --git a/src/services/tt-rss/ttrssserviceentrypoint.h b/src/services/tt-rss/ttrssserviceentrypoint.h index 0a4819e35..ebe3ec0fa 100755 --- a/src/services/tt-rss/ttrssserviceentrypoint.h +++ b/src/services/tt-rss/ttrssserviceentrypoint.h @@ -1,42 +1,42 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - - -#ifndef TTRSSSERVICEENTRYPOINT_H -#define TTRSSSERVICEENTRYPOINT_H - -#include "services/abstract/serviceentrypoint.h" - - -class TtRssServiceEntryPoint : public ServiceEntryPoint { - public: - explicit TtRssServiceEntryPoint(); - virtual ~TtRssServiceEntryPoint(); - - bool isSingleInstanceService() const; - QString name() const; - QString description() const; - QString version() const; - QString author() const; - QIcon icon() const; - QString code() const; - - ServiceRoot *createNewRoot() const; - QList initializeSubtree() const; -}; - -#endif // TTRSSSERVICEENTRYPOINT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + + +#ifndef TTRSSSERVICEENTRYPOINT_H +#define TTRSSSERVICEENTRYPOINT_H + +#include "services/abstract/serviceentrypoint.h" + + +class TtRssServiceEntryPoint : public ServiceEntryPoint { + public: + explicit TtRssServiceEntryPoint(); + virtual ~TtRssServiceEntryPoint(); + + bool isSingleInstanceService() const; + QString name() const; + QString description() const; + QString version() const; + QString author() const; + QIcon icon() const; + QString code() const; + + ServiceRoot* createNewRoot() const; + QList initializeSubtree() const; +}; + +#endif // TTRSSSERVICEENTRYPOINT_H diff --git a/src/services/tt-rss/ttrssserviceroot.cpp b/src/services/tt-rss/ttrssserviceroot.cpp index c1b90cb6d..e14e755ac 100755 --- a/src/services/tt-rss/ttrssserviceroot.cpp +++ b/src/services/tt-rss/ttrssserviceroot.cpp @@ -1,338 +1,331 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#include "services/tt-rss/ttrssserviceroot.h" - -#include "miscellaneous/application.h" -#include "miscellaneous/settings.h" -#include "miscellaneous/mutex.h" -#include "miscellaneous/textfactory.h" -#include "miscellaneous/databasequeries.h" -#include "network-web/networkfactory.h" -#include "miscellaneous/iconfactory.h" -#include "services/tt-rss/ttrssserviceentrypoint.h" -#include "services/tt-rss/ttrssfeed.h" -#include "services/tt-rss/ttrssrecyclebin.h" -#include "services/tt-rss/ttrsscategory.h" -#include "services/tt-rss/definitions.h" -#include "services/tt-rss/network/ttrssnetworkfactory.h" -#include "services/tt-rss/gui/formeditttrssaccount.h" -#include "services/tt-rss/gui/formttrssfeeddetails.h" - -#include -#include -#include - - -TtRssServiceRoot::TtRssServiceRoot(RootItem *parent) - : ServiceRoot(parent), CacheForServiceRoot(), m_recycleBin(new TtRssRecycleBin(this)), - m_actionSyncIn(nullptr), m_serviceMenu(QList()), m_network(new TtRssNetworkFactory()) { - setIcon(TtRssServiceEntryPoint().icon()); -} - -TtRssServiceRoot::~TtRssServiceRoot() { - delete m_network; -} - -void TtRssServiceRoot::start(bool freshly_activated) { - Q_UNUSED(freshly_activated) - - loadFromDatabase(); - - if (qApp->isFirstRun(QSL("3.1.1")) || (childCount() == 1 && child(0)->kind() == RootItemKind::Bin)) { - syncIn(); - } -} - -void TtRssServiceRoot::stop() { - m_network->logout(); - qDebug("Stopping Tiny Tiny RSS account, logging out with result '%d'.", (int) m_network->lastError()); -} - -QString TtRssServiceRoot::code() const { - return TtRssServiceEntryPoint().code(); -} - -bool TtRssServiceRoot::editViaGui() { - QScopedPointer form_pointer(new FormEditTtRssAccount(qApp->mainFormWidget())); - form_pointer.data()->execForEdit(this); - - return true; -} - -bool TtRssServiceRoot::deleteViaGui() { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - // Remove extra entry in "Tiny Tiny RSS accounts list" and then delete - // all the categories/feeds and messages. - if (DatabaseQueries::deleteTtRssAccount(database, accountId())) { - return ServiceRoot::deleteViaGui(); - } - else { - return false; - } -} - -bool TtRssServiceRoot::markAsReadUnread(RootItem::ReadStatus status) { - addMessageStatesToCache(customIDSOfMessagesForItem(this), status); - return ServiceRoot::markAsReadUnread(status); -} - -bool TtRssServiceRoot::supportsFeedAdding() const { - return true; -} - -bool TtRssServiceRoot::supportsCategoryAdding() const { - return false; -} - -void TtRssServiceRoot::addNewFeed(const QString &url) { - if (!qApp->feedUpdateLock()->tryLock()) { - // Lock was not obtained because - // it is used probably by feed updater or application - // is quitting. - qApp->showGuiMessage(tr("Cannot add item"), - tr("Cannot add feed because another critical operation is ongoing."), - QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); - // Thus, cannot delete and quit the method. - return; - } - - QScopedPointer form_pointer(new FormTtRssFeedDetails(this, qApp->mainFormWidget())); - - form_pointer.data()->addEditFeed(nullptr, this, url); - qApp->feedUpdateLock()->unlock(); -} - -void TtRssServiceRoot::addNewCategory() { - // NOTE: Do nothing. -} - -bool TtRssServiceRoot::canBeEdited() const { - return true; -} - -bool TtRssServiceRoot::canBeDeleted() const { - return true; -} - -QVariant TtRssServiceRoot::data(int column, int role) const { - switch (role) { - case Qt::ToolTipRole: - if (column == FDS_MODEL_TITLE_INDEX) { - return tr("Tiny Tiny RSS\n\nAccount ID: %3\nUsername: %1\nServer: %2\n" - "Last error: %4\nLast login on: %5").arg(m_network->username(), - m_network->url(), - QString::number(accountId()), - NetworkFactory::networkErrorText(m_network->lastError()), - m_network->lastLoginTime().isValid() ? - m_network->lastLoginTime().toString(Qt::DefaultLocaleShortDate) : - QSL("-")); - } - else { - return ServiceRoot::data(column, role); - } - - default: - return ServiceRoot::data(column, role); - } -} - -RecycleBin *TtRssServiceRoot::recycleBin() const { - return m_recycleBin; -} - -void TtRssServiceRoot::saveAllCachedData() { - QPair, QMap>> msgCache = takeMessageCache(); - QMapIterator i(msgCache.first); - - // Save the actual data read/unread. - while (i.hasNext()) { - i.next(); - auto key = i.key(); - QStringList ids = i.value(); - - if (!ids.isEmpty()) { - network()->updateArticles(ids, - UpdateArticle::Unread, - key == RootItem::Unread ? UpdateArticle::SetToTrue : UpdateArticle::SetToFalse); - } - } - - QMapIterator> j(msgCache.second); - - // Save the actual data important/not important. - while (j.hasNext()) { - j.next(); - auto key = j.key(); - QList messages = j.value(); - - if (!messages.isEmpty()) { - QStringList ids = customIDsOfMessages(messages); - network()->updateArticles(ids, - UpdateArticle::Starred, - key == RootItem::Important ? UpdateArticle::SetToTrue : UpdateArticle::SetToFalse); - } - } -} - -QList TtRssServiceRoot::serviceMenu() { - if (m_serviceMenu.isEmpty()) { - m_actionSyncIn = new QAction(qApp->icons()->fromTheme(QSL("view-refresh")), tr("Sync in"), this); - - connect(m_actionSyncIn, &QAction::triggered, this, &TtRssServiceRoot::syncIn); - m_serviceMenu.append(m_actionSyncIn); - } - - return m_serviceMenu; -} - -bool TtRssServiceRoot::onBeforeSetMessagesRead(RootItem *selected_item, const QList &messages, RootItem::ReadStatus read) { - Q_UNUSED(selected_item) - - addMessageStatesToCache(customIDsOfMessages(messages), read); - return true; -} - -bool TtRssServiceRoot::onBeforeSwitchMessageImportance(RootItem *selected_item, const QList &changes) { - Q_UNUSED(selected_item) - - // Now, we need to separate the changes because of ownCloud API limitations. - QList mark_starred_msgs; - QList mark_unstarred_msgs; - - foreach (const ImportanceChange &pair, changes) { - if (pair.second == RootItem::Important) { - mark_starred_msgs.append(pair.first); - } - else { - mark_unstarred_msgs.append(pair.first); - } - } - - if (!mark_starred_msgs.isEmpty()) { - addMessageStatesToCache(mark_starred_msgs, RootItem::Important); - } - - if (!mark_unstarred_msgs.isEmpty()) { - addMessageStatesToCache(mark_unstarred_msgs, RootItem::NotImportant); - } - - return true; -} - -TtRssNetworkFactory *TtRssServiceRoot::network() const { - return m_network; -} - -void TtRssServiceRoot::saveAccountDataToDatabase() { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - - if (accountId() != NO_PARENT_CATEGORY) { - // We are overwritting previously saved data. - if (DatabaseQueries::overwriteTtRssAccount(database, m_network->username(), m_network->password(), - m_network->authIsUsed(), m_network->authUsername(), - m_network->authPassword(), m_network->url(), - m_network->forceServerSideUpdate(), accountId())) { - updateTitle(); - itemChanged(QList() << this); - } - } - else { - bool saved; - int id_to_assign = DatabaseQueries::createAccount(database, code(), &saved); - - if (saved) { - if (DatabaseQueries::createTtRssAccount(database, id_to_assign, m_network->username(), - m_network->password(), m_network->authIsUsed(), - m_network->authUsername(), m_network->authPassword(), - m_network->url(), m_network->forceServerSideUpdate())) { - setId(id_to_assign); - setAccountId(id_to_assign); - updateTitle(); - } - } - } -} - -void TtRssServiceRoot::loadFromDatabase() { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - Assignment categories = DatabaseQueries::getTtRssCategories(database, accountId()); - Assignment feeds = DatabaseQueries::getTtRssFeeds(database, accountId()); - - // All data are now obtained, lets create the hierarchy. - assembleCategories(categories); - assembleFeeds(feeds); - - // As the last item, add recycle bin, which is needed. - appendChild(m_recycleBin); - updateCounts(true); -} - -void TtRssServiceRoot::updateTitle() { - QString host = QUrl(m_network->url()).host(); - - if (host.isEmpty()) { - host = m_network->url(); - } - - setTitle(m_network->username() + QL1S("@") + host + QSL(" (Tiny Tiny RSS)")); -} - -RootItem *TtRssServiceRoot::obtainNewTreeForSyncIn() const { - TtRssGetFeedsCategoriesResponse feed_cats_response = m_network->getFeedsCategories(); - - if (m_network->lastError() == QNetworkReply::NoError) { - return feed_cats_response.feedsCategories(true, m_network->url()); - } - else { - return nullptr; - } -} - -QMap TtRssServiceRoot::storeCustomFeedsData() { - QMap custom_data; - - foreach (const Feed *feed, getSubTreeFeeds()) { - QVariantMap feed_custom_data; - - feed_custom_data.insert(QSL("auto_update_interval"), feed->autoUpdateInitialInterval()); - feed_custom_data.insert(QSL("auto_update_type"), feed->autoUpdateType()); - - custom_data.insert(feed->customId(), feed_custom_data); - } - - return custom_data; -} - -void TtRssServiceRoot::restoreCustomFeedsData(const QMap &data, const QHash &feeds) { - QMapIterator i(data); - - while (i.hasNext()) { - i.next(); - - const int custom_id = i.key(); - - if (feeds.contains(custom_id)) { - Feed *feed = feeds.value(custom_id); - QVariantMap feed_custom_data = i.value().toMap(); - - feed->setAutoUpdateInitialInterval(feed_custom_data.value(QSL("auto_update_interval")).toInt()); - feed->setAutoUpdateType(static_cast(feed_custom_data.value(QSL("auto_update_type")).toInt())); - } - } -} +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/tt-rss/ttrssserviceroot.h" + +#include "miscellaneous/application.h" +#include "miscellaneous/settings.h" +#include "miscellaneous/mutex.h" +#include "miscellaneous/textfactory.h" +#include "miscellaneous/databasequeries.h" +#include "network-web/networkfactory.h" +#include "miscellaneous/iconfactory.h" +#include "services/tt-rss/ttrssserviceentrypoint.h" +#include "services/tt-rss/ttrssfeed.h" +#include "services/tt-rss/ttrssrecyclebin.h" +#include "services/tt-rss/ttrsscategory.h" +#include "services/tt-rss/definitions.h" +#include "services/tt-rss/network/ttrssnetworkfactory.h" +#include "services/tt-rss/gui/formeditttrssaccount.h" +#include "services/tt-rss/gui/formttrssfeeddetails.h" + +#include +#include +#include + + +TtRssServiceRoot::TtRssServiceRoot(RootItem* parent) + : ServiceRoot(parent), CacheForServiceRoot(), m_recycleBin(new TtRssRecycleBin(this)), + m_actionSyncIn(nullptr), m_serviceMenu(QList()), m_network(new TtRssNetworkFactory()) { + setIcon(TtRssServiceEntryPoint().icon()); +} + +TtRssServiceRoot::~TtRssServiceRoot() { + delete m_network; +} + +void TtRssServiceRoot::start(bool freshly_activated) { + Q_UNUSED(freshly_activated) + loadFromDatabase(); + + if (qApp->isFirstRun(QSL("3.1.1")) || (childCount() == 1 && child(0)->kind() == RootItemKind::Bin)) { + syncIn(); + } +} + +void TtRssServiceRoot::stop() { + m_network->logout(); + qDebug("Stopping Tiny Tiny RSS account, logging out with result '%d'.", (int) m_network->lastError()); +} + +QString TtRssServiceRoot::code() const { + return TtRssServiceEntryPoint().code(); +} + +bool TtRssServiceRoot::editViaGui() { + QScopedPointer form_pointer(new FormEditTtRssAccount(qApp->mainFormWidget())); + form_pointer.data()->execForEdit(this); + return true; +} + +bool TtRssServiceRoot::deleteViaGui() { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + + // Remove extra entry in "Tiny Tiny RSS accounts list" and then delete + // all the categories/feeds and messages. + if (DatabaseQueries::deleteTtRssAccount(database, accountId())) { + return ServiceRoot::deleteViaGui(); + } + + else { + return false; + } +} + +bool TtRssServiceRoot::markAsReadUnread(RootItem::ReadStatus status) { + addMessageStatesToCache(customIDSOfMessagesForItem(this), status); + return ServiceRoot::markAsReadUnread(status); +} + +bool TtRssServiceRoot::supportsFeedAdding() const { + return true; +} + +bool TtRssServiceRoot::supportsCategoryAdding() const { + return false; +} + +void TtRssServiceRoot::addNewFeed(const QString& url) { + if (!qApp->feedUpdateLock()->tryLock()) { + // Lock was not obtained because + // it is used probably by feed updater or application + // is quitting. + qApp->showGuiMessage(tr("Cannot add item"), + tr("Cannot add feed because another critical operation is ongoing."), + QSystemTrayIcon::Warning, qApp->mainFormWidget(), true); + // Thus, cannot delete and quit the method. + return; + } + + QScopedPointer form_pointer(new FormTtRssFeedDetails(this, qApp->mainFormWidget())); + form_pointer.data()->addEditFeed(nullptr, this, url); + qApp->feedUpdateLock()->unlock(); +} + +void TtRssServiceRoot::addNewCategory() { + // NOTE: Do nothing. +} + +bool TtRssServiceRoot::canBeEdited() const { + return true; +} + +bool TtRssServiceRoot::canBeDeleted() const { + return true; +} + +QVariant TtRssServiceRoot::data(int column, int role) const { + switch (role) { + case Qt::ToolTipRole: + if (column == FDS_MODEL_TITLE_INDEX) { + return tr("Tiny Tiny RSS\n\nAccount ID: %3\nUsername: %1\nServer: %2\n" + "Last error: %4\nLast login on: %5").arg(m_network->username(), + m_network->url(), + QString::number(accountId()), + NetworkFactory::networkErrorText(m_network->lastError()), + m_network->lastLoginTime().isValid() ? + m_network->lastLoginTime().toString(Qt::DefaultLocaleShortDate) : + QSL("-")); + } + + else { + return ServiceRoot::data(column, role); + } + + default: + return ServiceRoot::data(column, role); + } +} + +RecycleBin* TtRssServiceRoot::recycleBin() const { + return m_recycleBin; +} + +void TtRssServiceRoot::saveAllCachedData() { + QPair, QMap>> msgCache = takeMessageCache(); + QMapIterator i(msgCache.first); + + // Save the actual data read/unread. + while (i.hasNext()) { + i.next(); + auto key = i.key(); + QStringList ids = i.value(); + + if (!ids.isEmpty()) { + network()->updateArticles(ids, + UpdateArticle::Unread, + key == RootItem::Unread ? UpdateArticle::SetToTrue : UpdateArticle::SetToFalse); + } + } + + QMapIterator> j(msgCache.second); + + // Save the actual data important/not important. + while (j.hasNext()) { + j.next(); + auto key = j.key(); + QList messages = j.value(); + + if (!messages.isEmpty()) { + QStringList ids = customIDsOfMessages(messages); + network()->updateArticles(ids, + UpdateArticle::Starred, + key == RootItem::Important ? UpdateArticle::SetToTrue : UpdateArticle::SetToFalse); + } + } +} + +QList TtRssServiceRoot::serviceMenu() { + if (m_serviceMenu.isEmpty()) { + m_actionSyncIn = new QAction(qApp->icons()->fromTheme(QSL("view-refresh")), tr("Sync in"), this); + connect(m_actionSyncIn, &QAction::triggered, this, &TtRssServiceRoot::syncIn); + m_serviceMenu.append(m_actionSyncIn); + } + + return m_serviceMenu; +} + +bool TtRssServiceRoot::onBeforeSetMessagesRead(RootItem* selected_item, const QList& messages, RootItem::ReadStatus read) { + Q_UNUSED(selected_item) + addMessageStatesToCache(customIDsOfMessages(messages), read); + return true; +} + +bool TtRssServiceRoot::onBeforeSwitchMessageImportance(RootItem* selected_item, const QList& changes) { + Q_UNUSED(selected_item) + // Now, we need to separate the changes because of ownCloud API limitations. + QList mark_starred_msgs; + QList mark_unstarred_msgs; + + foreach (const ImportanceChange& pair, changes) { + if (pair.second == RootItem::Important) { + mark_starred_msgs.append(pair.first); + } + + else { + mark_unstarred_msgs.append(pair.first); + } + } + + if (!mark_starred_msgs.isEmpty()) { + addMessageStatesToCache(mark_starred_msgs, RootItem::Important); + } + + if (!mark_unstarred_msgs.isEmpty()) { + addMessageStatesToCache(mark_unstarred_msgs, RootItem::NotImportant); + } + + return true; +} + +TtRssNetworkFactory* TtRssServiceRoot::network() const { + return m_network; +} + +void TtRssServiceRoot::saveAccountDataToDatabase() { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + + if (accountId() != NO_PARENT_CATEGORY) { + // We are overwritting previously saved data. + if (DatabaseQueries::overwriteTtRssAccount(database, m_network->username(), m_network->password(), + m_network->authIsUsed(), m_network->authUsername(), + m_network->authPassword(), m_network->url(), + m_network->forceServerSideUpdate(), accountId())) { + updateTitle(); + itemChanged(QList() << this); + } + } + + else { + bool saved; + int id_to_assign = DatabaseQueries::createAccount(database, code(), &saved); + + if (saved) { + if (DatabaseQueries::createTtRssAccount(database, id_to_assign, m_network->username(), + m_network->password(), m_network->authIsUsed(), + m_network->authUsername(), m_network->authPassword(), + m_network->url(), m_network->forceServerSideUpdate())) { + setId(id_to_assign); + setAccountId(id_to_assign); + updateTitle(); + } + } + } +} + +void TtRssServiceRoot::loadFromDatabase() { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + Assignment categories = DatabaseQueries::getTtRssCategories(database, accountId()); + Assignment feeds = DatabaseQueries::getTtRssFeeds(database, accountId()); + // All data are now obtained, lets create the hierarchy. + assembleCategories(categories); + assembleFeeds(feeds); + // As the last item, add recycle bin, which is needed. + appendChild(m_recycleBin); + updateCounts(true); +} + +void TtRssServiceRoot::updateTitle() { + QString host = QUrl(m_network->url()).host(); + + if (host.isEmpty()) { + host = m_network->url(); + } + + setTitle(m_network->username() + QL1S("@") + host + QSL(" (Tiny Tiny RSS)")); +} + +RootItem* TtRssServiceRoot::obtainNewTreeForSyncIn() const { + TtRssGetFeedsCategoriesResponse feed_cats_response = m_network->getFeedsCategories(); + + if (m_network->lastError() == QNetworkReply::NoError) { + return feed_cats_response.feedsCategories(true, m_network->url()); + } + + else { + return nullptr; + } +} + +QMap TtRssServiceRoot::storeCustomFeedsData() { + QMap custom_data; + + foreach (const Feed* feed, getSubTreeFeeds()) { + QVariantMap feed_custom_data; + feed_custom_data.insert(QSL("auto_update_interval"), feed->autoUpdateInitialInterval()); + feed_custom_data.insert(QSL("auto_update_type"), feed->autoUpdateType()); + custom_data.insert(feed->customId(), feed_custom_data); + } + + return custom_data; +} + +void TtRssServiceRoot::restoreCustomFeedsData(const QMap& data, const QHash& feeds) { + QMapIterator i(data); + + while (i.hasNext()) { + i.next(); + const int custom_id = i.key(); + + if (feeds.contains(custom_id)) { + Feed* feed = feeds.value(custom_id); + QVariantMap feed_custom_data = i.value().toMap(); + feed->setAutoUpdateInitialInterval(feed_custom_data.value(QSL("auto_update_interval")).toInt()); + feed->setAutoUpdateType(static_cast(feed_custom_data.value(QSL("auto_update_type")).toInt())); + } + } +} diff --git a/src/services/tt-rss/ttrssserviceroot.h b/src/services/tt-rss/ttrssserviceroot.h index 8fdf0baaa..6a8de0c5e 100755 --- a/src/services/tt-rss/ttrssserviceroot.h +++ b/src/services/tt-rss/ttrssserviceroot.h @@ -1,80 +1,80 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2017 by Martin Rotter -// -// RSS Guard is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// RSS Guard is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with RSS Guard. If not, see . - -#ifndef TTRSSSERVICEROOT_H -#define TTRSSSERVICEROOT_H - -#include "services/abstract/serviceroot.h" -#include "services/abstract/cacheforserviceroot.h" - -#include - - -class TtRssCategory; -class TtRssFeed; -class TtRssNetworkFactory; -class TtRssRecycleBin; - -class TtRssServiceRoot : public ServiceRoot, public CacheForServiceRoot { - Q_OBJECT - - public: - explicit TtRssServiceRoot(RootItem *parent = nullptr); - virtual ~TtRssServiceRoot(); - - void start(bool freshly_activated); - void stop(); - QString code() const; - bool canBeEdited() const; - bool canBeDeleted() const; - bool editViaGui(); - bool deleteViaGui(); - bool markAsReadUnread(ReadStatus status); - bool supportsFeedAdding() const; - bool supportsCategoryAdding() const; - QVariant data(int column, int role) const; - QList serviceMenu(); - RecycleBin *recycleBin() const; - void saveAllCachedData(); - - bool onBeforeSetMessagesRead(RootItem *selected_item, const QList &messages, ReadStatus read); - bool onBeforeSwitchMessageImportance(RootItem *selected_item, const QList &changes); - - // Access to network. - TtRssNetworkFactory *network() const; - - void saveAccountDataToDatabase(); - void updateTitle(); - - public slots: - void addNewFeed(const QString &url = QString()); - void addNewCategory(); - - private: - RootItem *obtainNewTreeForSyncIn() const; - QMap storeCustomFeedsData(); - void restoreCustomFeedsData(const QMap &data, const QHash &feeds); - - void loadFromDatabase(); - - TtRssRecycleBin *m_recycleBin; - QAction *m_actionSyncIn; - QList m_serviceMenu; - TtRssNetworkFactory *m_network; -}; - -#endif // TTRSSSERVICEROOT_H +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2017 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef TTRSSSERVICEROOT_H +#define TTRSSSERVICEROOT_H + +#include "services/abstract/serviceroot.h" +#include "services/abstract/cacheforserviceroot.h" + +#include + + +class TtRssCategory; +class TtRssFeed; +class TtRssNetworkFactory; +class TtRssRecycleBin; + +class TtRssServiceRoot : public ServiceRoot, public CacheForServiceRoot { + Q_OBJECT + + public: + explicit TtRssServiceRoot(RootItem* parent = nullptr); + virtual ~TtRssServiceRoot(); + + void start(bool freshly_activated); + void stop(); + QString code() const; + bool canBeEdited() const; + bool canBeDeleted() const; + bool editViaGui(); + bool deleteViaGui(); + bool markAsReadUnread(ReadStatus status); + bool supportsFeedAdding() const; + bool supportsCategoryAdding() const; + QVariant data(int column, int role) const; + QList serviceMenu(); + RecycleBin* recycleBin() const; + void saveAllCachedData(); + + bool onBeforeSetMessagesRead(RootItem* selected_item, const QList& messages, ReadStatus read); + bool onBeforeSwitchMessageImportance(RootItem* selected_item, const QList& changes); + + // Access to network. + TtRssNetworkFactory* network() const; + + void saveAccountDataToDatabase(); + void updateTitle(); + + public slots: + void addNewFeed(const QString& url = QString()); + void addNewCategory(); + + private: + RootItem* obtainNewTreeForSyncIn() const; + QMap storeCustomFeedsData(); + void restoreCustomFeedsData(const QMap& data, const QHash& feeds); + + void loadFromDatabase(); + + TtRssRecycleBin* m_recycleBin; + QAction* m_actionSyncIn; + QList m_serviceMenu; + TtRssNetworkFactory* m_network; +}; + +#endif // TTRSSSERVICEROOT_H