File indexing completed on 2024-05-05 09:27:15
0001 /* 0002 This file is part of Akregator. 0003 0004 SPDX-FileCopyrightText: 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> 0005 SPDX-FileCopyrightText: 2004 Sashmit Bhaduri <smt@vfemail.net> 0006 SPDX-FileCopyrightText: 2005 Frank Osterfeld <osterfeld@kde.org> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0 0009 */ 0010 0011 #include "mainwidget.h" 0012 #include "abstractselectioncontroller.h" 0013 #include "actionmanagerimpl.h" 0014 #include "akregator_part.h" 0015 #include "akregatorconfig.h" 0016 #include "articlejobs.h" 0017 #include "articlelistview.h" 0018 #include "articleviewerwidget.h" 0019 #include "createfeedcommand.h" 0020 #include "createfoldercommand.h" 0021 #include "deletesubscriptioncommand.h" 0022 #include "editsubscriptioncommand.h" 0023 #include "expireitemscommand.h" 0024 #include "importfeedlistcommand.h" 0025 #include "utils.h" 0026 #include <PimCommon/BroadcastStatus> 0027 0028 #include "feedlist.h" 0029 #include "fetchqueue.h" 0030 #include "folder.h" 0031 #include "framemanager.h" 0032 #include "job/downloadarticlejob.h" 0033 #include "kernel.h" 0034 #include "mainframe.h" 0035 #include "openurlrequest.h" 0036 #include "progressmanager.h" 0037 #include "selectioncontroller.h" 0038 #include "subscriptionlistmodel.h" 0039 #include "subscriptionlistview.h" 0040 #include "tabwidget.h" 0041 #include "treenode.h" 0042 #include "types.h" 0043 #include "widgets/searchbar.h" 0044 #include <WebEngineViewer/ZoomActionMenu> 0045 0046 #include <KAboutData> 0047 #include <KLocalizedString> 0048 #include <KMessageBox> 0049 #include <KShell> 0050 0051 #include <KToggleAction> 0052 0053 #include <QApplication> 0054 #include <QClipboard> 0055 #include <QDesktopServices> 0056 #include <QDomDocument> 0057 #include <QSplitter> 0058 #include <QTimer> 0059 #include <QUrlQuery> 0060 #include <QVBoxLayout> 0061 0062 #include <PimCommon/NeedUpdateVersionUtils> 0063 #include <PimCommon/NeedUpdateVersionWidget> 0064 #include <PimCommon/NetworkManager> 0065 #include <algorithm> 0066 #include <chrono> 0067 #include <memory> 0068 #include <webengine/webengineframe.h> 0069 0070 using namespace std::chrono_literals; 0071 0072 using namespace Akregator; 0073 0074 MainWidget::~MainWidget() 0075 { 0076 // if m_shuttingDown is false, slotOnShutdown was not called. That 0077 // means that not the whole app is shutdown, only the part. So it 0078 // should be no risk to do the cleanups now 0079 if (!m_shuttingDown) { 0080 slotOnShutdown(); 0081 } 0082 } 0083 0084 MainWidget::MainWidget(Part *part, QWidget *parent, ActionManagerImpl *actionManager, const QString &name) 0085 : QWidget(parent) 0086 , m_feedList() 0087 , m_actionManager(actionManager) 0088 , m_feedListManagementInterface(new FeedListManagementImpl) 0089 { 0090 setObjectName(name); 0091 0092 FeedListManagementInterface::setInstance(m_feedListManagementInterface); 0093 0094 m_actionManager->initMainWidget(this); 0095 m_part = part; 0096 m_shuttingDown = false; 0097 m_displayingAboutPage = false; 0098 setFocusPolicy(Qt::StrongFocus); 0099 0100 connect(m_part, &Part::signalSettingsChanged, m_actionManager, &ActionManagerImpl::slotSettingsChanged); 0101 0102 auto topLayout = new QVBoxLayout(this); 0103 topLayout->setContentsMargins({}); 0104 topLayout->setSpacing(0); 0105 if (PimCommon::NeedUpdateVersionUtils::checkVersion()) { 0106 const auto status = PimCommon::NeedUpdateVersionUtils::obsoleteVersionStatus(KAboutData::applicationData().version(), QDate::currentDate()); 0107 if (status != PimCommon::NeedUpdateVersionUtils::ObsoleteVersion::NotObsoleteYet) { 0108 auto needUpdateVersionWidget = new PimCommon::NeedUpdateVersionWidget(this); 0109 topLayout->addWidget(needUpdateVersionWidget); 0110 needUpdateVersionWidget->setObsoleteVersion(status); 0111 } 0112 } 0113 0114 m_horizontalSplitter = new QSplitter(Qt::Horizontal, this); 0115 0116 m_horizontalSplitter->setOpaqueResize(true); 0117 m_horizontalSplitter->setChildrenCollapsible(false); 0118 topLayout->addWidget(m_horizontalSplitter); 0119 0120 connect(Kernel::self()->fetchQueue(), &FetchQueue::signalStarted, this, &MainWidget::slotFetchingStarted); 0121 connect(Kernel::self()->fetchQueue(), &FetchQueue::signalStopped, this, &MainWidget::slotFetchingStopped); 0122 0123 m_feedListView = new SubscriptionListView(m_horizontalSplitter); 0124 m_feedListView->setObjectName(QLatin1StringView("feedtree")); 0125 m_actionManager->initSubscriptionListView(m_feedListView); 0126 0127 connect(m_feedListView, &SubscriptionListView::userActionTakingPlace, this, &MainWidget::ensureArticleTabVisible); 0128 0129 m_tabWidget = new TabWidget(m_horizontalSplitter); 0130 m_actionManager->initTabWidget(m_tabWidget); 0131 0132 connect(m_part, &Part::signalSettingsChanged, this, &MainWidget::slotSettingsChanged); 0133 0134 connect(m_tabWidget, &TabWidget::signalCurrentFrameChanged, this, &MainWidget::slotCurrentFrameChanged); 0135 0136 connect(m_tabWidget, &TabWidget::signalRemoveFrameRequest, Kernel::self()->frameManager(), &FrameManager::slotRemoveFrame); 0137 0138 connect(m_tabWidget, 0139 SIGNAL(signalOpenUrlRequest(Akregator::OpenUrlRequest &)), 0140 Kernel::self()->frameManager(), 0141 SLOT(slotOpenUrlRequest(Akregator::OpenUrlRequest &))); 0142 0143 connect(Kernel::self()->frameManager(), &FrameManager::signalFrameAdded, m_tabWidget, &TabWidget::slotAddFrame); 0144 0145 connect(Kernel::self()->frameManager(), &FrameManager::signalSelectFrame, m_tabWidget, &TabWidget::slotSelectFrame); 0146 0147 connect(Kernel::self()->frameManager(), &FrameManager::signalFrameRemoved, m_tabWidget, &TabWidget::slotRemoveFrame); 0148 0149 connect(Kernel::self()->frameManager(), &FrameManager::signalRequestNewFrame, this, &MainWidget::slotRequestNewFrame); 0150 0151 connect(Kernel::self()->frameManager(), &FrameManager::signalFrameRemoved, this, &MainWidget::slotFramesChanged); 0152 connect(Kernel::self()->frameManager(), &FrameManager::signalCompleted, this, &MainWidget::slotFramesChanged); 0153 0154 connect(PimCommon::NetworkManager::self(), &PimCommon::NetworkManager::networkStatusChanged, this, &MainWidget::slotNetworkStatusChanged); 0155 0156 m_tabWidget->setWhatsThis(i18n("You can view multiple articles in several open tabs.")); 0157 0158 m_mainTab = new QWidget(this); 0159 m_mainTab->setObjectName(QLatin1StringView("Article Tab")); 0160 m_mainTab->setWhatsThis(i18n("Articles list.")); 0161 0162 auto mainTabLayout = new QVBoxLayout(m_mainTab); 0163 mainTabLayout->setContentsMargins({}); 0164 0165 m_searchBar = new SearchBar(m_mainTab); 0166 if (!Settings::showQuickFilter()) { 0167 m_searchBar->hide(); 0168 } 0169 connect(m_searchBar, &SearchBar::forceLostFocus, this, &MainWidget::slotSetFocusToViewer); 0170 m_articleSplitter = new QSplitter(Qt::Vertical, m_mainTab); 0171 m_articleSplitter->setObjectName(QLatin1StringView("panner2")); 0172 m_articleSplitter->setChildrenCollapsible(false); 0173 0174 m_articleWidget = new QWidget(m_articleSplitter); 0175 auto articleWidgetLayout = new QVBoxLayout; 0176 m_articleWidget->setLayout(articleWidgetLayout); 0177 articleWidgetLayout->setContentsMargins({}); 0178 articleWidgetLayout->setSpacing(0); 0179 0180 m_articleListView = new ArticleListView; 0181 articleWidgetLayout->addWidget(m_searchBar); 0182 articleWidgetLayout->addWidget(m_articleListView); 0183 connect(m_articleListView, &ArticleListView::userActionTakingPlace, this, &MainWidget::ensureArticleTabVisible); 0184 0185 m_selectionController = new SelectionController(this); 0186 m_selectionController->setArticleLister(m_articleListView); 0187 m_selectionController->setFeedSelector(m_feedListView); 0188 0189 connect(m_searchBar, &SearchBar::signalSearch, m_selectionController, &AbstractSelectionController::setFilters); 0190 0191 connect(m_part, &Part::signalSettingsChanged, m_selectionController, &AbstractSelectionController::settingsChanged); 0192 0193 auto expansionHandler = new FolderExpansionHandler(this); 0194 connect(m_feedListView, &QTreeView::expanded, expansionHandler, &FolderExpansionHandler::itemExpanded); 0195 connect(m_feedListView, &QTreeView::collapsed, expansionHandler, &FolderExpansionHandler::itemCollapsed); 0196 0197 m_selectionController->setFolderExpansionHandler(expansionHandler); 0198 0199 connect(m_selectionController, &AbstractSelectionController::currentSubscriptionChanged, this, &MainWidget::slotNodeSelected); 0200 0201 connect(m_selectionController, &AbstractSelectionController::currentArticleChanged, this, &MainWidget::slotArticleSelected); 0202 0203 connect(m_selectionController, &AbstractSelectionController::articleDoubleClicked, this, &MainWidget::slotOpenArticleInBrowser); 0204 0205 m_actionManager->initArticleListView(m_articleListView); 0206 0207 connect(m_articleListView, &ArticleListView::signalMouseButtonPressed, this, &MainWidget::slotMouseButtonPressed); 0208 0209 m_articleViewer = new ArticleViewerWidget(Settings::grantleeDirectory(), m_actionManager->actionCollection(), m_articleSplitter); 0210 m_articleListView->setFocusProxy(m_articleViewer); 0211 setFocusProxy(m_articleViewer); 0212 0213 connect(m_articleViewer, &ArticleViewerWidget::showStatusBarMessage, this, &MainWidget::slotShowStatusBarMessage); 0214 connect(m_articleViewer, 0215 SIGNAL(signalOpenUrlRequest(Akregator::OpenUrlRequest &)), 0216 Kernel::self()->frameManager(), 0217 SLOT(slotOpenUrlRequest(Akregator::OpenUrlRequest &))); 0218 connect(m_searchBar, &SearchBar::signalSearch, m_articleViewer, &ArticleViewerWidget::setFilters); 0219 mainTabLayout->addWidget(m_articleSplitter); 0220 0221 m_mainFrame = new MainFrame(this, m_mainTab); 0222 m_mainFrame->slotSetTitle(i18n("Articles")); 0223 m_mainFrame->setArticleViewer(m_articleViewer); 0224 connect(m_articleViewer->articleViewerWidgetNg()->articleViewerNg(), &ArticleViewerWebEngine::articleAction, this, &MainWidget::slotArticleAction); 0225 connect(m_tabWidget, &TabWidget::signalCopyInFrame, m_mainFrame, &MainFrame::slotCopyInFrame); 0226 connect(m_tabWidget, &TabWidget::signalPrintInFrame, m_mainFrame, &MainFrame::slotPrintInFrame); 0227 connect(m_tabWidget, &TabWidget::signalZoomChangedInFrame, m_mainFrame, &MainFrame::slotZoomChangeInFrame); 0228 connect(m_tabWidget, &TabWidget::signalPrintPreviewInFrame, m_mainFrame, &MainFrame::slotPrintPreviewInFrame); 0229 connect(m_tabWidget, &TabWidget::signalFindTextInFrame, m_mainFrame, &MainFrame::slotFindTextInFrame); 0230 connect(m_tabWidget, &TabWidget::signalTextToSpeechInFrame, m_mainFrame, &MainFrame::slotTextToSpeechInFrame); 0231 connect(m_tabWidget, &TabWidget::signalSaveLinkAsInFrame, m_mainFrame, &MainFrame::slotSaveLinkAsInFrame); 0232 connect(m_tabWidget, &TabWidget::signalCopyLinkAsInFrame, m_mainFrame, &MainFrame::slotCopyLinkAsInFrame); 0233 connect(m_tabWidget, &TabWidget::signalCopyImageLocation, m_mainFrame, &MainFrame::slotCopyImageLocationInFrame); 0234 connect(m_tabWidget, &TabWidget::signalSaveImageOnDisk, m_mainFrame, &MainFrame::slotSaveImageOnDiskInFrame); 0235 connect(m_tabWidget, &TabWidget::signalMute, m_mainFrame, &MainFrame::slotMute); 0236 0237 Kernel::self()->frameManager()->slotAddFrame(m_mainFrame); 0238 0239 const QList<int> sp1sizes = Settings::splitter1Sizes(); 0240 if (sp1sizes.count() >= m_horizontalSplitter->count()) { 0241 m_horizontalSplitter->setSizes(sp1sizes); 0242 } 0243 const QList<int> sp2sizes = Settings::splitter2Sizes(); 0244 if (sp2sizes.count() >= m_articleSplitter->count()) { 0245 m_articleSplitter->setSizes(sp2sizes); 0246 } 0247 0248 if (!Settings::self()->disableIntroduction()) { 0249 m_articleWidget->hide(); 0250 m_articleViewer->displayAboutPage(); 0251 m_mainFrame->slotSetTitle(i18n("About")); 0252 m_displayingAboutPage = true; 0253 } 0254 0255 m_fetchTimer = new QTimer(this); 0256 connect(m_fetchTimer, &QTimer::timeout, this, &MainWidget::slotDoIntervalFetches); 0257 m_fetchTimer->start(1min); 0258 0259 // delete expired articles once per hour 0260 m_expiryTimer = new QTimer(this); 0261 connect(m_expiryTimer, &QTimer::timeout, this, &MainWidget::slotDeleteExpiredArticles); 0262 m_expiryTimer->start(1h); 0263 0264 m_markReadTimer = new QTimer(this); 0265 m_markReadTimer->setSingleShot(true); 0266 connect(m_markReadTimer, &QTimer::timeout, this, &MainWidget::slotSetCurrentArticleReadDelayed); 0267 0268 setFeedList(QSharedPointer<FeedList>(new FeedList(Kernel::self()->storage()))); 0269 0270 switch (Settings::viewMode()) { 0271 case CombinedView: 0272 slotCombinedView(); 0273 break; 0274 case WidescreenView: 0275 slotWidescreenView(); 0276 break; 0277 default: 0278 slotNormalView(); 0279 } 0280 0281 if (!Settings::resetQuickFilterOnNodeChange()) { 0282 m_searchBar->slotSetStatus(Settings::statusFilter()); 0283 m_searchBar->slotSetText(Settings::textFilter()); 0284 } 0285 } 0286 0287 void MainWidget::slotSettingsChanged() 0288 { 0289 m_tabWidget->slotSettingsChanged(); 0290 m_articleViewer->updateAfterConfigChanged(); 0291 } 0292 0293 void MainWidget::slotSetFocusToViewer() 0294 { 0295 m_articleViewer->setFocus(); 0296 } 0297 0298 void MainWidget::slotOnShutdown() 0299 { 0300 disconnect(m_tabWidget, &TabWidget::signalCurrentFrameChanged, this, &MainWidget::slotCurrentFrameChanged); 0301 0302 m_shuttingDown = true; 0303 0304 // close all pageviewers in a controlled way 0305 // fixes bug 91660, at least when no part loading data 0306 while (m_tabWidget->count() > 1) { // remove frames until only the main frame remains 0307 m_tabWidget->setCurrentIndex(m_tabWidget->count() - 1); // select last page 0308 m_tabWidget->slotRemoveCurrentFrame(); 0309 } 0310 0311 Kernel::self()->fetchQueue()->slotAbort(); 0312 setFeedList(QSharedPointer<FeedList>()); 0313 0314 delete m_feedListManagementInterface; 0315 delete m_feedListView; // call delete here, so that the header settings will get saved 0316 delete m_articleListView; // same for this one 0317 0318 delete m_mainTab; 0319 delete m_mainFrame; 0320 m_mainFrame = nullptr; 0321 0322 Settings::self()->save(); 0323 } 0324 0325 void MainWidget::saveSettings() 0326 { 0327 const QList<int> spl1 = m_horizontalSplitter->sizes(); 0328 if (std::count(spl1.begin(), spl1.end(), 0) == 0) { 0329 Settings::setSplitter1Sizes(spl1); 0330 } 0331 const QList<int> spl2 = m_articleSplitter->sizes(); 0332 if (std::count(spl2.begin(), spl2.end(), 0) == 0) { 0333 Settings::setSplitter2Sizes(spl2); 0334 } 0335 Settings::setViewMode(m_viewMode); 0336 Settings::self()->save(); 0337 } 0338 0339 void MainWidget::connectFrame(Akregator::WebEngineFrame *frame) 0340 { 0341 connect(m_tabWidget, &TabWidget::signalCopyInFrame, frame, &WebEngineFrame::slotCopyInFrame); 0342 connect(m_tabWidget, &TabWidget::signalPrintInFrame, frame, &WebEngineFrame::slotPrintInFrame); 0343 connect(m_tabWidget, &TabWidget::signalZoomChangedInFrame, frame, &WebEngineFrame::slotZoomChangeInFrame); 0344 connect(m_tabWidget, &TabWidget::signalPrintPreviewInFrame, frame, &WebEngineFrame::slotPrintPreviewInFrame); 0345 connect(m_tabWidget, &TabWidget::signalFindTextInFrame, frame, &WebEngineFrame::slotFindTextInFrame); 0346 connect(m_tabWidget, &TabWidget::signalTextToSpeechInFrame, frame, &WebEngineFrame::slotTextToSpeechInFrame); 0347 connect(m_tabWidget, &TabWidget::signalSaveLinkAsInFrame, frame, &WebEngineFrame::slotSaveLinkAsInFrame); 0348 connect(m_tabWidget, &TabWidget::signalCopyLinkAsInFrame, frame, &WebEngineFrame::slotCopyLinkAsInFrame); 0349 connect(m_tabWidget, &TabWidget::signalCopyImageLocation, frame, &WebEngineFrame::slotCopyImageLocationInFrame); 0350 connect(m_tabWidget, &TabWidget::signalSaveImageOnDisk, frame, &WebEngineFrame::slotSaveImageOnDiskInFrame); 0351 connect(m_tabWidget, &TabWidget::signalMute, frame, &WebEngineFrame::slotMute); 0352 0353 connect(frame, &WebEngineFrame::showStatusBarMessage, this, &MainWidget::slotShowStatusBarMessage); 0354 connect(frame, &WebEngineFrame::signalIconChanged, m_tabWidget, &TabWidget::slotSetIcon); 0355 connect(frame, &WebEngineFrame::webPageMutedOrAudibleChanged, m_tabWidget, &TabWidget::slotWebPageMutedOrAudibleChanged); 0356 } 0357 0358 void MainWidget::slotRequestNewFrame(int &frameId) 0359 { 0360 auto frame = new WebEngineFrame(m_actionManager->actionCollection(), m_tabWidget); 0361 connectFrame(frame); 0362 0363 Kernel::self()->frameManager()->slotAddFrame(frame); 0364 0365 frameId = frame->id(); 0366 } 0367 0368 void MainWidget::sendArticle(bool attach) 0369 { 0370 QByteArray text; 0371 QString title; 0372 0373 Frame *frame = Kernel::self()->frameManager()->currentFrame(); 0374 0375 if (frame && frame->id() > 0) { // are we in some other tab than the articlelist? 0376 text = frame->url().toString().toLatin1(); 0377 title = frame->title(); 0378 } else { // nah, we're in articlelist.. 0379 const Article article = m_selectionController->currentArticle(); 0380 if (!article.isNull()) { 0381 text = article.link().toDisplayString().toLatin1(); 0382 title = Akregator::Utils::convertHtmlTags(article.title()); 0383 } 0384 } 0385 0386 if (text.isEmpty()) { 0387 return; 0388 } 0389 sendArticle(text, title, attach); 0390 } 0391 0392 void MainWidget::cleanUpDownloadFile() 0393 { 0394 for (QPointer<Akregator::DownloadArticleJob> job : std::as_const(mListDownloadArticleJobs)) { 0395 if (job) { 0396 job->forceCleanupTemporaryFile(); 0397 } 0398 } 0399 } 0400 0401 void MainWidget::sendArticle(const QByteArray &text, const QString &title, bool attach) 0402 { 0403 if (attach) { 0404 QPointer<Akregator::DownloadArticleJob> download = new Akregator::DownloadArticleJob(this); 0405 download->setArticleUrl(QUrl(QString::fromUtf8(text))); 0406 download->setText(QString::fromUtf8(text)); 0407 download->setTitle(title); 0408 mListDownloadArticleJobs.append(download); 0409 download->start(); 0410 } else { 0411 QUrlQuery query; 0412 query.addQueryItem(QStringLiteral("subject"), title); 0413 query.addQueryItem(QStringLiteral("body"), QString::fromUtf8(text)); 0414 QUrl url; 0415 url.setScheme(QStringLiteral("mailto")); 0416 url.setQuery(query); 0417 QDesktopServices::openUrl(url); 0418 } 0419 } 0420 0421 void MainWidget::importFeedList(const QDomDocument &doc) 0422 { 0423 auto cmd = new ImportFeedListCommand; 0424 cmd->setParentWidget(this); 0425 cmd->setFeedListDocument(doc); 0426 cmd->setTargetList(m_feedList); 0427 cmd->start(); 0428 } 0429 0430 void MainWidget::setFeedList(const QSharedPointer<FeedList> &list) 0431 { 0432 if (list == m_feedList) { 0433 return; 0434 } 0435 const QSharedPointer<FeedList> oldList = m_feedList; 0436 0437 m_feedList = list; 0438 if (m_feedList) { 0439 connect(m_feedList.data(), &FeedList::unreadCountChanged, this, &MainWidget::slotSetTotalUnread); 0440 } 0441 0442 slotSetTotalUnread(); 0443 0444 m_feedListManagementInterface->setFeedList(m_feedList); 0445 Kernel::self()->setFeedList(m_feedList); 0446 ProgressManager::self()->setFeedList(m_feedList); 0447 m_selectionController->setFeedList(m_feedList); 0448 0449 if (oldList) { 0450 oldList->disconnect(this); 0451 } 0452 0453 slotDeleteExpiredArticles(); 0454 } 0455 0456 void MainWidget::deleteExpiredArticles(const QSharedPointer<FeedList> &list) 0457 { 0458 if (!list) { 0459 return; 0460 } 0461 auto cmd = new ExpireItemsCommand(this); 0462 cmd->setParentWidget(this); 0463 cmd->setFeedList(list); 0464 cmd->setFeeds(list->feedIds()); 0465 cmd->start(); 0466 } 0467 0468 void MainWidget::slotDeleteExpiredArticles() 0469 { 0470 deleteExpiredArticles(m_feedList); 0471 } 0472 0473 QDomDocument MainWidget::feedListToOPML() 0474 { 0475 QDomDocument dom; 0476 if (m_feedList) { 0477 dom = m_feedList->toOpml(); 0478 } 0479 return dom; 0480 } 0481 0482 void MainWidget::addFeedToGroup(const QString &url, const QString &groupName) 0483 { 0484 // Locate the group. 0485 const QList<TreeNode *> namedGroups = m_feedList->findByTitle(groupName); 0486 Folder *group = nullptr; 0487 for (TreeNode *const candidate : namedGroups) { 0488 if (candidate->isGroup()) { 0489 group = static_cast<Folder *>(candidate); 0490 break; 0491 } 0492 } 0493 0494 if (!group) { 0495 auto g = new Folder(groupName); 0496 m_feedList->allFeedsFolder()->appendChild(g); 0497 group = g; 0498 } 0499 0500 // Invoke the Add Feed dialog with url filled in. 0501 addFeed(url, nullptr, group, true); 0502 } 0503 0504 void MainWidget::slotNormalView() 0505 { 0506 if (m_viewMode == NormalView) { 0507 return; 0508 } 0509 0510 if (m_viewMode == CombinedView) { 0511 m_articleWidget->show(); 0512 0513 const Article article = m_selectionController->currentArticle(); 0514 0515 if (!article.isNull()) { 0516 m_articleViewer->showArticle(article); 0517 } else { 0518 m_articleViewer->slotShowSummary(m_selectionController->selectedSubscription()); 0519 } 0520 } 0521 0522 m_articleSplitter->setOrientation(Qt::Vertical); 0523 m_viewMode = NormalView; 0524 0525 Settings::setViewMode(m_viewMode); 0526 } 0527 0528 void MainWidget::slotWidescreenView() 0529 { 0530 if (m_viewMode == WidescreenView) { 0531 return; 0532 } 0533 0534 if (m_viewMode == CombinedView) { 0535 m_articleWidget->show(); 0536 0537 Article article = m_selectionController->currentArticle(); 0538 0539 if (!article.isNull()) { 0540 m_articleViewer->showArticle(article); 0541 } else { 0542 m_articleViewer->slotShowSummary(m_selectionController->selectedSubscription()); 0543 } 0544 } 0545 0546 m_articleSplitter->setOrientation(Qt::Horizontal); 0547 m_viewMode = WidescreenView; 0548 0549 Settings::setViewMode(m_viewMode); 0550 } 0551 0552 void MainWidget::slotCombinedView() 0553 { 0554 if (m_viewMode == CombinedView) { 0555 return; 0556 } 0557 0558 m_articleListView->slotClear(); 0559 m_articleWidget->hide(); 0560 m_viewMode = CombinedView; 0561 0562 Settings::setViewMode(m_viewMode); 0563 } 0564 0565 void MainWidget::slotMoveCurrentNodeUp() 0566 { 0567 TreeNode *current = m_selectionController->selectedSubscription(); 0568 if (!current) { 0569 return; 0570 } 0571 TreeNode *prev = current->prevSibling(); 0572 Folder *parent = current->parent(); 0573 0574 if (!prev || !parent) { 0575 return; 0576 } 0577 0578 parent->removeChild(prev); 0579 parent->insertChild(prev, current); 0580 m_feedListView->ensureNodeVisible(current); 0581 } 0582 0583 void MainWidget::slotMoveCurrentNodeDown() 0584 { 0585 TreeNode *current = m_selectionController->selectedSubscription(); 0586 if (!current) { 0587 return; 0588 } 0589 TreeNode *next = current->nextSibling(); 0590 Folder *parent = current->parent(); 0591 0592 if (!next || !parent) { 0593 return; 0594 } 0595 0596 parent->removeChild(current); 0597 parent->insertChild(current, next); 0598 m_feedListView->ensureNodeVisible(current); 0599 } 0600 0601 void MainWidget::slotMoveCurrentNodeLeft() 0602 { 0603 TreeNode *current = m_selectionController->selectedSubscription(); 0604 if (!current || !current->parent() || !current->parent()->parent()) { 0605 return; 0606 } 0607 0608 Folder *parent = current->parent(); 0609 Folder *grandparent = current->parent()->parent(); 0610 0611 parent->removeChild(current); 0612 grandparent->insertChild(current, parent); 0613 m_feedListView->ensureNodeVisible(current); 0614 } 0615 0616 void MainWidget::slotMoveCurrentNodeRight() 0617 { 0618 TreeNode *current = m_selectionController->selectedSubscription(); 0619 if (!current || !current->parent()) { 0620 return; 0621 } 0622 TreeNode *prev = current->prevSibling(); 0623 0624 if (prev && prev->isGroup()) { 0625 auto fg = static_cast<Folder *>(prev); 0626 current->parent()->removeChild(current); 0627 fg->appendChild(current); 0628 m_feedListView->ensureNodeVisible(current); 0629 } 0630 } 0631 0632 void MainWidget::slotSendLink() 0633 { 0634 sendArticle(); 0635 } 0636 0637 void MainWidget::slotSendFile() 0638 { 0639 sendArticle(true); 0640 } 0641 0642 void MainWidget::slotNodeSelected(TreeNode *node) 0643 { 0644 m_markReadTimer->stop(); 0645 0646 if (m_displayingAboutPage) { 0647 m_mainFrame->slotSetTitle(i18n("Articles")); 0648 if (m_viewMode != CombinedView) { 0649 m_articleWidget->show(); 0650 } 0651 if (Settings::showQuickFilter()) { 0652 m_searchBar->show(); 0653 } 0654 m_displayingAboutPage = false; 0655 } 0656 0657 m_tabWidget->setCurrentWidget(m_mainFrame); 0658 if (Settings::resetQuickFilterOnNodeChange()) { 0659 m_searchBar->slotClearSearch(); 0660 } 0661 0662 if (m_viewMode == CombinedView) { 0663 m_articleViewer->showNode(node); 0664 } else { 0665 m_articleViewer->slotShowSummary(node); 0666 } 0667 0668 if (node) { 0669 m_mainFrame->setWindowTitle(node->title()); 0670 } 0671 0672 m_actionManager->slotNodeSelected(node); 0673 } 0674 0675 void MainWidget::slotFeedAdd() 0676 { 0677 Folder *group = nullptr; 0678 if (!m_selectionController->selectedSubscription()) { 0679 group = m_feedList->allFeedsFolder(); 0680 } else { 0681 if (m_selectionController->selectedSubscription()->isGroup()) { 0682 group = static_cast<Folder *>(m_selectionController->selectedSubscription()); 0683 } else { 0684 group = m_selectionController->selectedSubscription()->parent(); 0685 } 0686 } 0687 0688 TreeNode *const lastChild = !group->children().isEmpty() ? group->children().constLast() : nullptr; 0689 0690 addFeed(QString(), lastChild, group, false); 0691 } 0692 0693 void MainWidget::addFeed(const QString &url, TreeNode *after, Folder *parent, bool autoExec) 0694 { 0695 auto cmd(new CreateFeedCommand(this)); 0696 cmd->setParentWidget(this); 0697 cmd->setPosition(parent, after); 0698 cmd->setRootFolder(m_feedList->allFeedsFolder()); 0699 cmd->setAutoExecute(autoExec); 0700 cmd->setUrl(url); 0701 cmd->setSubscriptionListView(m_feedListView); 0702 cmd->start(); 0703 } 0704 0705 void MainWidget::slotFeedAddGroup() 0706 { 0707 auto cmd = new CreateFolderCommand(this); 0708 cmd->setParentWidget(this); 0709 cmd->setSelectedSubscription(m_selectionController->selectedSubscription()); 0710 cmd->setRootFolder(m_feedList->allFeedsFolder()); 0711 cmd->setSubscriptionListView(m_feedListView); 0712 cmd->start(); 0713 } 0714 0715 void MainWidget::slotFeedRemove() 0716 { 0717 TreeNode *selectedNode = m_selectionController->selectedSubscription(); 0718 0719 // don't delete root element! (safety valve) 0720 if (!selectedNode || selectedNode == m_feedList->allFeedsFolder()) { 0721 return; 0722 } 0723 0724 auto cmd = new DeleteSubscriptionCommand(this); 0725 cmd->setParentWidget(this); 0726 cmd->setSubscription(m_feedList, selectedNode->id()); 0727 cmd->start(); 0728 } 0729 0730 void MainWidget::slotFeedModify() 0731 { 0732 TreeNode *const node = m_selectionController->selectedSubscription(); 0733 if (!node) { 0734 return; 0735 } 0736 auto cmd = new EditSubscriptionCommand(this); 0737 cmd->setParentWidget(this); 0738 cmd->setSubscription(m_feedList, node->id()); 0739 cmd->setSubscriptionListView(m_feedListView); 0740 cmd->start(); 0741 } 0742 0743 void MainWidget::slotNextUnreadArticle() 0744 { 0745 ensureArticleTabVisible(); 0746 if (m_viewMode == CombinedView) { 0747 m_feedListView->slotNextUnreadFeed(); 0748 return; 0749 } 0750 TreeNode *sel = m_selectionController->selectedSubscription(); 0751 if (sel && sel->unread() > 0) { 0752 m_articleListView->slotNextUnreadArticle(); 0753 } else { 0754 m_feedListView->slotNextUnreadFeed(); 0755 } 0756 } 0757 0758 void MainWidget::slotPrevUnreadArticle() 0759 { 0760 ensureArticleTabVisible(); 0761 if (m_viewMode == CombinedView) { 0762 m_feedListView->slotPrevUnreadFeed(); 0763 return; 0764 } 0765 TreeNode *sel = m_selectionController->selectedSubscription(); 0766 if (sel && sel->unread() > 0) { 0767 m_articleListView->slotPreviousUnreadArticle(); 0768 } else { 0769 m_feedListView->slotPrevUnreadFeed(); 0770 } 0771 } 0772 0773 bool MainWidget::confirmMarkFeedAsRead(bool isSingleFeed, bool isGroup) 0774 { 0775 QString msg, caption; 0776 if (isSingleFeed && !isGroup) { 0777 msg = i18n("<qt>Are you sure you want to mark <b>all articles in the feed</b> as read?</qt>"); 0778 caption = i18nc("@title:window", "Mark Feed as Read"); 0779 } else { 0780 if (!isGroup) { 0781 msg = i18n("<qt>Are you sure you want to mark <b>all feeds</b> as read?</qt>"); 0782 } else { 0783 msg = i18n("<qt>Are you sure you want to mark <b>all feeds in the folder</b> as read?</qt>"); 0784 } 0785 caption = i18nc("@title:window", "Mark Feeds as Read"); 0786 } 0787 if (KMessageBox::warningContinueCancel(this, 0788 msg, 0789 caption, 0790 KStandardGuiItem::cont(), 0791 KStandardGuiItem::cancel(), 0792 QStringLiteral("Disable Mark Feed As Read Confirmation")) 0793 != KMessageBox::Continue) { 0794 return false; 0795 } 0796 return true; 0797 } 0798 0799 void MainWidget::slotMarkAllFeedsRead() 0800 { 0801 if (!confirmMarkFeedAsRead(false, false)) { 0802 return; 0803 } 0804 KJob *job = m_feedList->createMarkAsReadJob(); 0805 connect(job, &KJob::finished, m_selectionController, &AbstractSelectionController::forceFilterUpdate); 0806 job->start(); 0807 } 0808 0809 void MainWidget::slotMarkAllRead() 0810 { 0811 TreeNode *current = m_selectionController->selectedSubscription(); 0812 if (!current || !confirmMarkFeedAsRead(true, current->isGroup())) { 0813 return; 0814 } 0815 KJob *job = current->createMarkAsReadJob(); 0816 connect(job, &KJob::finished, m_selectionController, &AbstractSelectionController::forceFilterUpdate); 0817 job->start(); 0818 } 0819 0820 void MainWidget::slotSetTotalUnread() 0821 { 0822 Q_EMIT signalUnreadCountChanged(m_feedList ? m_feedList->unread() : 0); 0823 } 0824 0825 void MainWidget::slotDoIntervalFetches() 0826 { 0827 if (!m_feedList) { 0828 return; 0829 } 0830 #if 0 0831 // the following solid check apparently doesn't work reliably and causes 0832 // interval fetching not working although the user is actually online (but solid reports he's not 0833 const Networking::Status status = Solid::Networking::status(); 0834 if (status != Networking::Connected && status != Networking::Unknown) { 0835 return; 0836 } 0837 #endif 0838 m_feedList->addToFetchQueue(Kernel::self()->fetchQueue(), true); 0839 } 0840 0841 void MainWidget::slotFetchCurrentFeed() 0842 { 0843 if (!m_selectionController->selectedSubscription()) { 0844 return; 0845 } 0846 if (isNetworkAvailable()) { 0847 m_selectionController->selectedSubscription()->slotAddToFetchQueue(Kernel::self()->fetchQueue()); 0848 } else { 0849 m_mainFrame->slotSetStatusText(i18n("Networking is not available.")); 0850 } 0851 } 0852 0853 void MainWidget::slotFetchAllFeeds() 0854 { 0855 if (m_feedList && isNetworkAvailable()) { 0856 m_feedList->addToFetchQueue(Kernel::self()->fetchQueue()); 0857 } else if (m_feedList) { 0858 m_mainFrame->slotSetStatusText(i18n("Networking is not available.")); 0859 } 0860 } 0861 0862 void MainWidget::slotFetchingStarted() 0863 { 0864 m_mainFrame->slotSetState(Frame::Started); 0865 m_actionManager->action(QStringLiteral("feed_stop"))->setEnabled(true); 0866 m_mainFrame->slotSetStatusText(i18n("Fetching Feeds...")); 0867 } 0868 0869 void MainWidget::slotFetchingStopped() 0870 { 0871 m_mainFrame->slotSetState(Frame::Completed); 0872 m_actionManager->action(QStringLiteral("feed_stop"))->setEnabled(false); 0873 m_mainFrame->slotSetStatusText(QString()); 0874 } 0875 0876 void MainWidget::slotArticleSelected(const Akregator::Article &article) 0877 { 0878 if (m_viewMode == CombinedView) { 0879 return; 0880 } 0881 0882 m_markReadTimer->stop(); 0883 0884 Q_ASSERT(article.isNull() || article.feed()); 0885 0886 QList<Article> articles = m_selectionController->selectedArticles(); 0887 Q_EMIT signalArticlesSelected(articles); 0888 0889 auto const maai = qobject_cast<KToggleAction *>(m_actionManager->action(QStringLiteral("article_set_status_important"))); 0890 Q_ASSERT(maai); 0891 maai->setChecked(article.keep()); 0892 0893 m_articleViewer->showArticle(article); 0894 if (m_selectionController->selectedArticles().isEmpty()) { 0895 m_articleListView->setCurrentIndex(m_selectionController->currentArticleIndex()); 0896 } 0897 0898 if (article.isNull() || article.status() == Akregator::Read) { 0899 return; 0900 } 0901 0902 if (!Settings::useMarkReadDelay()) { 0903 return; 0904 } 0905 0906 const int delay = Settings::markReadDelay(); 0907 0908 if (delay > 0) { 0909 m_markReadTimer->start(delay * 1000); 0910 } else { 0911 auto job = new Akregator::ArticleModifyJob; 0912 const Akregator::ArticleId aid = {article.feed()->xmlUrl(), article.guid()}; 0913 job->setStatus(aid, Akregator::Read); 0914 job->start(); 0915 } 0916 } 0917 0918 void MainWidget::slotMouseButtonPressed(int button, const QUrl &url) 0919 { 0920 if (button != Qt::MiddleButton) { 0921 return; 0922 } 0923 0924 if (!url.isValid()) { 0925 return; 0926 } 0927 0928 OpenUrlRequest req(url); 0929 0930 switch (Settings::mMBBehaviour()) { 0931 case Settings::EnumMMBBehaviour::OpenInExternalBrowser: 0932 req.setOptions(OpenUrlRequest::ExternalBrowser); 0933 break; 0934 case Settings::EnumMMBBehaviour::OpenInBackground: 0935 req.setOptions(OpenUrlRequest::NewTab); 0936 req.setOpenInBackground(true); 0937 break; 0938 default: 0939 req.setOptions(OpenUrlRequest::NewTab); 0940 req.setOpenInBackground(false); 0941 } 0942 0943 Kernel::self()->frameManager()->slotOpenUrlRequest(req); 0944 } 0945 0946 void MainWidget::slotOpenHomepage() 0947 { 0948 Feed *feed = dynamic_cast<Feed *>(m_selectionController->selectedSubscription()); 0949 0950 if (!feed) { 0951 return; 0952 } 0953 0954 QUrl url(feed->htmlUrl()); 0955 0956 if (url.isValid()) { 0957 OpenUrlRequest req(url); 0958 req.setOptions(OpenUrlRequest::ExternalBrowser); 0959 Kernel::self()->frameManager()->slotOpenUrlRequest(req); 0960 } 0961 } 0962 0963 void MainWidget::slotOpenSelectedArticlesInBrowser() 0964 { 0965 const QList<Article> articles = m_selectionController->selectedArticles(); 0966 0967 for (const Akregator::Article &article : articles) { 0968 slotOpenArticleInBrowser(article); 0969 } 0970 } 0971 0972 void MainWidget::slotOpenArticleInBrowser(const Akregator::Article &article) 0973 { 0974 if (!article.isNull() && article.link().isValid()) { 0975 OpenUrlRequest req(article.link()); 0976 req.setOptions(OpenUrlRequest::ExternalBrowser); 0977 Kernel::self()->frameManager()->slotOpenUrlRequest(req); 0978 } 0979 } 0980 0981 void MainWidget::openSelectedArticles(bool openInBackground) 0982 { 0983 const QList<Article> articles = m_selectionController->selectedArticles(); 0984 0985 for (const Akregator::Article &article : articles) { 0986 const QUrl url = article.link(); 0987 if (!url.isValid()) { 0988 continue; 0989 } 0990 0991 OpenUrlRequest req(url); 0992 req.setOptions(OpenUrlRequest::NewTab); 0993 if (openInBackground) { 0994 req.setOpenInBackground(true); 0995 Kernel::self()->frameManager()->slotOpenUrlRequest(req, false /*don't use settings for open in background*/); 0996 } else { 0997 Kernel::self()->frameManager()->slotOpenUrlRequest(req); 0998 } 0999 } 1000 } 1001 1002 void MainWidget::currentArticleInfo(QString &link, QString &title) 1003 { 1004 const Article article = m_selectionController->currentArticle(); 1005 1006 if (article.isNull()) { 1007 return; 1008 } 1009 if (article.link().isValid()) { 1010 link = article.link().url(); 1011 title = Utils::convertHtmlTags(article.title()); 1012 } 1013 } 1014 1015 void MainWidget::updateQuickSearchLineText() 1016 { 1017 m_searchBar->updateQuickSearchLineText(m_actionManager->quickSearchLineText()); 1018 } 1019 1020 void MainWidget::slotCopyLinkAddress() 1021 { 1022 const Article article = m_selectionController->currentArticle(); 1023 1024 if (article.isNull()) { 1025 return; 1026 } 1027 1028 QString link; 1029 if (article.link().isValid()) { 1030 link = article.link().url(); 1031 QClipboard *cb = QApplication::clipboard(); 1032 cb->setText(link, QClipboard::Clipboard); 1033 // don't set url to selection as it's a no-no according to a fd.o spec 1034 // cb->setText(link, QClipboard::Selection); 1035 } 1036 } 1037 1038 void MainWidget::slotToggleShowQuickFilter() 1039 { 1040 if (Settings::showQuickFilter()) { 1041 Settings::setShowQuickFilter(false); 1042 m_searchBar->slotClearSearch(); 1043 m_searchBar->hide(); 1044 } else { 1045 Settings::setShowQuickFilter(true); 1046 if (!m_displayingAboutPage) { 1047 m_searchBar->show(); 1048 } 1049 } 1050 } 1051 1052 void MainWidget::slotArticleDelete() 1053 { 1054 if (m_viewMode == CombinedView) { 1055 return; 1056 } 1057 1058 const QList<Article> articles = m_selectionController->selectedArticles(); 1059 1060 QString msg; 1061 switch (articles.count()) { 1062 case 0: 1063 return; 1064 case 1: 1065 msg = i18n("<qt>Are you sure you want to delete article <b>%1</b>?</qt>", articles.first().title()); 1066 break; 1067 default: 1068 msg = i18np("<qt>Are you sure you want to delete the selected article?</qt>", 1069 "<qt>Are you sure you want to delete the %1 selected articles?</qt>", 1070 articles.count()); 1071 } 1072 1073 if (KMessageBox::warningContinueCancel(this, 1074 msg, 1075 i18nc("@title:window", "Delete Article"), 1076 KStandardGuiItem::del(), 1077 KStandardGuiItem::cancel(), 1078 QStringLiteral("Disable delete article confirmation")) 1079 != KMessageBox::Continue) { 1080 return; 1081 } 1082 1083 TreeNode *const selected = m_selectionController->selectedSubscription(); 1084 1085 if (selected) { 1086 selected->setNotificationMode(false); 1087 } 1088 1089 auto job = new Akregator::ArticleDeleteJob; 1090 for (const Akregator::Article &i : articles) { 1091 Feed *const feed = i.feed(); 1092 if (!feed) { 1093 continue; 1094 } 1095 const Akregator::ArticleId aid = {feed->xmlUrl(), i.guid()}; 1096 job->appendArticleId(aid); 1097 } 1098 1099 job->start(); 1100 1101 if (selected) { 1102 selected->setNotificationMode(true); 1103 } 1104 } 1105 1106 void MainWidget::slotFramesChanged() 1107 { 1108 // We need to wait till the frame is fully loaded 1109 QMetaObject::invokeMethod(m_part, &Akregator::Part::slotAutoSave, Qt::QueuedConnection); 1110 } 1111 1112 void MainWidget::slotArticleToggleKeepFlag(bool) 1113 { 1114 const QList<Article> articles = m_selectionController->selectedArticles(); 1115 1116 if (articles.isEmpty()) { 1117 return; 1118 } 1119 1120 bool allFlagsSet = true; 1121 for (const Akregator::Article &i : articles) { 1122 allFlagsSet = allFlagsSet && i.keep(); 1123 if (!allFlagsSet) { 1124 break; 1125 } 1126 } 1127 1128 auto job = new Akregator::ArticleModifyJob; 1129 for (const Akregator::Article &i : articles) { 1130 const Akregator::ArticleId aid = {i.feed()->xmlUrl(), i.guid()}; 1131 job->setKeep(aid, !allFlagsSet); 1132 } 1133 job->start(); 1134 } 1135 1136 namespace 1137 { 1138 void setArticleStatus(const QString &feedUrl, const QString &articleId, int status) 1139 { 1140 if (!feedUrl.isEmpty() && !articleId.isEmpty()) { 1141 auto job = new Akregator::ArticleModifyJob; 1142 const Akregator::ArticleId aid = {feedUrl, articleId}; 1143 job->setStatus(aid, status); 1144 job->start(); 1145 } 1146 } 1147 1148 void setSelectedArticleStatus(const Akregator::AbstractSelectionController *controller, int status) 1149 { 1150 const QList<Akregator::Article> articles = controller->selectedArticles(); 1151 1152 if (articles.isEmpty()) { 1153 return; 1154 } 1155 1156 auto job = new Akregator::ArticleModifyJob; 1157 for (const Akregator::Article &i : articles) { 1158 const Akregator::ArticleId aid = {i.feed()->xmlUrl(), i.guid()}; 1159 job->setStatus(aid, status); 1160 } 1161 job->start(); 1162 } 1163 } 1164 1165 void MainWidget::slotSetSelectedArticleRead() 1166 { 1167 ::setSelectedArticleStatus(m_selectionController, Akregator::Read); 1168 } 1169 1170 void MainWidget::slotSetSelectedArticleUnread() 1171 { 1172 ::setSelectedArticleStatus(m_selectionController, Akregator::Unread); 1173 } 1174 1175 void MainWidget::slotSetSelectedArticleNew() 1176 { 1177 ::setSelectedArticleStatus(m_selectionController, Akregator::New); 1178 } 1179 1180 void MainWidget::slotSetCurrentArticleReadDelayed() 1181 { 1182 const Article article = m_selectionController->currentArticle(); 1183 1184 if (article.isNull()) { 1185 return; 1186 } 1187 1188 auto const job = new Akregator::ArticleModifyJob; 1189 const Akregator::ArticleId aid = {article.feed()->xmlUrl(), article.guid()}; 1190 job->setStatus(aid, Akregator::Read); 1191 job->start(); 1192 } 1193 1194 void MainWidget::slotShowStatusBarMessage(const QString &msg) 1195 { 1196 PimCommon::BroadcastStatus::instance()->setStatusMsg(msg); 1197 } 1198 1199 void MainWidget::readProperties(const KConfigGroup &config) 1200 { 1201 if (!Settings::resetQuickFilterOnNodeChange()) { 1202 // read filter settings 1203 m_searchBar->slotSetText(config.readEntry("searchLine")); 1204 m_searchBar->slotSetStatus(config.readEntry("searchCombo").toInt()); 1205 } 1206 const QString currentTabName = config.readEntry("CurrentTab"); 1207 // Reopen tabs 1208 const QStringList childList = config.readEntry(QStringLiteral("Children"), QStringList()); 1209 int currentFrameId = -1; 1210 for (const QString &framePrefix : childList) { 1211 auto const frame = new WebEngineFrame(m_actionManager->actionCollection(), m_tabWidget); 1212 frame->loadConfig(config, framePrefix + QLatin1Char('_')); 1213 1214 connectFrame(frame); 1215 Kernel::self()->frameManager()->slotAddFrame(frame); 1216 if (currentTabName == framePrefix) { 1217 currentFrameId = frame->id(); 1218 } 1219 } 1220 if (currentFrameId != -1) { 1221 m_tabWidget->slotSelectFrame(currentFrameId); 1222 } 1223 } 1224 1225 void MainWidget::saveProperties(KConfigGroup &config) 1226 { 1227 // save filter settings 1228 const QString searchStr(m_searchBar->text()); 1229 if (searchStr.isEmpty()) { 1230 config.deleteEntry("searchLine"); 1231 } else { 1232 config.writeEntry("searchLine", m_searchBar->text()); 1233 } 1234 config.writeEntry("searchCombo", static_cast<int>(m_searchBar->status())); 1235 1236 Kernel::self()->frameManager()->saveProperties(config); 1237 } 1238 1239 void MainWidget::ensureArticleTabVisible() 1240 { 1241 m_tabWidget->setCurrentWidget(m_mainFrame); 1242 } 1243 1244 void MainWidget::slotReloadAllTabs() 1245 { 1246 m_tabWidget->slotReloadAllTabs(); 1247 } 1248 1249 bool MainWidget::isNetworkAvailable() const 1250 { 1251 return PimCommon::NetworkManager::self()->isOnline(); 1252 } 1253 1254 void MainWidget::slotNetworkStatusChanged(bool status) 1255 { 1256 if (status) { 1257 m_mainFrame->slotSetStatusText(i18n("Networking is available now.")); 1258 this->slotFetchAllFeeds(); 1259 } else { 1260 m_mainFrame->slotSetStatusText(i18n("Networking is not available.")); 1261 } 1262 } 1263 1264 void MainWidget::slotOpenSelectedArticles() 1265 { 1266 openSelectedArticles(false); 1267 } 1268 1269 void MainWidget::slotOpenSelectedArticlesInBackground() 1270 { 1271 openSelectedArticles(true); 1272 } 1273 1274 void MainWidget::slotCurrentFrameChanged(int frameId) 1275 { 1276 Kernel::self()->frameManager()->slotChangeFrame(frameId); 1277 m_actionManager->zoomActionMenu()->setZoomFactor(Kernel::self()->frameManager()->currentFrame()->zoomFactor() * 100); 1278 } 1279 1280 void MainWidget::slotFocusQuickSearch() 1281 { 1282 m_searchBar->setFocusSearchLine(); 1283 } 1284 1285 void MainWidget::slotArticleAction(Akregator::ArticleViewerWebEngine::ArticleAction type, const QString &articleId, const QString &feed) 1286 { 1287 switch (type) { 1288 case ArticleViewerWebEngine::DeleteAction: { 1289 auto job = new Akregator::ArticleDeleteJob; 1290 const Akregator::ArticleId aid = {feed, articleId}; 1291 job->appendArticleId(aid); 1292 job->start(); 1293 break; 1294 } 1295 case ArticleViewerWebEngine::MarkAsRead: 1296 ::setArticleStatus(feed, articleId, Akregator::Read); 1297 break; 1298 case ArticleViewerWebEngine::MarkAsUnRead: 1299 ::setArticleStatus(feed, articleId, Akregator::Unread); 1300 break; 1301 1302 case ArticleViewerWebEngine::MarkAsImportant: { 1303 auto job = new Akregator::ArticleModifyJob; 1304 const Akregator::Article article = m_feedList->findArticle(feed, articleId); 1305 const Akregator::ArticleId aid = {feed, articleId}; 1306 job->setKeep(aid, !article.keep()); 1307 job->start(); 1308 break; 1309 } 1310 case ArticleViewerWebEngine::SendUrlArticle: { 1311 case ArticleViewerWebEngine::SendFileArticle: 1312 const Article article = m_feedList->findArticle(feed, articleId); 1313 const QByteArray text = article.link().toDisplayString().toLatin1(); 1314 const QString title = Akregator::Utils::convertHtmlTags(article.title()); 1315 if (text.isEmpty()) { 1316 return; 1317 } 1318 sendArticle(text, title, (type == ArticleViewerWebEngine::SendFileArticle)); 1319 break; 1320 } 1321 case ArticleViewerWebEngine::OpenInBackgroundTab: { 1322 const Akregator::Article article = m_feedList->findArticle(feed, articleId); 1323 const QUrl url = article.link(); 1324 if (url.isValid()) { 1325 OpenUrlRequest req(url); 1326 req.setOptions(OpenUrlRequest::NewTab); 1327 req.setOpenInBackground(true); 1328 Kernel::self()->frameManager()->slotOpenUrlRequest(req, false /*don't use settings for open in background*/); 1329 } 1330 break; 1331 } 1332 case ArticleViewerWebEngine::OpenInExternalBrowser: { 1333 const Akregator::Article article = m_feedList->findArticle(feed, articleId); 1334 slotOpenArticleInBrowser(article); 1335 break; 1336 } 1337 } 1338 } 1339 1340 #include "moc_mainwidget.cpp"