File indexing completed on 2024-04-28 09:40:20
0001 /*************************************************************************** 0002 * Copyright (C) 2008-2018 by Daniel Nicoletti * 0003 * dantti12@gmail.com * 0004 * Copyright (C) 2008 by Trever Fischer * 0005 * wm161@wm161.net * 0006 * * 0007 * This program is free software; you can redistribute it and/or modify * 0008 * it under the terms of the GNU General Public License as published by * 0009 * the Free Software Foundation; either version 2 of the License, or * 0010 * (at your option) any later version. * 0011 * * 0012 * This program is distributed in the hope that it will be useful, * 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0015 * GNU General Public License for more details. * 0016 * * 0017 * You should have received a copy of the GNU General Public License * 0018 * along with this program; see the file COPYING. If not, write to * 0019 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0020 * Boston, MA 02110-1301, USA. * 0021 ***************************************************************************/ 0022 0023 #include <config.h> 0024 0025 #include "PackageModel.h" 0026 #include <PkStrings.h> 0027 0028 #include <Daemon> 0029 0030 #include <QPainter> 0031 0032 #include <KIconLoader> 0033 #include <QLoggingCategory> 0034 #include <PkIcons.h> 0035 #include <KLocalizedString> 0036 #include <KCategorizedSortFilterProxyModel> 0037 #include <KFormat> 0038 0039 #ifdef HAVE_APPSTREAM 0040 #include <AppStreamQt5/icon.h> 0041 #include <AppStream.h> 0042 #endif 0043 0044 #define ICON_SIZE 22 0045 #define OVERLAY_SIZE 16 0046 0047 using namespace PackageKit; 0048 0049 Q_DECLARE_LOGGING_CATEGORY(APPER_LIB) 0050 0051 PackageModel::PackageModel(QObject *parent) 0052 : QAbstractItemModel(parent), 0053 m_finished(false), 0054 m_checkable(false), 0055 m_fetchSizesTransaction(nullptr), 0056 m_fetchInstalledVersionsTransaction(nullptr) 0057 { 0058 m_installedEmblem = PkIcons::getIcon(QLatin1String("dialog-ok-apply"), QString()).pixmap(16, 16); 0059 0060 m_roles[SortRole] = "rSort"; 0061 m_roles[NameRole] = "rName"; 0062 m_roles[SummaryRole] = "rSummary"; 0063 m_roles[VersionRole] = "rVersion"; 0064 m_roles[ArchRole] = "rArch"; 0065 m_roles[IconRole] = "rIcon"; 0066 m_roles[IdRole] = "rId"; 0067 m_roles[CheckStateRole] = "rChecked"; 0068 m_roles[InfoRole] = "rInfo"; 0069 m_roles[ApplicationId] = "rApplicationId"; 0070 m_roles[IsPackageRole] = "rIsPackageRole"; 0071 m_roles[PackageName] = "rPackageName"; 0072 m_roles[InfoIconRole] = "rInfoIcon"; 0073 } 0074 0075 void PackageModel::addSelectedPackagesFromModel(PackageModel *model) 0076 { 0077 const QList<InternalPackage> packages = model->internalSelectedPackages(); 0078 for (const InternalPackage &package : packages) { 0079 addPackage(package.info, package.packageID, package.summary, true); 0080 } 0081 finished(); 0082 } 0083 0084 void PackageModel::addNotSelectedPackage(Transaction::Info info, const QString &packageID, const QString &summary) 0085 { 0086 addPackage(info, packageID, summary); 0087 } 0088 0089 void PackageModel::addPackage(Transaction::Info info, const QString &packageID, const QString &summary, bool selected) 0090 { 0091 if (m_finished) { 0092 qDebug() << Q_FUNC_INFO << "we are finished calling clear"; 0093 clear(); 0094 } 0095 0096 switch(info) { 0097 case Transaction::InfoBlocked: 0098 case Transaction::InfoFinished: 0099 case Transaction::InfoCleanup: 0100 return; 0101 default: 0102 break; 0103 } 0104 0105 #ifdef HAVE_APPSTREAM 0106 QList<AppStream::Component> applications; 0107 if (!m_checkable) { 0108 const QString packageName = Transaction::packageName(packageID); 0109 applications = AppStreamHelper::instance()->applications(packageName); 0110 0111 for (const AppStream::Component &app : applications) { 0112 InternalPackage iPackage; 0113 iPackage.info = info; 0114 iPackage.packageID = packageID; 0115 iPackage.pkgName = packageName; 0116 iPackage.version = Transaction::packageVersion(packageID); 0117 iPackage.arch = Transaction::packageArch(packageID); 0118 iPackage.repo = Transaction::packageData(packageID); 0119 iPackage.isPackage = false; 0120 if (app.name().isEmpty()) { 0121 iPackage.displayName = packageName; 0122 } else { 0123 iPackage.displayName = app.name(); 0124 } 0125 if (app.summary().isEmpty()) { 0126 iPackage.summary = summary; 0127 } else { 0128 iPackage.summary = app.summary(); 0129 } 0130 0131 const QList<AppStream::Icon> icons = app.icons(); 0132 for (const AppStream::Icon &icon : icons) { 0133 if (icon.url().isEmpty()) { 0134 iPackage.icon = icon.name(); 0135 } else { 0136 iPackage.icon = icon.url().toLocalFile(); 0137 } 0138 break; 0139 } 0140 iPackage.appId = app.id(); 0141 iPackage.size = 0; 0142 0143 if (selected) { 0144 checkPackage(iPackage, false); 0145 } 0146 m_packages.append(iPackage); 0147 } 0148 } 0149 0150 if (applications.isEmpty()) { 0151 #endif //HAVE_APPSTREAM 0152 0153 InternalPackage iPackage; 0154 iPackage.info = info; 0155 iPackage.packageID = packageID; 0156 iPackage.pkgName = Transaction::packageName(packageID); 0157 iPackage.displayName = iPackage.pkgName; 0158 iPackage.version = Transaction::packageVersion(packageID); 0159 iPackage.arch = Transaction::packageArch(packageID); 0160 iPackage.repo = Transaction::packageData(packageID); 0161 iPackage.summary = summary; 0162 0163 #ifdef HAVE_APPSTREAM 0164 iPackage.icon = AppStreamHelper::instance()->genericIcon(iPackage.pkgName); 0165 0166 if (m_checkable) { 0167 // in case of updates model only check if it's an app 0168 applications = AppStreamHelper::instance()->applications(iPackage.pkgName); 0169 if (!applications.isEmpty()) { 0170 iPackage.isPackage = false; 0171 } 0172 } 0173 #endif // HAVE_APPSTREAM 0174 0175 if (selected) { 0176 checkPackage(iPackage, false); 0177 } 0178 m_packages.append(iPackage); 0179 0180 #ifdef HAVE_APPSTREAM 0181 } 0182 #endif // HAVE_APPSTREAM 0183 } 0184 0185 void PackageModel::addSelectedPackage(Transaction::Info info, const QString &packageID, const QString &summary) 0186 { 0187 addPackage(info, packageID, summary, true); 0188 } 0189 0190 QVariant PackageModel::headerData(int section, Qt::Orientation orientation, int role) const 0191 { 0192 QVariant ret; 0193 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { 0194 switch (section) { 0195 case NameCol: 0196 if (m_checkable) { 0197 ret = PkStrings::packageQuantity(true, 0198 m_packages.size(), 0199 m_checkedPackages.size()); 0200 } else { 0201 ret = i18n("Name"); 0202 } 0203 break; 0204 case VersionCol: 0205 ret = i18n("Version"); 0206 break; 0207 case CurrentVersionCol: 0208 ret = i18n("Installed Version"); 0209 break; 0210 case ArchCol: 0211 ret = i18n("Arch"); 0212 break; 0213 case OriginCol: 0214 ret = i18n("Origin"); 0215 break; 0216 case SizeCol: 0217 ret = i18n("Size"); 0218 break; 0219 case ActionCol: 0220 ret = i18n("Action"); 0221 break; 0222 } 0223 } 0224 return ret; 0225 } 0226 0227 int PackageModel::rowCount(const QModelIndex &parent) const 0228 { 0229 if (parent.isValid() || !m_finished) { 0230 return 0; 0231 } 0232 return m_packages.size(); 0233 } 0234 0235 QModelIndex PackageModel::index(int row, int column, const QModelIndex &parent) const 0236 { 0237 // kDebug() << parent.isValid() << m_packageCount << row << column; 0238 // Check to see if the index isn't out of list 0239 if (!parent.isValid() && m_packages.size() > row) { 0240 return createIndex(row, column); 0241 } 0242 return QModelIndex(); 0243 } 0244 0245 QModelIndex PackageModel::parent(const QModelIndex &index) const 0246 { 0247 Q_UNUSED(index) 0248 return QModelIndex(); 0249 } 0250 0251 QHash<int, QByteArray> PackageModel::roleNames() const 0252 { 0253 return m_roles; 0254 } 0255 0256 QVariant PackageModel::data(const QModelIndex &index, int role) const 0257 { 0258 if (!index.isValid()) { 0259 return QVariant(); 0260 } 0261 0262 const InternalPackage &package = m_packages[index.row()]; 0263 0264 if (index.column() == NameCol) { 0265 switch (role) { 0266 case Qt::CheckStateRole: 0267 if (!m_checkable) { 0268 return QVariant(); 0269 } 0270 if (containsChecked(package.packageID)) { 0271 return Qt::Checked; 0272 } 0273 return Qt::Unchecked; 0274 case CheckStateRole: 0275 if (containsChecked(package.packageID)) { 0276 return Qt::Checked; 0277 } 0278 return Qt::Unchecked; 0279 case IsPackageRole: 0280 return package.isPackage; 0281 case Qt::DisplayRole: 0282 return package.displayName; 0283 case Qt::DecorationRole: 0284 { 0285 QPixmap icon = QPixmap(44, ICON_SIZE); 0286 icon.fill(Qt::transparent); 0287 if (!package.icon.isNull()) { 0288 QPixmap pixmap; 0289 if (package.icon.startsWith(QLatin1String("/"))) { 0290 pixmap = QPixmap(); 0291 pixmap.load(package.icon); 0292 pixmap = pixmap.scaledToHeight(ICON_SIZE); 0293 } else { 0294 pixmap = KIconLoader::global()->loadIcon(package.icon, 0295 KIconLoader::NoGroup, 0296 ICON_SIZE, 0297 KIconLoader::DefaultState, 0298 QStringList(), 0299 nullptr, 0300 true); 0301 } 0302 0303 if (!pixmap.isNull()) { 0304 QPainter painter(&icon); 0305 painter.drawPixmap(QPoint(2, 0), pixmap); 0306 } 0307 } 0308 0309 if (package.info == Transaction::InfoInstalled || 0310 package.info == Transaction::InfoCollectionInstalled) { 0311 QPainter painter(&icon); 0312 QPoint startPoint; 0313 // bottom right corner 0314 startPoint = QPoint(44 - OVERLAY_SIZE, 4); 0315 painter.drawPixmap(startPoint, m_installedEmblem); 0316 } else if (m_checkable) { 0317 QIcon emblemIcon = PkIcons::packageIcon(package.info); 0318 QPainter painter(&icon); 0319 QPoint startPoint; 0320 // bottom right corner 0321 startPoint = QPoint(44 - OVERLAY_SIZE, 4); 0322 painter.drawPixmap(startPoint, emblemIcon.pixmap(OVERLAY_SIZE, OVERLAY_SIZE)); 0323 } 0324 return icon; 0325 } 0326 case PackageName: 0327 return package.pkgName; 0328 case Qt::ToolTipRole: 0329 if (m_checkable) { 0330 return PkStrings::info(package.info); 0331 } else { 0332 return i18n("Version: %1\nArchitecture: %2", package.version, package.arch); 0333 } 0334 } 0335 } else if (role == Qt::DisplayRole) { 0336 if (index.column() == VersionCol) { 0337 return package.version; 0338 } else if (index.column() == CurrentVersionCol) { 0339 return package.currentVersion; 0340 } else if (index.column() == ArchCol) { 0341 return package.arch; 0342 } else if (index.column() == OriginCol) { 0343 return package.repo; 0344 } else if (index.column() == SizeCol) { 0345 KFormat f; 0346 return package.size ? f.formatByteSize(package.size) : QString(); 0347 } 0348 } else if (index.column() == SizeCol && role == Qt::TextAlignmentRole) { 0349 return static_cast<int>(Qt::AlignRight | Qt::AlignVCenter); 0350 } 0351 0352 switch (role) { 0353 case IconRole: 0354 return package.icon; 0355 case SortRole: 0356 return QString(package.displayName % QLatin1Char(' ') % package.version % QLatin1Char(' ') % package.arch); 0357 case CheckStateRole: 0358 if (containsChecked(package.packageID)) { 0359 return Qt::Checked; 0360 } 0361 return Qt::Unchecked; 0362 case IdRole: 0363 return package.packageID; 0364 case NameRole: 0365 return package.displayName; 0366 case SummaryRole: 0367 return package.summary; 0368 case VersionRole: 0369 return package.version; 0370 case ArchRole: 0371 return package.arch; 0372 case OriginCol: 0373 return package.repo; 0374 case InfoRole: 0375 return qVariantFromValue(package.info); 0376 case KCategorizedSortFilterProxyModel::CategoryDisplayRole: 0377 if (package.info == Transaction::InfoInstalled || 0378 package.info == Transaction::InfoCollectionInstalled) { 0379 return i18n("To be Removed"); 0380 } else { 0381 return i18n("To be Installed"); 0382 } 0383 case KCategorizedSortFilterProxyModel::CategorySortRole: 0384 // USING 0 here seems to let things unsorted 0385 return package.isPackage ? 1 : 0; // Packages comes after applications 0386 case ApplicationId: 0387 return package.appId; 0388 case InfoIconRole: 0389 return PkIcons::packageIcon(package.info); 0390 default: 0391 return QVariant(); 0392 } 0393 0394 return QVariant(); 0395 } 0396 0397 bool PackageModel::setData(const QModelIndex &index, const QVariant &value, int role) 0398 { 0399 if (role == Qt::CheckStateRole && m_packages.size() > index.row()) { 0400 if (value.toBool()) { 0401 checkPackage(m_packages[index.row()]); 0402 } else { 0403 uncheckPackage(m_packages[index.row()].packageID); 0404 } 0405 0406 emit changed(!m_checkedPackages.isEmpty()); 0407 0408 return true; 0409 } 0410 return false; 0411 } 0412 0413 Qt::ItemFlags PackageModel::flags(const QModelIndex &index) const 0414 { 0415 if (index.column() == NameCol) { 0416 return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | QAbstractItemModel::flags(index); 0417 } 0418 return QAbstractItemModel::flags(index); 0419 } 0420 0421 int PackageModel::columnCount(const QModelIndex &parent) const 0422 { 0423 Q_UNUSED(parent); 0424 if (m_checkable) { 0425 // when the model is checkable the action column is not shown 0426 return ActionCol; 0427 } else { 0428 return ActionCol + 1; 0429 } 0430 } 0431 0432 void PackageModel::removePackage(const QString &packageID) 0433 { 0434 int i = 0; 0435 while (i < m_packages.size()) { 0436 InternalPackage iPackage = m_packages[i]; 0437 if (iPackage.packageID == packageID && 0438 iPackage.info != Transaction::InfoUntrusted) { 0439 beginRemoveRows(QModelIndex(), i, i); 0440 m_packages.remove(i); 0441 endRemoveRows(); 0442 0443 // since we removed one entry we don't 0444 // need to increase the counter 0445 continue; 0446 } 0447 ++i; 0448 } 0449 } 0450 0451 void PackageModel::checkAll() 0452 { 0453 m_checkedPackages.clear(); 0454 for (const InternalPackage &package : qAsConst(m_packages)) { 0455 checkPackage(package, false); 0456 } 0457 emit dataChanged(createIndex(0, 0), 0458 createIndex(m_packages.size(), 0)); 0459 emit changed(!m_checkedPackages.isEmpty()); 0460 } 0461 0462 void PackageModel::clear() 0463 { 0464 qDebug() << Q_FUNC_INFO; 0465 beginRemoveRows(QModelIndex(), 0, m_packages.size()); 0466 m_finished = false; 0467 m_packages.clear(); 0468 m_fetchSizesTransaction = nullptr; 0469 m_fetchInstalledVersionsTransaction = nullptr; 0470 0471 if (m_getUpdatesTransaction) { 0472 m_getUpdatesTransaction->disconnect(this); 0473 m_getUpdatesTransaction->cancel(); 0474 } 0475 endRemoveRows(); 0476 } 0477 0478 void PackageModel::clearSelectedNotPresent() 0479 { 0480 auto it = m_checkedPackages.begin(); 0481 while (it != m_checkedPackages.end()) { 0482 const InternalPackage &package = it.value(); 0483 0484 bool notFound = true; 0485 for (const InternalPackage &iPackage : qAsConst(m_packages)) { 0486 if (iPackage.packageID == package.packageID) { 0487 notFound = false; 0488 break; 0489 } 0490 } 0491 0492 if (notFound) { 0493 // Uncheck the package If it's not in the model 0494 it = m_checkedPackages.erase(it); 0495 uncheckPackageLogic(package.packageID); 0496 } else { 0497 ++it; 0498 } 0499 } 0500 } 0501 0502 bool PackageModel::checkable() const 0503 { 0504 return m_checkable; 0505 } 0506 0507 void PackageModel::uncheckInstalledPackages() 0508 { 0509 auto it = m_checkedPackages.begin(); 0510 while (it != m_checkedPackages.end()) { 0511 const InternalPackage &package = it.value(); 0512 if (package.info == Transaction::InfoInstalled || 0513 package.info == Transaction::InfoCollectionInstalled) { 0514 const QString pkgId = it.key(); 0515 it = m_checkedPackages.erase(it); 0516 uncheckPackageLogic(pkgId, true); 0517 } else { 0518 ++it; 0519 } 0520 } 0521 } 0522 0523 void PackageModel::uncheckAvailablePackages() 0524 { 0525 auto it = m_checkedPackages.begin(); 0526 while (it != m_checkedPackages.end()) { 0527 const InternalPackage &package = it.value(); 0528 if (package.info == Transaction::InfoAvailable || 0529 package.info == Transaction::InfoCollectionAvailable) { 0530 const QString pkgId = it.key(); 0531 it = m_checkedPackages.erase(it); 0532 uncheckPackageLogic(pkgId, true); 0533 } else { 0534 ++it; 0535 } 0536 } 0537 } 0538 0539 void PackageModel::finished() 0540 { 0541 auto trans = qobject_cast<Transaction*>(sender()); 0542 qDebug() << Q_FUNC_INFO << trans << sender(); 0543 if (trans /*== m_getUpdatesTransaction*/) { 0544 // m_getUpdatesTransaction = 0; 0545 // When pkd dies this method is called twice 0546 // pk-qt2 bug.. 0547 disconnect(trans, &Transaction::finished, this, &PackageModel::finished); 0548 } 0549 0550 // The whole structure is about to change 0551 if (!m_packages.isEmpty()) { 0552 beginInsertRows(QModelIndex(), 0, m_packages.size() - 1); 0553 m_finished = true; 0554 endInsertRows(); 0555 } 0556 0557 emit changed(!m_checkedPackages.isEmpty()); 0558 } 0559 0560 void PackageModel::fetchSizes() 0561 { 0562 if (m_fetchSizesTransaction) { 0563 return; 0564 } 0565 0566 // get package size 0567 QStringList pkgs; 0568 for (const InternalPackage &p : qAsConst(m_packages)) { 0569 pkgs << p.packageID; 0570 } 0571 if (!pkgs.isEmpty()) { 0572 m_fetchSizesTransaction = Daemon::getDetails(pkgs); 0573 connect(m_fetchSizesTransaction, &Transaction::details, this, &PackageModel::updateSize); 0574 connect(m_fetchSizesTransaction, &Transaction::finished, this, &PackageModel::fetchSizesFinished); 0575 } 0576 } 0577 0578 void PackageModel::fetchSizesFinished() 0579 { 0580 auto trans = qobject_cast<Transaction*>(sender()); 0581 if (trans) { 0582 // When pkd dies this method is called twice 0583 // pk-qt2 bug.. 0584 disconnect(trans, &Transaction::finished, this, &PackageModel::fetchSizesFinished); 0585 } 0586 // emit this after all is changed otherwise on large models it will 0587 // be hell slow... 0588 emit dataChanged(createIndex(0, SizeCol), createIndex(m_packages.size(), SizeCol)); 0589 emit changed(!m_checkedPackages.isEmpty()); 0590 } 0591 0592 void PackageModel::updateSize(const PackageKit::Details &details) 0593 { 0594 // if size is 0 don't waste time looking for the package 0595 qulonglong size = details.size(); 0596 if (size == 0) { 0597 return; 0598 } 0599 0600 for (int i = 0; i < m_packages.size(); ++i) { 0601 const QString packageId = details.packageId(); 0602 if (packageId == m_packages[i].packageID) { 0603 m_packages[i].size = size; 0604 if (m_checkable) { 0605 // updates the checked packages as well 0606 if (m_checkedPackages.contains(packageId)) { 0607 // Avoid checking packages that aren't checked 0608 m_checkedPackages[packageId].size = size; 0609 } 0610 break; 0611 } 0612 0613 #ifdef HAVE_APPSTREAM 0614 if (m_checkable) { 0615 // checkable models don't have duplicated package ids 0616 // so don't waste time scanning all list 0617 break; 0618 } 0619 #else 0620 // Without AppStream we don't have duplicated package ids 0621 break; 0622 #endif // HAVE_APPSTREAM 0623 } 0624 } 0625 } 0626 0627 void PackageModel::fetchCurrentVersions() 0628 { 0629 if (m_fetchInstalledVersionsTransaction) { 0630 return; 0631 } 0632 0633 // get package current version 0634 QStringList pkgs; 0635 for (const InternalPackage &p : qAsConst(m_packages)) { 0636 pkgs << p.pkgName; 0637 } 0638 0639 if (!pkgs.isEmpty()) { 0640 m_fetchInstalledVersionsTransaction = Daemon::resolve(pkgs, Transaction::FilterInstalled);; 0641 connect(m_fetchInstalledVersionsTransaction, &Transaction::package, this, &PackageModel::updateCurrentVersion); 0642 connect(m_fetchInstalledVersionsTransaction, &Transaction::finished, this, &PackageModel::fetchCurrentVersionsFinished); 0643 } 0644 } 0645 0646 void PackageModel::fetchCurrentVersionsFinished() 0647 { 0648 auto trans = qobject_cast<Transaction*>(sender()); 0649 if (trans) { 0650 // When pkd dies this method is called twice 0651 // pk-qt2 bug.. 0652 disconnect(trans, &Transaction::finished, this, &PackageModel::fetchCurrentVersionsFinished); 0653 } 0654 // emit this after all is changed otherwise on large models it will 0655 // be hell slow... 0656 emit dataChanged(createIndex(0, CurrentVersionCol), createIndex(m_packages.size(), CurrentVersionCol)); 0657 emit changed(!m_checkedPackages.isEmpty()); 0658 } 0659 0660 void PackageModel::updateCurrentVersion(Transaction::Info info, const QString &packageID, const QString &summary) 0661 { 0662 Q_UNUSED(info) 0663 Q_UNUSED(summary) 0664 // if current version is empty don't waste time looking 0665 if (!Transaction::packageVersion(packageID).isEmpty()) { 0666 for (int i = 0; i < m_packages.size(); ++i) { 0667 if (Transaction::packageName(packageID) == m_packages[i].pkgName && 0668 Transaction::packageArch(packageID) == m_packages[i].arch) { 0669 m_packages[i].currentVersion = Transaction::packageVersion(packageID); 0670 if (m_checkable) { 0671 // updates the checked packages as well 0672 if (m_checkedPackages.contains(m_packages[i].packageID)) { 0673 // Avoid checking packages that aren't checked 0674 m_checkedPackages[m_packages[i].packageID].currentVersion = Transaction::packageVersion(packageID); 0675 } 0676 break; 0677 } 0678 } 0679 } 0680 } 0681 } 0682 0683 void PackageModel::getUpdates(bool fetchCurrentVersions, bool selected) 0684 { 0685 clear(); 0686 m_getUpdatesTransaction = Daemon::getUpdates(); 0687 if (selected) { 0688 connect(m_getUpdatesTransaction, &Transaction::package, this, &PackageModel::addSelectedPackage); 0689 } else { 0690 connect(m_getUpdatesTransaction, &Transaction::package, this, &PackageModel::addNotSelectedPackage); 0691 } 0692 // connect(m_getUpdatesTransaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), 0693 // m_busySeq, SLOT(stop())); 0694 // connect(m_getUpdatesTransaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), 0695 // this, SLOT(finished())); 0696 // This is required to estimate download size 0697 connect(m_getUpdatesTransaction, &Transaction::finished, this, &PackageModel::fetchSizes); 0698 0699 if (fetchCurrentVersions) { 0700 connect(m_getUpdatesTransaction, &Transaction::finished, this, &PackageModel::fetchCurrentVersions); 0701 } 0702 0703 connect(m_getUpdatesTransaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)), 0704 this, SLOT(getUpdatesFinished())); 0705 // get all updates 0706 } 0707 0708 void PackageModel::toggleSelection(const QString &packageID) 0709 { 0710 if (containsChecked(packageID)) { 0711 uncheckPackage(packageID, true); 0712 } else { 0713 for (const InternalPackage &package : qAsConst(m_packages)) { 0714 if (package.packageID == packageID) { 0715 checkPackage(package); 0716 break; 0717 } 0718 } 0719 } 0720 } 0721 0722 QString PackageModel::selectionStateText() const 0723 { 0724 return headerData(NameCol, Qt::Horizontal).toString(); 0725 } 0726 0727 bool PackageModel::hasChanges() const 0728 { 0729 return !m_checkedPackages.isEmpty(); 0730 } 0731 0732 int PackageModel::countInfo(PackageKit::Transaction::Info info) const 0733 { 0734 int ret = 0; 0735 for (const InternalPackage &package : qAsConst(m_packages)) { 0736 if (package.info == info) { 0737 ++ret; 0738 } 0739 } 0740 return ret; 0741 } 0742 0743 void PackageModel::checkPackage(const InternalPackage &package, bool emitDataChanged) 0744 { 0745 QString pkgId = package.packageID; 0746 if (!containsChecked(pkgId)) { 0747 m_checkedPackages[pkgId] = package; 0748 0749 // A checkable model does not have duplicated entries 0750 if (emitDataChanged || !m_checkable || !m_packages.isEmpty()) { 0751 // This is a slow operation so in case the user 0752 // is unchecking all of the packages there is 0753 // no need to emit data changed for every item 0754 for (int i = 0; i < m_packages.size(); ++i) { 0755 if (m_packages[i].packageID == pkgId) { 0756 QModelIndex index = createIndex(i, 0); 0757 emit dataChanged(index, index); 0758 } 0759 } 0760 0761 // The model might not be displayed yet 0762 if (m_finished) { 0763 emit changed(!m_checkedPackages.isEmpty()); 0764 } 0765 } 0766 } 0767 } 0768 0769 void PackageModel::uncheckAll() 0770 { 0771 auto it = m_checkedPackages.begin(); 0772 while (it != m_checkedPackages.end()) { 0773 const QString pkgId = it.key(); 0774 it = m_checkedPackages.erase(it); 0775 uncheckPackageLogic(pkgId, true, false); 0776 } 0777 emit dataChanged(createIndex(0, 0), 0778 createIndex(m_packages.size(), 0)); 0779 emit changed(!m_checkedPackages.isEmpty()); 0780 } 0781 0782 void PackageModel::uncheckPackageDefault(const QString &packageID) 0783 { 0784 uncheckPackage(packageID); 0785 } 0786 0787 void PackageModel::uncheckPackage(const QString &packageID, 0788 bool forceEmitUnchecked, 0789 bool emitDataChanged) 0790 { 0791 auto it = m_checkedPackages.find(packageID); 0792 if (it != m_checkedPackages.end()) { 0793 m_checkedPackages.erase(it); 0794 uncheckPackageLogic(packageID, forceEmitUnchecked, emitDataChanged); 0795 } 0796 } 0797 0798 void PackageModel::uncheckPackageLogic(const QString &packageID, bool forceEmitUnchecked, bool emitDataChanged) 0799 { 0800 if (forceEmitUnchecked || sender() == nullptr) { 0801 // The package might be removed by rmSelectedPackage 0802 // If we don't copy it the browse model won't uncheck there 0803 // right package 0804 emit packageUnchecked(packageID); 0805 } 0806 0807 if (emitDataChanged || !m_checkable) { 0808 // This is a slow operation so in case the user 0809 // is unchecking all of the packages there is 0810 // no need to emit data changed for every item 0811 for (int i = 0; i < m_packages.size(); ++i) { 0812 if (m_packages[i].packageID == packageID) { 0813 QModelIndex index = createIndex(i, 0); 0814 emit dataChanged(index, index); 0815 } 0816 } 0817 0818 // The model might not be displayed yet 0819 if (m_finished) { 0820 emit changed(!m_checkedPackages.isEmpty()); 0821 } 0822 } 0823 } 0824 0825 QList<PackageModel::InternalPackage> PackageModel::internalSelectedPackages() const 0826 { 0827 QList<InternalPackage> ret; 0828 QHash<QString, InternalPackage>::const_iterator i = m_checkedPackages.constBegin(); 0829 while (i != m_checkedPackages.constEnd()) { 0830 ret << i.value(); 0831 ++i; 0832 } 0833 return ret; 0834 } 0835 0836 bool PackageModel::containsChecked(const QString &pid) const 0837 { 0838 return m_checkedPackages.contains(pid); 0839 } 0840 0841 void PackageModel::setAllChecked(bool checked) 0842 { 0843 if (checked) { 0844 checkAll(); 0845 } else { 0846 uncheckAll(); 0847 } 0848 } 0849 0850 QStringList PackageModel::selectedPackagesToInstall() const 0851 { 0852 QStringList list; 0853 for (const InternalPackage &package : qAsConst(m_checkedPackages)) { 0854 if (package.info != Transaction::InfoInstalled && 0855 package.info != Transaction::InfoCollectionInstalled) { 0856 // append the packages are not installed 0857 list << package.packageID; 0858 } 0859 } 0860 return list; 0861 } 0862 0863 QStringList PackageModel::selectedPackagesToRemove() const 0864 { 0865 QStringList list; 0866 for (const InternalPackage &package : qAsConst(m_checkedPackages)) { 0867 if (package.info == Transaction::InfoInstalled || 0868 package.info == Transaction::InfoCollectionInstalled) { 0869 // check what packages are installed and marked to be removed 0870 list << package.packageID; 0871 } 0872 } 0873 return list; 0874 } 0875 0876 QStringList PackageModel::packagesWithInfo(Transaction::Info info) const 0877 { 0878 QStringList list; 0879 for (const InternalPackage &package : qAsConst(m_packages)) { 0880 if (package.info == info) { 0881 // Append to the list if the package matches the info value 0882 list << package.packageID; 0883 } 0884 } 0885 return list; 0886 } 0887 0888 QStringList PackageModel::packageIDs() const 0889 { 0890 QStringList list; 0891 for (const InternalPackage &package : qAsConst(m_packages)) { 0892 list << package.packageID; 0893 } 0894 return list; 0895 } 0896 0897 unsigned long PackageModel::downloadSize() const 0898 { 0899 unsigned long size = 0; 0900 for (const InternalPackage &package : qAsConst(m_checkedPackages)) { 0901 size += package.size; 0902 } 0903 return size; 0904 } 0905 0906 bool PackageModel::allSelected() const 0907 { 0908 for (const InternalPackage &package : qAsConst(m_packages)) { 0909 if (!containsChecked(package.packageID)) { 0910 return false; 0911 } 0912 } 0913 return true; 0914 } 0915 0916 void PackageModel::setCheckable(bool checkable) 0917 { 0918 m_checkable = checkable; 0919 } 0920 0921 #include "moc_PackageModel.cpp"