File indexing completed on 2024-04-28 13:40:49
0001 /*************************************************************************** 0002 * Copyright (C) 2008-2018 by Daniel Nicoletti * 0003 * dantti12@gmail.com * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; see the file COPYING. If not, write to * 0017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0018 * Boston, MA 02110-1301, USA. * 0019 ***************************************************************************/ 0020 0021 #include "ApperKCM.h" 0022 #include "ui_ApperKCM.h" 0023 0024 #include <config.h> 0025 0026 #include <KAboutData> 0027 0028 #include <KLocalizedString> 0029 #include <QStandardPaths> 0030 #include <KMessageBox> 0031 #include <KFileItemDelegate> 0032 #include <KHelpMenu> 0033 #include <QTabBar> 0034 #include <QToolBar> 0035 #include <QSignalMapper> 0036 #include <QTimer> 0037 #include <QPointer> 0038 #include <QKeyEvent> 0039 0040 #include <PackageModel.h> 0041 #include <ApplicationSortFilterModel.h> 0042 #include <ChangesDelegate.h> 0043 #include <PkStrings.h> 0044 #include <PkIcons.h> 0045 #include <PkTransactionWidget.h> 0046 0047 #ifdef HAVE_APPSTREAM 0048 #include <AppStream.h> 0049 #endif 0050 0051 #include <QLoggingCategory> 0052 #include <Daemon> 0053 0054 #include "FiltersMenu.h" 0055 #include "BrowseView.h" 0056 #include "CategoryModel.h" 0057 #include "TransactionHistory.h" 0058 #include "Settings/Settings.h" 0059 #include "Updater/Updater.h" 0060 0061 Q_LOGGING_CATEGORY(APPER, "apper") 0062 0063 #define BAR_SEARCH 0 0064 #define BAR_UPDATE 1 0065 #define BAR_SETTINGS 2 0066 #define BAR_TITLE 3 0067 0068 ApperKCM::ApperKCM(QWidget *parent) : 0069 QWidget(parent), 0070 ui(new Ui::ApperKCM), 0071 m_findIcon(QIcon::fromTheme(QLatin1String("edit-find"))), 0072 m_cancelIcon(QIcon::fromTheme(QLatin1String("dialog-cancel"))) 0073 { 0074 ui->setupUi(this); 0075 0076 // store the actions supported by the backend 0077 connect(Daemon::global(), &Daemon::changed, this, &ApperKCM::daemonChanged); 0078 0079 // Set the current locale 0080 Daemon::global()->setHints(QLatin1String("locale=") + QLocale::system().name() + QLatin1String(".UTF-8")); 0081 0082 // Browse TAB 0083 ui->backTB->setIcon(QIcon::fromTheme(QLatin1String("go-previous"))); 0084 0085 // create our toolbar 0086 auto toolBar = new QToolBar(this); 0087 ui->gridLayout_2->addWidget(toolBar); 0088 toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); 0089 0090 connect(ui->browseView, &BrowseView::categoryActivated, this, &ApperKCM::on_homeView_activated); 0091 0092 auto findMenu = new QMenu(this); 0093 // find is just a generic name in case we don't have any search method 0094 m_genericActionK = new KToolBarPopupAction(m_findIcon, i18n("Find"), this); 0095 toolBar->addAction(m_genericActionK); 0096 0097 // Add actions that the backend supports 0098 findMenu->addAction(ui->actionFindName); 0099 setCurrentAction(ui->actionFindName); 0100 0101 findMenu->addAction(ui->actionFindDescription); 0102 if (!m_currentAction) { 0103 setCurrentAction(ui->actionFindDescription); 0104 } 0105 0106 findMenu->addAction(ui->actionFindFile); 0107 if (!m_currentAction) { 0108 setCurrentAction(ui->actionFindFile); 0109 } 0110 0111 0112 // If no action was set we can't use this search 0113 if (m_currentAction == nullptr) { 0114 m_genericActionK->setEnabled(false); 0115 ui->searchKLE->setEnabled(false); 0116 } else { 0117 // Check to see if we need the KToolBarPopupAction 0118 setCurrentActionCancel(false); 0119 if (findMenu->actions().size() > 1) { 0120 m_currentAction->setVisible(false); 0121 m_genericActionK->setMenu(findMenu); 0122 } else { 0123 m_currentAction->setVisible(true); 0124 toolBar->removeAction(m_genericActionK); 0125 toolBar->addAction(m_currentAction); 0126 } 0127 connect(m_genericActionK, &KToolBarPopupAction::triggered, this, &ApperKCM::genericActionKTriggered); 0128 } 0129 0130 // Create the groups model 0131 m_groupsModel = new CategoryModel(this); 0132 ui->browseView->setCategoryModel(m_groupsModel); 0133 connect(m_groupsModel, &CategoryModel::finished, this, &ApperKCM::setupHomeModel); 0134 ui->homeView->setSpacing(10); 0135 ui->homeView->viewport()->setAttribute(Qt::WA_Hover); 0136 0137 KFileItemDelegate *delegate = new KFileItemDelegate(this); 0138 delegate->setWrapMode(QTextOption::WordWrap); 0139 ui->homeView->setItemDelegate(delegate); 0140 0141 // install the backend filters 0142 ui->filtersTB->setMenu(m_filtersMenu = new FiltersMenu(this)); 0143 connect(m_filtersMenu, &FiltersMenu::filtersChanged, this, &ApperKCM::search); 0144 ui->filtersTB->setIcon(QIcon::fromTheme(QLatin1String("view-filter"))); 0145 ApplicationSortFilterModel *proxy = ui->browseView->proxy(); 0146 proxy->setApplicationFilter(m_filtersMenu->filterApplications()); 0147 connect(m_filtersMenu, QOverload<bool>::of(&FiltersMenu::filterApplications), proxy, &ApplicationSortFilterModel::setApplicationFilter); 0148 0149 //initialize the model, delegate, client and connect it's signals 0150 m_browseModel = ui->browseView->model(); 0151 0152 // CHANGES TAB 0153 ui->changesView->viewport()->setAttribute(Qt::WA_Hover); 0154 m_changesModel = new PackageModel(this); 0155 auto changedProxy = new KCategorizedSortFilterProxyModel(this); 0156 changedProxy->setSourceModel(m_changesModel); 0157 changedProxy->setDynamicSortFilter(true); 0158 changedProxy->setCategorizedModel(true); 0159 changedProxy->setSortCaseSensitivity(Qt::CaseInsensitive); 0160 changedProxy->setSortRole(PackageModel::SortRole); 0161 changedProxy->sort(0); 0162 ui->changesView->setModel(changedProxy); 0163 auto changesDelegate = new ChangesDelegate(ui->changesView); 0164 changesDelegate->setExtendPixmapWidth(0); 0165 ui->changesView->setItemDelegate(changesDelegate); 0166 0167 // Connect this signal to keep track of changes 0168 connect(m_browseModel, &PackageModel::changed, this, &ApperKCM::checkChanged); 0169 0170 // packageUnchecked from changes model 0171 connect(m_changesModel, &PackageModel::packageUnchecked, m_changesModel, &PackageModel::removePackage); 0172 connect(m_changesModel, &PackageModel::packageUnchecked, m_browseModel, &PackageModel::uncheckPackageDefault); 0173 0174 ui->reviewMessage->setIcon(QIcon::fromTheme(QLatin1String("edit-redo"))); 0175 ui->reviewMessage->setText(i18n("Some software changes were made")); 0176 auto reviewAction = new QAction(i18n("Review"), this); 0177 connect(reviewAction, &QAction::triggered, this, &ApperKCM::showReviewPages); 0178 ui->reviewMessage->addAction(reviewAction); 0179 auto discardAction = new QAction(i18n("Discard"), this); 0180 connect(discardAction, &QAction::triggered, m_browseModel, &PackageModel::uncheckAll); 0181 ui->reviewMessage->addAction(discardAction); 0182 auto applyAction = new QAction(i18n("Apply"), this); 0183 connect(applyAction, &QAction::triggered, this, &ApperKCM::save); 0184 ui->reviewMessage->addAction(applyAction); 0185 ui->reviewMessage->setCloseButtonVisible(false); 0186 ui->reviewMessage->hide(); 0187 connect(ui->reviewMessage, &KMessageWidget::showAnimationFinished, this, [this] () { 0188 if (!ui->reviewMessage->property("HasChanges").toBool()) { 0189 ui->reviewMessage->animatedHide(); 0190 } 0191 }); 0192 connect(ui->reviewMessage, &KMessageWidget::hideAnimationFinished, this, [this] () { 0193 if (ui->reviewMessage->property("HasChanges").toBool()) { 0194 ui->reviewMessage->animatedShow(); 0195 } 0196 }); 0197 0198 auto menu = new QMenu(this); 0199 ui->settingsTB->setMenu(menu); 0200 ui->settingsTB->setIcon(QIcon::fromTheme(QLatin1String("preferences-other"))); 0201 0202 auto signalMapper = new QSignalMapper(this); 0203 connect(signalMapper, QOverload<const QString &>::of(&QSignalMapper::mapped), this, &ApperKCM::setPage); 0204 0205 QAction *action; 0206 action = menu->addAction(QIcon::fromTheme(QLatin1String("view-history")), i18n("History")); 0207 signalMapper->setMapping(action, QLatin1String("history")); 0208 connect(action, &QAction::triggered, signalMapper, QOverload<>::of(&QSignalMapper::map)); 0209 0210 action = menu->addAction(QIcon::fromTheme(QLatin1String("preferences-other")), i18n("Settings")); 0211 signalMapper->setMapping(action, QLatin1String("settings")); 0212 connect(action, &QAction::triggered, signalMapper, QOverload<>::of(&QSignalMapper::map)); 0213 0214 auto helpMenu = new KHelpMenu(this, KAboutData::applicationData()); 0215 menu->addMenu(helpMenu->menu()); 0216 0217 // Make sure the search bar is visible 0218 ui->stackedWidgetBar->setCurrentIndex(BAR_SEARCH); 0219 } 0220 0221 void ApperKCM::setupHomeModel() 0222 { 0223 KCategorizedSortFilterProxyModel *oldProxy = m_groupsProxyModel; 0224 m_groupsProxyModel = new KCategorizedSortFilterProxyModel(this); 0225 m_groupsProxyModel->setSourceModel(m_groupsModel); 0226 m_groupsProxyModel->setCategorizedModel(true); 0227 m_groupsProxyModel->sort(0); 0228 ui->homeView->setModel(m_groupsProxyModel); 0229 if (oldProxy) { 0230 oldProxy->deleteLater(); 0231 } 0232 } 0233 0234 void ApperKCM::genericActionKTriggered() 0235 { 0236 m_currentAction->trigger(); 0237 } 0238 0239 void ApperKCM::setCurrentAction(QAction *action) 0240 { 0241 // just load the new action if it changes this 0242 // also ensures that our menu has more than one action 0243 if (m_currentAction != action) { 0244 // hides the item from the list 0245 action->setVisible(false); 0246 // ensures the current action was created 0247 if (m_currentAction) { 0248 // show the item back in the list 0249 m_currentAction->setVisible(true); 0250 } 0251 m_currentAction = action; 0252 // copy data from the curront action 0253 m_genericActionK->setText(m_currentAction->text()); 0254 m_genericActionK->setIcon(m_currentAction->icon()); 0255 } 0256 } 0257 0258 void ApperKCM::setCurrentActionEnabled(bool state) 0259 { 0260 if (m_currentAction) { 0261 m_currentAction->setEnabled(state); 0262 } 0263 m_genericActionK->setEnabled(state); 0264 } 0265 0266 void ApperKCM::setCurrentActionCancel(bool cancel) 0267 { 0268 if (cancel) { 0269 // every action should like cancel 0270 ui->actionFindName->setText(i18n("&Cancel")); 0271 ui->actionFindFile->setText(i18n("&Cancel")); 0272 ui->actionFindDescription->setText(i18n("&Cancel")); 0273 m_genericActionK->setText(i18n("&Cancel")); 0274 // set cancel icons 0275 ui->actionFindFile->setIcon(m_cancelIcon); 0276 ui->actionFindDescription->setIcon(m_cancelIcon); 0277 ui->actionFindName->setIcon(m_cancelIcon); 0278 m_genericActionK->setIcon(m_cancelIcon); 0279 } else { 0280 ui->actionFindName->setText(i18n("Find by &name")); 0281 ui->actionFindFile->setText(i18n("Find by f&ile name")); 0282 ui->actionFindDescription->setText(i18n("Find by &description")); 0283 // Define actions icon 0284 ui->actionFindFile->setIcon(QIcon::fromTheme(QLatin1String("document-open"))); 0285 ui->actionFindDescription->setIcon(QIcon::fromTheme(QLatin1String("document-edit"))); 0286 ui->actionFindName->setIcon(m_findIcon); 0287 m_genericActionK->setIcon(m_findIcon); 0288 if (m_currentAction) { 0289 m_genericActionK->setText(m_currentAction->text()); 0290 } else { 0291 // This might happen when the backend can 0292 // only search groups 0293 m_genericActionK->setText(i18n("Find")); 0294 } 0295 } 0296 } 0297 0298 void ApperKCM::checkChanged() 0299 { 0300 bool hasChanges = false; 0301 if (ui->stackedWidget->currentWidget() == ui->pageHome || 0302 ui->stackedWidget->currentWidget() == ui->pageChanges || 0303 ui->stackedWidget->currentWidget() == ui->pageBrowse) { 0304 hasChanges = m_browseModel->hasChanges(); 0305 if (!hasChanges && ui->stackedWidget->currentWidget() == ui->pageChanges) { 0306 search(); 0307 } 0308 if (hasChanges) { 0309 if (!ui->reviewMessage->isHideAnimationRunning() && !ui->reviewMessage->isShowAnimationRunning()) { 0310 ui->reviewMessage->animatedShow(); 0311 } 0312 } else { 0313 if (!ui->reviewMessage->isHideAnimationRunning() && !ui->reviewMessage->isShowAnimationRunning()) { 0314 ui->reviewMessage->animatedHide(); 0315 } 0316 } 0317 ui->reviewMessage->setProperty("HasChanges", hasChanges); 0318 } else if (ui->stackedWidget->currentWidget() == m_updaterPage) { 0319 hasChanges = m_updaterPage->hasChanges(); 0320 } else if (ui->stackedWidget->currentWidget() == m_settingsPage) { 0321 hasChanges = m_settingsPage->hasChanges(); 0322 } 0323 0324 emit changed(hasChanges); 0325 } 0326 0327 void ApperKCM::errorCode(PackageKit::Transaction::Error error, const QString &details) 0328 { 0329 if (error != Transaction::ErrorTransactionCancelled) { 0330 KMessageBox::detailedError(this, PkStrings::errorMessage(error), details, PkStrings::error(error), KMessageBox::Notify); 0331 } 0332 } 0333 0334 ApperKCM::~ApperKCM() 0335 { 0336 delete ui; 0337 } 0338 0339 void ApperKCM::daemonChanged() 0340 { 0341 Transaction::Roles roles = Daemon::roles(); 0342 if (m_roles == roles) { 0343 return; 0344 } 0345 m_roles = roles; 0346 0347 // Add actions that the backend supports 0348 ui->actionFindName->setEnabled(roles & Transaction::RoleSearchName); 0349 ui->actionFindDescription->setEnabled(roles & Transaction::RoleSearchDetails); 0350 ui->actionFindFile->setEnabled(roles & Transaction::RoleSearchFile); 0351 0352 ui->browseView->init(roles); 0353 0354 m_groupsModel->setRoles(roles); 0355 0356 m_filtersMenu->setFilters(Daemon::filters()); 0357 } 0358 0359 void ApperKCM::on_actionFindName_triggered() 0360 { 0361 setCurrentAction(ui->actionFindName); 0362 if (!ui->searchKLE->text().isEmpty()) { 0363 // cache the search 0364 m_searchRole = Transaction::RoleSearchName; 0365 m_searchString = ui->searchKLE->text(); 0366 // create the main transaction 0367 search(); 0368 } 0369 } 0370 0371 void ApperKCM::on_actionFindDescription_triggered() 0372 { 0373 setCurrentAction(ui->actionFindDescription); 0374 if (!ui->searchKLE->text().isEmpty()) { 0375 // cache the search 0376 m_searchRole = Transaction::RoleSearchDetails; 0377 m_searchString = ui->searchKLE->text(); 0378 // create the main transaction 0379 search(); 0380 } 0381 } 0382 0383 void ApperKCM::on_actionFindFile_triggered() 0384 { 0385 setCurrentAction(ui->actionFindFile); 0386 if (!ui->searchKLE->text().isEmpty()) { 0387 // cache the search 0388 m_searchRole = Transaction::RoleSearchFile; 0389 m_searchString = ui->searchKLE->text(); 0390 // create the main transaction 0391 search(); 0392 } 0393 } 0394 0395 void ApperKCM::on_homeView_activated(const QModelIndex &index) 0396 { 0397 if (index.isValid()) { 0398 const auto proxy = qobject_cast<const QSortFilterProxyModel*>(index.model()); 0399 // If the cast failed it's the index came from browseView 0400 if (proxy) { 0401 m_searchParentCategory = proxy->mapToSource(index); 0402 } else { 0403 m_searchParentCategory = index; 0404 } 0405 0406 // cache the search 0407 m_searchRole = static_cast<Transaction::Role>(index.data(CategoryModel::SearchRole).toUInt()); 0408 qCDebug(APPER) << m_searchRole << index.data(CategoryModel::CategoryRole).toString(); 0409 if (m_searchRole == Transaction::RoleResolve) { 0410 #ifdef HAVE_APPSTREAM 0411 CategoryMatcher parser = index.data(CategoryModel::CategoryRole).value<CategoryMatcher>(); 0412 // m_searchCategory = AppStream::instance()->findPkgNames(parser); 0413 #endif // HAVE_APPSTREAM 0414 } else if (m_searchRole == Transaction::RoleSearchGroup) { 0415 if (index.data(CategoryModel::GroupRole).type() == QVariant::String) { 0416 QString category = index.data(CategoryModel::GroupRole).toString(); 0417 if (category.startsWith(QLatin1Char('@')) || 0418 (category.startsWith(QLatin1String("repo:")) && category.size() > 5)) { 0419 m_searchGroupCategory = category; 0420 } else { 0421 m_groupsModel->setRootIndex(m_searchParentCategory); 0422 ui->backTB->setEnabled(true); 0423 return; 0424 } 0425 } else { 0426 m_searchGroupCategory.clear(); 0427 int groupRole = index.data(CategoryModel::GroupRole).toInt(); 0428 m_searchGroup = static_cast<PackageKit::Transaction::Group>(groupRole); 0429 m_searchString = index.data().toString(); // Store the nice name to change the title 0430 } 0431 } else if (m_searchRole == Transaction::RoleGetUpdates) { 0432 setPage(QLatin1String("updates")); 0433 return; 0434 } 0435 0436 // create the main transaction 0437 search(); 0438 } 0439 } 0440 0441 bool ApperKCM::canChangePage() 0442 { 0443 bool changed; 0444 // Check if we can change the current page 0445 if (ui->stackedWidget->currentWidget() == m_updaterPage) { 0446 changed = m_updaterPage->hasChanges(); 0447 } else if (ui->stackedWidget->currentWidget() == m_settingsPage) { 0448 changed = m_settingsPage->hasChanges(); 0449 } else { 0450 changed = m_browseModel->hasChanges(); 0451 } 0452 0453 // if there are no changes don't ask the user 0454 if (!changed) { 0455 return true; 0456 } 0457 0458 const int queryUser = KMessageBox::warningYesNoCancel( 0459 this, 0460 i18n("The settings of the current module have changed.\n" 0461 "Do you want to apply the changes or discard them?"), 0462 i18n("Apply Settings"), 0463 KStandardGuiItem::apply(), 0464 KStandardGuiItem::discard(), 0465 KStandardGuiItem::cancel()); 0466 0467 switch (queryUser) { 0468 case KMessageBox::Yes: 0469 save(); 0470 return true; 0471 case KMessageBox::No: 0472 load(); 0473 return true; 0474 case KMessageBox::Cancel: 0475 return false; 0476 default: 0477 return false; 0478 } 0479 } 0480 0481 QString ApperKCM::page() const 0482 { 0483 return QString(); 0484 } 0485 0486 void ApperKCM::setPage(const QString &page) 0487 { 0488 auto transaction = qobject_cast<PkTransaction*>(ui->stackedWidget->currentWidget()); 0489 if (transaction) { 0490 return; 0491 } 0492 0493 if (page == QLatin1String("settings")) { 0494 if (ui->stackedWidget->currentWidget() != m_settingsPage) { 0495 if (!canChangePage()) { 0496 return; 0497 } 0498 0499 if (m_settingsPage == nullptr) { 0500 m_settingsPage = new Settings(m_roles, this); 0501 connect(m_settingsPage, &Settings::changed, this, &ApperKCM::checkChanged); 0502 connect(m_settingsPage, &Settings::refreshCache, this, &ApperKCM::refreshCache); 0503 ui->stackedWidget->addWidget(m_settingsPage); 0504 0505 connect(ui->generalSettingsPB, &QPushButton::toggled, m_settingsPage, &Settings::showGeneralSettings); 0506 connect(ui->repoSettingsPB, &QPushButton::toggled, m_settingsPage, &Settings::showRepoSettings); 0507 } 0508 checkChanged(); 0509 // ui->buttonBox->clear(); 0510 // ui->buttonBox->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Reset); 0511 // setButtons(KCModule::Default | KCModule::Apply); 0512 emit changed(true); // THIS IS DUMB setButtons only take effect after changed goes true 0513 emit changed(false); 0514 ui->generalSettingsPB->setChecked(true); 0515 ui->stackedWidgetBar->setCurrentIndex(BAR_SETTINGS); 0516 ui->stackedWidget->setCurrentWidget(m_settingsPage); 0517 m_settingsPage->load(); 0518 ui->titleL->clear(); 0519 ui->backTB->setEnabled(true); 0520 emit caption(i18n("Settings")); 0521 } 0522 } else if (page == QLatin1String("updates")) { 0523 if (ui->stackedWidget->currentWidget() != m_updaterPage) { 0524 if (!canChangePage()) { 0525 return; 0526 } 0527 0528 if (m_updaterPage == nullptr) { 0529 m_updaterPage = new Updater(m_roles, this); 0530 connect(m_updaterPage, &Updater::refreshCache, this, &ApperKCM::refreshCache); 0531 connect(m_updaterPage, &Updater::downloadSize, ui->downloadL, &QLabel::setText); 0532 // connect(m_updaterPage, &Updater::changed, this, &ApperKCM::checkChanged); 0533 ui->stackedWidget->addWidget(m_updaterPage); 0534 ui->checkUpdatesPB->setIcon(QIcon::fromTheme(QLatin1String("view-refresh"))); 0535 connect(ui->checkUpdatesPB, &QPushButton::clicked, this, &ApperKCM::refreshCache); 0536 0537 ui->updatePB->setIcon(QIcon::fromTheme(QLatin1String("system-software-update"))); 0538 connect(ui->updatePB, &QPushButton::clicked, this, &ApperKCM::save); 0539 connect(m_updaterPage, &Updater::changed, ui->updatePB, &QPushButton::setEnabled); 0540 } 0541 0542 checkChanged(); 0543 ui->stackedWidget->setCurrentWidget(m_updaterPage); 0544 m_updaterPage->load(); 0545 ui->stackedWidgetBar->setCurrentIndex(BAR_UPDATE); 0546 ui->backTB->setEnabled(true); 0547 emit caption(i18n("Updates")); 0548 } 0549 } else if (page == QLatin1String("home")) { 0550 if (ui->stackedWidget->currentWidget() == m_updaterPage || 0551 ui->stackedWidget->currentWidget() == m_settingsPage) { 0552 on_backTB_clicked(); 0553 } 0554 } else if (page == QLatin1String("history")) { 0555 m_history = new TransactionHistory(this); 0556 ui->searchKLE->clear(); 0557 connect(ui->searchKLE, &QLineEdit::textChanged, m_history, &TransactionHistory::setFilterRegExp); 0558 ui->stackedWidget->addWidget(m_history); 0559 ui->stackedWidget->setCurrentWidget(m_history); 0560 ui->backTB->setEnabled(true); 0561 ui->filtersTB->setEnabled(false); 0562 ui->widget->setEnabled(false); 0563 emit caption(i18n("History")); 0564 } 0565 } 0566 0567 void ApperKCM::on_backTB_clicked() 0568 { 0569 bool canGoBack = false; 0570 if (ui->stackedWidget->currentWidget() == ui->pageBrowse) { 0571 if (!ui->browseView->goBack()) { 0572 return; 0573 } else if (m_groupsModel->hasParent()) { 0574 canGoBack = true; 0575 } 0576 } else if (ui->stackedWidget->currentWidget() == m_history) { 0577 ui->filtersTB->setEnabled(true); 0578 ui->widget->setEnabled(true); 0579 m_history->deleteLater(); 0580 m_history = nullptr; 0581 } else if (ui->stackedWidget->currentWidget() == ui->pageHome) { 0582 if (m_groupsModel->setParentIndex()) { 0583 // if we are able to set a new parent item 0584 // do not disable back button 0585 return; 0586 } 0587 } else if (ui->stackedWidget->currentWidget() == m_updaterPage) { 0588 if (!canChangePage()) { 0589 return; 0590 } 0591 ui->stackedWidgetBar->setCurrentIndex(BAR_SEARCH); 0592 checkChanged(); 0593 } else if (ui->stackedWidget->currentWidget() == m_settingsPage) { 0594 if (!canChangePage()) { 0595 return; 0596 } 0597 emit changed(true); // THIS IS DUMB setButtons only take effect after changed goes true 0598 ui->stackedWidgetBar->setCurrentIndex(BAR_SEARCH); 0599 checkChanged(); 0600 } 0601 0602 ui->homeView->selectionModel()->clear(); 0603 ui->stackedWidget->setCurrentWidget(ui->pageHome); 0604 ui->backTB->setEnabled(canGoBack); 0605 // reset the search role 0606 m_searchRole = Transaction::RoleUnknown; 0607 emit caption(); 0608 } 0609 0610 void ApperKCM::showReviewPages() 0611 { 0612 m_changesModel->clear(); 0613 m_changesModel->addSelectedPackagesFromModel(m_browseModel); 0614 ui->stackedWidget->setCurrentWidget(ui->pageChanges); 0615 ui->backTB->setEnabled(true); 0616 emit caption(i18n("Pending Changes")); 0617 } 0618 0619 void ApperKCM::disconnectTransaction() 0620 { 0621 if (m_searchTransaction) { 0622 // Disconnect everything so that the model don't store 0623 // wrong data 0624 m_searchTransaction->cancel(); 0625 disconnect(m_searchTransaction, &Transaction::finished, ui->browseView->busyCursor(), &KPixmapSequenceOverlayPainter::stop); 0626 disconnect(m_searchTransaction, &Transaction::finished, this, &ApperKCM::finished); 0627 disconnect(m_searchTransaction, &Transaction::finished, m_browseModel, &PackageModel::finished); 0628 disconnect(m_searchTransaction, &Transaction::finished, m_browseModel, &PackageModel::fetchSizes); 0629 disconnect(m_searchTransaction, &Transaction::package, m_browseModel, &PackageModel::addNotSelectedPackage); 0630 disconnect(m_searchTransaction, &Transaction::errorCode, this, &ApperKCM::errorCode); 0631 } 0632 } 0633 0634 void ApperKCM::search() 0635 { 0636 ui->browseView->cleanUi(); 0637 if (ui->stackedWidgetBar->currentIndex() != BAR_SEARCH) { 0638 ui->stackedWidgetBar->setCurrentIndex(BAR_SEARCH); 0639 } 0640 0641 disconnectTransaction(); 0642 0643 // search 0644 switch (m_searchRole) { 0645 case Transaction::RoleSearchName: 0646 m_searchTransaction = Daemon::searchNames(m_searchString, m_filtersMenu->filters()); 0647 emit caption(m_searchString); 0648 break; 0649 case Transaction::RoleSearchDetails: 0650 m_searchTransaction = Daemon::searchDetails(m_searchString, m_filtersMenu->filters()); 0651 emit caption(m_searchString); 0652 break; 0653 case Transaction::RoleSearchFile: 0654 m_searchTransaction = Daemon::searchFiles(m_searchString, m_filtersMenu->filters()); 0655 emit caption(m_searchString); 0656 break; 0657 case Transaction::RoleSearchGroup: 0658 if (m_searchGroupCategory.isEmpty()) { 0659 m_searchTransaction = Daemon::searchGroup(m_searchGroup, m_filtersMenu->filters()); 0660 // m_searchString has the group nice name 0661 emit caption(m_searchString); 0662 } else { 0663 ui->browseView->setParentCategory(m_searchParentCategory); 0664 emit caption(m_searchParentCategory.data().toString()); 0665 #ifndef HAVE_APPSTREAM 0666 if (m_searchGroupCategory.startsWith(QLatin1Char('@')) || 0667 m_searchGroupCategory.startsWith(QLatin1String("repo:"))) { 0668 m_searchTransaction = Daemon::searchGroup(m_searchGroupCategory, m_filtersMenu->filters()); 0669 } 0670 #endif 0671 // else the transaction is useless 0672 } 0673 break; 0674 case Transaction::RoleGetPackages: 0675 // we want all the installed ones 0676 ui->browseView->disableExportInstalledPB(); 0677 m_searchTransaction = Daemon::getPackages(Transaction::FilterInstalled | m_filtersMenu->filters()); 0678 connect(m_searchTransaction, &Transaction::finished, ui->browseView, &BrowseView::enableExportInstalledPB); 0679 emit caption(i18n("Installed Software")); 0680 break; 0681 case Transaction::RoleResolve: 0682 #ifdef HAVE_APPSTREAM 0683 if (!m_searchCategory.isEmpty()) { 0684 ui->browseView->setParentCategory(m_searchParentCategory); 0685 // WARNING the resolve might fail if the backend 0686 // has a low limit MaximumItemsToResolve 0687 m_searchTransaction = Daemon::resolve(m_searchCategory, m_filtersMenu->filters()); 0688 emit caption(m_searchParentCategory.data().toString()); 0689 } else { 0690 ui->browseView->setParentCategory(m_searchParentCategory); 0691 KMessageBox::error(this, i18n("Could not find an application that matched this category")); 0692 emit caption(); 0693 disconnectTransaction(); 0694 m_searchTransaction = 0; 0695 return; 0696 } 0697 break; 0698 #endif 0699 default: 0700 qCWarning(APPER) << "Search type not defined yet"; 0701 emit caption(); 0702 disconnectTransaction(); 0703 m_searchTransaction = nullptr; 0704 return; 0705 } 0706 connect(m_searchTransaction, &Transaction::finished, ui->browseView->busyCursor(), &KPixmapSequenceOverlayPainter::stop); 0707 connect(m_searchTransaction, &Transaction::finished, this, &ApperKCM::finished); 0708 connect(m_searchTransaction, &Transaction::finished, m_browseModel, &PackageModel::finished); 0709 0710 if (ui->browseView->isShowingSizes()) { 0711 connect(m_searchTransaction, &Transaction::finished, m_browseModel, &PackageModel::fetchSizes); 0712 } 0713 connect(m_searchTransaction, &Transaction::package, m_browseModel, &PackageModel::addNotSelectedPackage); 0714 connect(m_searchTransaction, &Transaction::errorCode, this, &ApperKCM::errorCode); 0715 0716 // cleans the models 0717 m_browseModel->clear(); 0718 0719 ui->browseView->showInstalledPanel(m_searchRole == Transaction::RoleGetPackages); 0720 ui->browseView->busyCursor()->start(); 0721 0722 ui->backTB->setEnabled(true); 0723 setCurrentActionCancel(true); 0724 setCurrentActionEnabled(m_searchTransaction->allowCancel()); 0725 0726 ui->stackedWidget->setCurrentWidget(ui->pageBrowse); 0727 } 0728 0729 void ApperKCM::changed() 0730 { 0731 Transaction *trans = qobject_cast<Transaction*>(sender()); 0732 setCurrentActionEnabled(trans->allowCancel()); 0733 } 0734 0735 void ApperKCM::refreshCache() 0736 { 0737 emit changed(false); 0738 0739 QWidget *currentWidget = ui->stackedWidget->currentWidget(); 0740 0741 auto transactionW = new PkTransactionWidget(this); 0742 connect(transactionW, &PkTransactionWidget::titleChangedProgress, this, &ApperKCM::caption); 0743 QPointer<PkTransaction> transaction = new PkTransaction(transactionW); 0744 Daemon::setHints(QLatin1String("cache-age=")+QString::number(m_cacheAge)); 0745 transaction->refreshCache(m_forceRefreshCache); 0746 transactionW->setTransaction(transaction, Transaction::RoleRefreshCache); 0747 0748 ui->stackedWidget->addWidget(transactionW); 0749 ui->stackedWidget->setCurrentWidget(transactionW); 0750 ui->stackedWidgetBar->setCurrentIndex(BAR_TITLE); 0751 ui->backTB->setEnabled(false); 0752 connect(transactionW, &PkTransactionWidget::titleChanged, ui->titleL, &QLabel::setText); 0753 0754 QEventLoop loop; 0755 connect(transaction, &PkTransaction::finished, &loop, &QEventLoop::quit); 0756 0757 // wait for the end of transaction 0758 if (!transaction->isFinished()) { 0759 loop.exec(); 0760 if (!transaction) { 0761 // Avoid crashing 0762 return; 0763 } 0764 0765 // If the refresh failed force next refresh Cache call 0766 m_forceRefreshCache = transaction->exitStatus() == PkTransaction::Failed; 0767 } 0768 0769 if (m_updaterPage) { 0770 m_updaterPage->getUpdates(); 0771 } 0772 0773 if (currentWidget == m_settingsPage) { 0774 setPage(QLatin1String("settings")); 0775 } else { 0776 setPage(QLatin1String("updates")); 0777 } 0778 0779 QTimer::singleShot(0, this, &ApperKCM::checkChanged); 0780 } 0781 0782 void ApperKCM::save() 0783 { 0784 QWidget *currentWidget = ui->stackedWidget->currentWidget(); 0785 if (currentWidget == m_settingsPage) { 0786 m_settingsPage->save(); 0787 } else { 0788 ui->reviewMessage->hide(); 0789 0790 auto transactionW = new PkTransactionWidget(this); 0791 connect(transactionW, &PkTransactionWidget::titleChangedProgress, this, &ApperKCM::caption); 0792 QPointer<PkTransaction> transaction = new PkTransaction(transactionW); 0793 0794 ui->stackedWidget->addWidget(transactionW); 0795 ui->stackedWidget->setCurrentWidget(transactionW); 0796 ui->stackedWidgetBar->setCurrentIndex(BAR_TITLE); 0797 ui->backTB->setEnabled(false); 0798 connect(transactionW, &PkTransactionWidget::titleChanged, ui->titleL, &QLabel::setText); 0799 emit changed(false); 0800 0801 QEventLoop loop; 0802 connect(transaction, &PkTransaction::finished, &loop, &QEventLoop::quit); 0803 if (currentWidget == m_updaterPage) { 0804 transaction->updatePackages(m_updaterPage->packagesToUpdate()); 0805 transactionW->setTransaction(transaction, Transaction::RoleUpdatePackages); 0806 0807 // wait for the end of transaction 0808 if (!transaction->isFinished()) { 0809 loop.exec(); 0810 if (!transaction) { 0811 // Avoid crashing 0812 return; 0813 } 0814 } 0815 } else { 0816 // install then remove packages 0817 QStringList installPackages = m_browseModel->selectedPackagesToInstall(); 0818 if (!installPackages.isEmpty()) { 0819 transaction->installPackages(installPackages); 0820 transactionW->setTransaction(transaction, Transaction::RoleInstallPackages); 0821 0822 // wait for the end of transaction 0823 if (!transaction->isFinished()) { 0824 loop.exec(); 0825 if (!transaction) { 0826 // Avoid crashing 0827 return; 0828 } 0829 } 0830 0831 if (transaction->exitStatus() == PkTransaction::Success) { 0832 m_browseModel->uncheckAvailablePackages(); 0833 } 0834 } 0835 0836 QStringList removePackages = m_browseModel->selectedPackagesToRemove(); 0837 if (!removePackages.isEmpty()) { 0838 transaction->removePackages(removePackages); 0839 transactionW->setTransaction(transaction, Transaction::RoleRemovePackages); 0840 0841 // wait for the end of transaction 0842 if (!transaction->isFinished()) { 0843 loop.exec(); 0844 if (!transaction) { 0845 // Avoid crashing 0846 return; 0847 } 0848 } 0849 0850 if (transaction->exitStatus() == PkTransaction::Success) { 0851 m_browseModel->uncheckInstalledPackages(); 0852 } 0853 } 0854 } 0855 0856 transaction->deleteLater(); 0857 if (currentWidget == m_updaterPage) { 0858 m_updaterPage->getUpdates(); 0859 setPage(QLatin1String("updates")); 0860 } else { 0861 // install then remove packages 0862 search(); 0863 } 0864 QTimer::singleShot(0, this, &ApperKCM::checkChanged); 0865 } 0866 } 0867 0868 void ApperKCM::load() 0869 { 0870 if (ui->stackedWidget->currentWidget() == m_updaterPage) { 0871 m_updaterPage->load(); 0872 } else if (ui->stackedWidget->currentWidget() == m_settingsPage) { 0873 m_settingsPage->load(); 0874 } else { 0875 // set focus on the search lineEdit 0876 ui->searchKLE->setFocus(Qt::OtherFocusReason); 0877 m_browseModel->setAllChecked(false); 0878 } 0879 } 0880 0881 void ApperKCM::defaults() 0882 { 0883 if (ui->stackedWidget->currentWidget() == m_settingsPage) { 0884 m_settingsPage->defaults(); 0885 } 0886 } 0887 0888 void ApperKCM::finished() 0889 { 0890 // if m_currentAction is false means that our 0891 // find button should be disable as there aren't any 0892 // search methods 0893 setCurrentActionEnabled(m_currentAction); 0894 setCurrentActionCancel(false); 0895 m_searchTransaction = nullptr; 0896 } 0897 0898 void ApperKCM::keyPressEvent(QKeyEvent *event) 0899 { 0900 if (ui->searchKLE->hasFocus() && 0901 ui->stackedWidget->currentWidget() != m_history && 0902 (event->key() == Qt::Key_Return || 0903 event->key() == Qt::Key_Enter)) { 0904 // special tab handling here 0905 m_currentAction->trigger(); 0906 return; 0907 } 0908 // KCModule::keyPressEvent(event); 0909 } 0910 0911 void ApperKCM::closeEvent(QCloseEvent *event) 0912 { 0913 // PkTransaction *transaction = qobject_cast<PkTransaction*>(stackedWidget->currentWidget()); 0914 // if (transaction) { 0915 event->ignore(); 0916 // } else { 0917 // event->accept(); 0918 // } 0919 } 0920 0921 #include "moc_ApperKCM.cpp"