Changeset View
Changeset View
Standalone View
Standalone View
src/context/AppletModel.cpp
- This file was added.
1 | /**************************************************************************************** | ||||
---|---|---|---|---|---|
2 | * Copyright (c) 2017 Malte Veerman <malte.veerman@gmail.com> * | ||||
3 | * * | ||||
4 | * This program is free software; you can redistribute it and/or modify it under * | ||||
5 | * the terms of the GNU General Public License as published by the Free Software * | ||||
6 | * Foundation; either version 2 of the License, or (at your option) any later * | ||||
7 | * version. * | ||||
8 | * * | ||||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY * | ||||
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * | ||||
11 | * PARTICULAR PURPOSE. See the GNU General Public License for more details. * | ||||
12 | * * | ||||
13 | * You should have received a copy of the GNU General Public License along with * | ||||
14 | * this program. If not, see <http://www.gnu.org/licenses/>. * | ||||
15 | ****************************************************************************************/ | ||||
16 | | ||||
17 | #define DEBUG_PREFIX "AppletModel" | ||||
18 | | ||||
19 | #include "AppletModel.h" | ||||
20 | | ||||
21 | #include "AmarokContextPackageStructure.h" | ||||
22 | #include "AppletLoader.h" | ||||
23 | #include "core/support/Amarok.h" | ||||
24 | #include "core/support/Debug.h" | ||||
25 | | ||||
26 | #include <algorithm> | ||||
27 | | ||||
28 | #include <QUrl> | ||||
29 | | ||||
30 | #include <KPackage/Package> | ||||
31 | #include <KPackage/PackageLoader> | ||||
32 | #include <KPluginInfo> | ||||
33 | | ||||
34 | | ||||
35 | using namespace Context; | ||||
36 | | ||||
37 | | ||||
38 | class Context::AppletPackage : public KPackage::Package | ||||
39 | { | ||||
40 | public: | ||||
41 | AppletPackage(const KPackage::Package &package) : KPackage::Package(package) {} | ||||
42 | | ||||
43 | bool operator==(const AppletPackage &p) | ||||
44 | { | ||||
45 | return metadata() == p.metadata(); | ||||
46 | } | ||||
47 | }; | ||||
48 | | ||||
49 | | ||||
50 | AppletModel::AppletModel(AppletLoader *loader, QObject* parent) | ||||
51 | : QAbstractListModel(parent) | ||||
52 | , m_loader(loader) | ||||
53 | { | ||||
54 | newApplets(loader->applets()); | ||||
55 | connect(loader, &AppletLoader::finished, this, &AppletModel::newApplets); | ||||
56 | } | ||||
57 | | ||||
58 | AppletModel::~AppletModel() | ||||
59 | { | ||||
60 | } | ||||
61 | | ||||
62 | int AppletModel::rowCount(const QModelIndex& parent) const | ||||
63 | { | ||||
64 | Q_UNUSED(parent) | ||||
65 | | ||||
66 | return m_packages.size(); | ||||
67 | } | ||||
68 | | ||||
69 | void AppletModel::newApplets(const QList<KPluginMetaData>& applets) | ||||
70 | { | ||||
71 | DEBUG_BLOCK | ||||
72 | | ||||
73 | beginResetModel(); | ||||
74 | | ||||
75 | m_packages.clear(); | ||||
76 | | ||||
77 | for (const auto applet : applets) | ||||
78 | { | ||||
79 | auto loader = KPackage::PackageLoader::self(); | ||||
80 | auto structure = new AmarokContextPackageStructure; | ||||
81 | loader->addKnownPackageStructure(QStringLiteral("Amarok/Context"), structure); | ||||
82 | auto package = loader->loadPackage(QStringLiteral("Amarok/Context"), applet.pluginId()); | ||||
83 | | ||||
84 | if (package.isValid()) | ||||
85 | { | ||||
86 | m_packages << package; | ||||
87 | } | ||||
88 | else | ||||
89 | error() << "Error loading package:" << applet.pluginId(); | ||||
90 | } | ||||
91 | | ||||
92 | //Sort applets by name | ||||
93 | std::sort(m_packages.begin(), m_packages.end(), [] (const AppletPackage &p1, const AppletPackage &p2) { | ||||
94 | return p1.metadata().name() < p2.metadata().name(); | ||||
95 | }); | ||||
96 | | ||||
97 | endResetModel(); | ||||
98 | } | ||||
99 | | ||||
100 | QVariant AppletModel::data(const QModelIndex& index, int role) const | ||||
101 | { | ||||
102 | int row = index.row(); | ||||
103 | | ||||
104 | if (row >= m_packages.size()) | ||||
105 | return QVariant(); | ||||
106 | | ||||
107 | const auto &package = m_packages.at(row); | ||||
108 | | ||||
109 | switch (role) | ||||
110 | { | ||||
111 | case Name: | ||||
112 | return package.metadata().name(); | ||||
113 | | ||||
114 | case Id: | ||||
115 | return package.metadata().pluginId(); | ||||
116 | | ||||
117 | case Icon: | ||||
118 | return package.metadata().iconName(); | ||||
119 | | ||||
120 | case Mainscript: | ||||
121 | return QUrl::fromLocalFile(package.filePath("mainscript")); | ||||
122 | | ||||
123 | case Collapsed: | ||||
124 | return Amarok::config("Context").readEntry(package.metadata().pluginId() + "_collapsed", false); | ||||
125 | | ||||
126 | case ContentHeight: | ||||
127 | return Amarok::config("Context").readEntry(package.metadata().pluginId() + "_contentHeight", 300); | ||||
128 | | ||||
129 | case PackagePath: | ||||
130 | return QVariant(package.path() + "contents/"); | ||||
131 | } | ||||
132 | | ||||
133 | return QVariant(); | ||||
134 | } | ||||
135 | | ||||
136 | bool Context::AppletModel::setData(const QModelIndex& index, const QVariant& value, int role) | ||||
137 | { | ||||
138 | int row = index.row(); | ||||
139 | | ||||
140 | if (row >= m_packages.size()) | ||||
141 | return false; | ||||
142 | | ||||
143 | const auto &package = m_packages.at(row); | ||||
144 | | ||||
145 | switch (role) | ||||
146 | { | ||||
147 | case Collapsed: | ||||
148 | { | ||||
149 | Amarok::config("Context").writeEntry(package.metadata().pluginId() + "_collapsed", value.toBool()); | ||||
150 | emit dataChanged(index, index, QVector<int>{role}); | ||||
151 | return true; | ||||
152 | } | ||||
153 | case ContentHeight: | ||||
154 | { | ||||
155 | Amarok::config("Context").writeEntry(package.metadata().pluginId() + "_contentHeight", value.toReal()); | ||||
156 | emit dataChanged(index, index, QVector<int>{role}); | ||||
157 | return true; | ||||
158 | } | ||||
159 | | ||||
160 | default: | ||||
161 | warning() << (Role) role << "is read-only."; | ||||
162 | } | ||||
163 | | ||||
164 | return false; | ||||
165 | } | ||||
166 | | ||||
167 | QHash< int, QByteArray > AppletModel::roleNames() const | ||||
168 | { | ||||
169 | QHash<int, QByteArray> roles; | ||||
170 | roles.insert(Name, "name"); | ||||
171 | roles.insert(Id, "appletId"); | ||||
172 | roles.insert(Icon, "icon"); | ||||
173 | roles.insert(Mainscript, "mainscript"); | ||||
174 | roles.insert(Collapsed, "collapsed"); | ||||
175 | roles.insert(PackagePath, "packagePath"); | ||||
176 | roles.insert(ContentHeight, "contentHeight"); | ||||
177 | | ||||
178 | return roles; | ||||
179 | } | ||||
180 | | ||||
181 | void AppletModel::setAppletCollapsed(const QString& id, bool collapsed) | ||||
182 | { | ||||
183 | DEBUG_BLOCK | ||||
184 | | ||||
185 | debug() << "Set collapsed for applet:" << id << "to:" << collapsed; | ||||
186 | | ||||
187 | auto package = findPackage(id); | ||||
188 | if (package.isValid()) | ||||
189 | { | ||||
190 | Amarok::config("Context").writeEntry(id + "_collapsed", collapsed); | ||||
191 | int row = m_packages.indexOf(package); | ||||
192 | auto index = createIndex(row, 0); | ||||
193 | emit dataChanged(index, index, QVector<int>{Collapsed}); | ||||
194 | } | ||||
195 | } | ||||
196 | | ||||
197 | void Context::AppletModel::setAppletContentHeight(const QString& id, qreal height) | ||||
198 | { | ||||
199 | DEBUG_BLOCK | ||||
200 | | ||||
201 | debug() << "Set content height for applet:" << id << "to:" << height; | ||||
202 | | ||||
203 | auto package = findPackage(id); | ||||
204 | if (package.isValid()) | ||||
205 | { | ||||
206 | Amarok::config("Context").writeEntry(id + "_contentHeight", height); | ||||
207 | int row = m_packages.indexOf(package); | ||||
208 | auto index = createIndex(row, 0); | ||||
209 | emit dataChanged(index, index, QVector<int>{ContentHeight}); | ||||
210 | } | ||||
211 | } | ||||
212 | | ||||
213 | AppletPackage AppletModel::findPackage(const QString& id) | ||||
214 | { | ||||
215 | for (const auto &package : m_packages) | ||||
216 | { | ||||
217 | auto metadata = package.metadata(); | ||||
218 | | ||||
219 | if (metadata.pluginId() == id) | ||||
220 | return package; | ||||
221 | } | ||||
222 | | ||||
223 | warning() << "Applet with id:" << id << "not found."; | ||||
224 | return AppletPackage(KPackage::Package()); | ||||
225 | } | ||||
226 | | ||||
227 | AppletProxyModel::AppletProxyModel(AppletModel* appletModel, QObject *parent) | ||||
228 | : QSortFilterProxyModel(parent) | ||||
229 | , m_appletModel(appletModel) | ||||
230 | { | ||||
231 | setSourceModel(appletModel); | ||||
232 | setDynamicSortFilter(true); | ||||
233 | sort(0); | ||||
234 | | ||||
235 | connect(m_appletModel->loader(), &AppletLoader::finished, this, &AppletProxyModel::enabledAppletsChanged); | ||||
236 | } | ||||
237 | | ||||
238 | AppletProxyModel::~AppletProxyModel() | ||||
239 | { | ||||
240 | } | ||||
241 | | ||||
242 | QStringList AppletProxyModel::enabledApplets() const | ||||
243 | { | ||||
244 | QStringList list; | ||||
245 | for (const auto &applet : m_appletModel->loader()->enabledApplets()) | ||||
246 | { | ||||
247 | list << applet.pluginId(); | ||||
248 | } | ||||
249 | | ||||
250 | std::sort(list.begin(), list.end(), | ||||
251 | [] (const QString &a, const QString &b) { | ||||
252 | QStringList ae = Amarok::config("Context").readEntry("enabledApplets", QStringList()); | ||||
253 | return ae.indexOf(a) < ae.indexOf(b); | ||||
254 | } | ||||
255 | ); | ||||
256 | | ||||
257 | return list; | ||||
258 | } | ||||
259 | | ||||
260 | void AppletProxyModel::setAppletEnabled(const QString& id, bool enabled, int place) | ||||
261 | { | ||||
262 | DEBUG_BLOCK | ||||
263 | | ||||
264 | debug() << "Set enabled for applet:" << id << "to:" << enabled; | ||||
265 | | ||||
266 | if (enabled == appletEnabled(id)) | ||||
267 | return; | ||||
268 | | ||||
269 | QStringList ea = enabledApplets(); | ||||
270 | | ||||
271 | if (enabled) | ||||
272 | { | ||||
273 | if (place <= -1) | ||||
274 | place = ea.size(); | ||||
275 | | ||||
276 | debug() << "Applet's new place is" << place; | ||||
277 | ea.insert(place, id); | ||||
278 | } | ||||
279 | else | ||||
280 | { | ||||
281 | ea.removeAll(id); | ||||
282 | } | ||||
283 | Amarok::config("Context").writeEntry("enabledApplets", ea); | ||||
284 | | ||||
285 | debug() << "New enabled applets:" << ea; | ||||
286 | | ||||
287 | invalidateFilter(); | ||||
288 | | ||||
289 | emit enabledAppletsChanged(); | ||||
290 | } | ||||
291 | | ||||
292 | void AppletProxyModel::setAppletPlace(const QString& id, int place) | ||||
293 | { | ||||
294 | DEBUG_BLOCK | ||||
295 | | ||||
296 | debug() << "Set place for applet:" << id << "to:" << place; | ||||
297 | | ||||
298 | int currentPlace = appletPlace(id); | ||||
299 | debug() << "Current place is" << currentPlace; | ||||
300 | | ||||
301 | if (currentPlace == place) | ||||
302 | return; | ||||
303 | | ||||
304 | if (place <= -1) | ||||
305 | { | ||||
306 | setAppletEnabled(id, false); | ||||
307 | return; | ||||
308 | } | ||||
309 | | ||||
310 | if (currentPlace <= -1) | ||||
311 | setAppletEnabled(id, true, place); | ||||
312 | | ||||
313 | QStringList ea = enabledApplets(); | ||||
314 | | ||||
315 | place = qMin(place, ea.size() - 1); | ||||
316 | bool forward = place > currentPlace; | ||||
317 | | ||||
318 | beginMoveRows(QModelIndex(), currentPlace, currentPlace, QModelIndex(), forward ? place + 1 : place); | ||||
319 | ea.move(currentPlace, place); | ||||
320 | Amarok::config("Context").writeEntry("enabledApplets", ea); | ||||
321 | endMoveRows(); | ||||
322 | | ||||
323 | debug() << "New enabled applets:" << ea; | ||||
324 | } | ||||
325 | | ||||
326 | int AppletProxyModel::appletPlace(const QString& id) const | ||||
327 | { | ||||
328 | return enabledApplets().indexOf(id); | ||||
329 | } | ||||
330 | | ||||
331 | bool AppletProxyModel::appletEnabled(const QString& id) const | ||||
332 | { | ||||
333 | return enabledApplets().contains(id); | ||||
334 | } | ||||
335 | | ||||
336 | void Context::AppletProxyModel::clear() | ||||
337 | { | ||||
338 | for( const auto &applet : enabledApplets() ) | ||||
339 | { | ||||
340 | setAppletEnabled( applet, false ); | ||||
341 | } | ||||
342 | } | ||||
343 | | ||||
344 | bool AppletProxyModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const | ||||
345 | { | ||||
346 | int placeLeft = appletPlace(source_left.data(AppletModel::Id).toString()); | ||||
347 | int placeRight = appletPlace(source_right.data(AppletModel::Id).toString()); | ||||
348 | | ||||
349 | return placeLeft < placeRight; | ||||
350 | } | ||||
351 | | ||||
352 | bool AppletProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const | ||||
353 | { | ||||
354 | QModelIndex index = sourceModel()->index(source_row, 0, source_parent); | ||||
355 | return appletEnabled(index.data(AppletModel::Id).toString()); | ||||
356 | } | ||||
357 | | ||||
358 | |