Warning, file /plasma/plasma-mobile/containments/homescreens/halcyon/pinnedmodel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 // SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
0002 // SPDX-License-Identifier: GPL-2.0-or-later
0003 
0004 #include "pinnedmodel.h"
0005 
0006 #include <QJsonArray>
0007 #include <QJsonDocument>
0008 
0009 #include <KLocalizedString>
0010 
0011 PinnedModel::PinnedModel(QObject *parent, Plasma::Applet *applet)
0012     : QAbstractListModel{parent}
0013     , m_applet{applet}
0014 {
0015 }
0016 
0017 PinnedModel::~PinnedModel() = default;
0018 
0019 int PinnedModel::rowCount(const QModelIndex &parent) const
0020 {
0021     return m_applications.count();
0022 }
0023 
0024 QVariant PinnedModel::data(const QModelIndex &index, int role) const
0025 {
0026     if (!index.isValid()) {
0027         return QVariant();
0028     }
0029 
0030     switch (role) {
0031     case IsFolderRole:
0032         return m_folders.at(index.row()) != nullptr;
0033     case ApplicationRole:
0034         return QVariant::fromValue(m_applications.at(index.row()));
0035     case FolderRole:
0036         return QVariant::fromValue(m_folders.at(index.row()));
0037     }
0038 
0039     return QVariant();
0040 }
0041 
0042 QHash<int, QByteArray> PinnedModel::roleNames() const
0043 {
0044     return {{IsFolderRole, "isFolder"}, {ApplicationRole, "application"}, {FolderRole, "folder"}};
0045 }
0046 
0047 void PinnedModel::addApp(const QString &storageId, int row)
0048 {
0049     if (row < 0 || row > m_applications.size()) {
0050         return;
0051     }
0052 
0053     if (KService::Ptr service = KService::serviceByStorageId(storageId)) {
0054         Application *app = new Application(this, service);
0055 
0056         beginInsertRows(QModelIndex(), row, row);
0057         m_applications.insert(row, app);
0058         m_folders.insert(row, nullptr); // maintain indicies
0059         endInsertRows();
0060 
0061         save();
0062     }
0063 }
0064 
0065 void PinnedModel::addFolder(QString name, int row)
0066 {
0067     if (row < 0 || row > m_applications.size()) {
0068         return;
0069     }
0070 
0071     ApplicationFolder *folder = new ApplicationFolder(this, name);
0072     connect(folder, &ApplicationFolder::saveRequested, this, &PinnedModel::save);
0073     connect(folder, &ApplicationFolder::moveAppOutRequested, this, &PinnedModel::addAppFromFolder);
0074 
0075     beginInsertRows(QModelIndex(), row, row);
0076     m_applications.insert(row, nullptr);
0077     m_folders.insert(row, folder);
0078     endInsertRows();
0079 
0080     save();
0081 }
0082 
0083 void PinnedModel::removeEntry(int row)
0084 {
0085     if (row < 0 || row >= m_applications.size()) {
0086         return;
0087     }
0088 
0089     beginRemoveRows(QModelIndex(), row, row);
0090     if (m_folders[row]) {
0091         m_folders[row]->deleteLater();
0092     }
0093     if (m_applications[row]) {
0094         m_applications[row]->deleteLater();
0095     }
0096     m_applications.removeAt(row);
0097     m_folders.removeAt(row);
0098     endRemoveRows();
0099 
0100     save();
0101 }
0102 
0103 void PinnedModel::moveEntry(int fromRow, int toRow)
0104 {
0105     if (fromRow < 0 || toRow < 0 || fromRow >= m_applications.length() || toRow >= m_applications.length() || fromRow == toRow) {
0106         return;
0107     }
0108     if (toRow > fromRow) {
0109         ++toRow;
0110     }
0111 
0112     beginMoveRows(QModelIndex(), fromRow, fromRow, QModelIndex(), toRow);
0113     if (toRow > fromRow) {
0114         Application *app = m_applications.at(fromRow);
0115         m_applications.insert(toRow, app);
0116         m_applications.takeAt(fromRow);
0117 
0118         ApplicationFolder *folder = m_folders.at(fromRow);
0119         m_folders.insert(toRow, folder);
0120         m_folders.takeAt(fromRow);
0121 
0122     } else {
0123         Application *app = m_applications.takeAt(fromRow);
0124         m_applications.insert(toRow, app);
0125 
0126         ApplicationFolder *folder = m_folders.takeAt(fromRow);
0127         m_folders.insert(toRow, folder);
0128     }
0129     endMoveRows();
0130 
0131     save();
0132 
0133     // HACK: didn't seem to persist
0134     m_applet->config().sync();
0135 }
0136 
0137 void PinnedModel::createFolderFromApps(int sourceAppRow, int draggedAppRow)
0138 {
0139     if (sourceAppRow < 0 || sourceAppRow >= m_applications.size() || draggedAppRow < 0 || draggedAppRow >= m_applications.size()) {
0140         return;
0141     }
0142 
0143     if (sourceAppRow == draggedAppRow || !m_applications[sourceAppRow] || !m_applications[draggedAppRow]) {
0144         return;
0145     }
0146 
0147     // replace source app with folder containing both apps
0148     ApplicationFolder *folder = new ApplicationFolder(this, i18nc("Default application folder name.", "Folder"));
0149     connect(folder, &ApplicationFolder::saveRequested, this, &PinnedModel::save);
0150     connect(folder, &ApplicationFolder::moveAppOutRequested, this, &PinnedModel::addAppFromFolder);
0151 
0152     folder->addApp(m_applications[sourceAppRow]->storageId(), 0);
0153     folder->addApp(m_applications[draggedAppRow]->storageId(), 0);
0154 
0155     m_applications[sourceAppRow]->deleteLater();
0156     m_applications[sourceAppRow] = nullptr;
0157     m_folders[sourceAppRow] = folder;
0158 
0159     Q_EMIT dataChanged(index(sourceAppRow, 0), index(sourceAppRow, 0), {IsFolderRole, ApplicationRole, FolderRole});
0160     save();
0161 
0162     // remove dragged app after
0163     removeEntry(draggedAppRow);
0164 }
0165 
0166 void PinnedModel::addAppToFolder(int appRow, int folderRow)
0167 {
0168     if (appRow < 0 || appRow >= m_applications.size() || folderRow < 0 || folderRow >= m_applications.size()) {
0169         return;
0170     }
0171 
0172     if (!m_applications[appRow] || !m_folders[folderRow]) {
0173         return;
0174     }
0175 
0176     ApplicationFolder *folder = m_folders[folderRow];
0177     Application *app = m_applications[appRow];
0178     folder->addApp(app->storageId(), folder->applications() ? folder->applications()->rowCount() : 0);
0179 
0180     removeEntry(appRow);
0181 }
0182 
0183 void PinnedModel::load()
0184 {
0185     if (!m_applet) {
0186         return;
0187     }
0188 
0189     QJsonDocument doc = QJsonDocument::fromJson(m_applet->config().readEntry("Pinned", "{}").toUtf8());
0190 
0191     beginResetModel();
0192 
0193     for (QJsonValueRef r : doc.array()) {
0194         QJsonObject obj = r.toObject();
0195 
0196         if (obj[QStringLiteral("type")].toString() == "application") {
0197             // read application
0198             Application *app = Application::fromJson(obj, this);
0199             if (app) {
0200                 m_applications.append(app);
0201                 m_folders.append(nullptr);
0202             }
0203 
0204         } else if (obj[QStringLiteral("type")].toString() == "folder") {
0205             // read folder
0206             ApplicationFolder *folder = ApplicationFolder::fromJson(obj, this);
0207             connect(folder, &ApplicationFolder::saveRequested, this, &PinnedModel::save);
0208             connect(folder, &ApplicationFolder::moveAppOutRequested, this, &PinnedModel::addAppFromFolder);
0209 
0210             if (folder) {
0211                 m_applications.append(nullptr);
0212                 m_folders.append(folder);
0213             }
0214         }
0215     }
0216 
0217     endResetModel();
0218 }
0219 
0220 void PinnedModel::save()
0221 {
0222     if (!m_applet) {
0223         return;
0224     }
0225 
0226     QJsonArray arr;
0227     for (int i = 0; i < m_applications.size() && i < m_folders.size(); i++) {
0228         if (m_applications[i]) {
0229             arr.push_back(m_applications[i]->toJson());
0230         } else if (m_folders[i]) {
0231             arr.push_back(m_folders[i]->toJson());
0232         }
0233     }
0234     QByteArray data = QJsonDocument(arr).toJson(QJsonDocument::Compact);
0235 
0236     m_applet->config().writeEntry("Pinned", QString::fromStdString(data.toStdString()));
0237     Q_EMIT m_applet->configNeedsSaving();
0238 }
0239 
0240 void PinnedModel::addAppFromFolder(const QString &storageId)
0241 {
0242     addApp(storageId, 0);
0243 }