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