File indexing completed on 2024-04-14 15:48:54

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"