add more spacing
[personal-kdebase.git] / workspace / plasma / applets / kickoff / core / systemmodel.cpp
blobebaf08f1889a5053cb01c1e25e4628e7236ffa8c
1 /*
2 Copyright 2007 Robert Knight <robertknight@gmail.com>
3 Copyright 2007 Kevin Ottens <ervin@kde.org>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 #include "systemmodel.h"
23 // Qt
24 #include <QFile>
25 #include <QHash>
26 #include <QTimer>
28 // KDE
29 #include <KConfigGroup>
30 #include <KDebug>
31 #include <KDiskFreeSpaceInfo>
32 #include <KLocalizedString>
33 #include <KIcon>
34 #include <KGlobal>
35 #include <KUrl>
36 #include <KServiceTypeTrader>
37 #include <KStandardDirs>
38 #include <KSycoca>
39 #include <kfileplacesmodel.h>
40 #include <solid/device.h>
41 #include <solid/deviceinterface.h>
42 #include <solid/devicenotifier.h>
43 #include <solid/storageaccess.h>
44 #include <solid/storagedrive.h>
46 // Local
47 #include "core/models.h"
48 #include "core/systemmodel.h"
50 using namespace Kickoff;
52 static const int APPLICATIONS_ROW = 0;
53 static const int BOOKMARKS_ROW = 1;
54 static const int REMOVABLE_ROW = 2;
55 static const int FIXED_ROW = 3;
56 static const int LAST_ROW = FIXED_ROW;
58 struct UsageInfo {
59 UsageInfo()
60 : used(0),
61 available(0),
62 dirty(true) {}
64 quint64 used;
65 quint64 available;
66 bool dirty;
69 class SystemModel::Private
71 public:
72 Private(SystemModel *parent)
73 : q(parent)
74 , placesModel(new KFilePlacesModel(parent)) {
75 q->setSourceModel(placesModel);
77 connect(placesModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)),
78 q, SLOT(sourceDataChanged(QModelIndex, QModelIndex)));
79 connect(placesModel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)),
80 q, SLOT(sourceRowsAboutToBeInserted(QModelIndex, int, int)));
81 connect(placesModel, SIGNAL(rowsInserted(QModelIndex, int, int)),
82 q, SLOT(sourceRowsInserted(QModelIndex, int, int)));
83 connect(placesModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
84 q, SLOT(sourceRowsAboutToBeRemoved(QModelIndex, int, int)));
85 connect(placesModel, SIGNAL(rowsRemoved(QModelIndex, int, int)),
86 q, SLOT(sourceRowsRemoved(QModelIndex, int, int)));
88 topLevelSections << i18n("Applications")
89 << i18n("Places")
90 << i18n("Removable Storage")
91 << i18n("Storage");
92 loadApplications();
93 connect(&refreshTimer, SIGNAL(timeout()),
94 q, SLOT(startRefreshingUsageInfo()));
95 refreshTimer.start(10000);
96 QTimer::singleShot(0, q, SLOT(startRefreshingUsageInfo()));
97 connect(KSycoca::self(), SIGNAL(databaseChanged()), q, SLOT(reloadApplications()));
100 void queryFreeSpace(const QString& mountPoint) {
101 KDiskFreeSpaceInfo freeSpace = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint);
102 if (freeSpace.isValid())
103 q->freeSpaceInfoAvailable(freeSpace.mountPoint(), freeSpace.size() / 1024,
104 freeSpace.used() / 1024, freeSpace.available() / 1024);
107 void loadApplications() {
108 QStringList apps = Kickoff::systemApplicationList();
109 appsList.clear();
111 foreach(const QString &app, apps) {
112 KService::Ptr service = KService::serviceByStorageId(app);
114 if (!service) {
115 continue;
118 appsList << service;
120 //kDebug() << "*************" << appsList;
123 SystemModel * const q;
124 KFilePlacesModel *placesModel;
125 QStringList topLevelSections;
126 KService::List appsList;
127 QList<QString> mountPointsQueue;
128 QMap<QString, UsageInfo> usageByMountpoint;
129 QTimer refreshTimer;
132 SystemModel::SystemModel(QObject *parent)
133 : KickoffProxyModel(parent)
134 , d(new Private(this))
138 SystemModel::~SystemModel()
140 delete d;
143 QModelIndex SystemModel::mapFromSource(const QModelIndex &sourceIndex) const
145 if (!sourceIndex.isValid()) return QModelIndex();
147 QModelIndex parent;
149 if (!d->placesModel->isDevice(sourceIndex)) {
150 parent = index(BOOKMARKS_ROW, 0);
151 } else {
152 Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
154 Solid::StorageDrive *drive = 0;
155 Solid::Device parentDevice = dev;
156 while (parentDevice.isValid() && !drive) {
157 drive = parentDevice.as<Solid::StorageDrive>();
158 parentDevice = parentDevice.parent();
161 if (drive && (drive->isHotpluggable() || drive->isRemovable())) {
162 parent = index(REMOVABLE_ROW, 0);
163 } else {
164 parent = index(FIXED_ROW, 0);
168 return index(sourceIndex.row(), 0, parent);
171 QModelIndex SystemModel::mapToSource(const QModelIndex &proxyIndex) const
173 if (!proxyIndex.isValid() || !proxyIndex.parent().isValid()) {
174 return QModelIndex();
177 return d->placesModel->index(proxyIndex.row(), proxyIndex.column());
180 QModelIndex SystemModel::index(int row, int column, const QModelIndex &parent) const
182 if (!parent.isValid()) {
183 return createIndex(row, column, 0);
186 // We use the row+1 of the parent as internal Id.
187 return createIndex(row, column, parent.row() + 1);
190 QModelIndex SystemModel::parent(const QModelIndex &item) const
192 if (item.internalId() > 0) {
193 return index(item.internalId() - 1, 0);
194 } else {
195 return QModelIndex();
199 int SystemModel::rowCount(const QModelIndex &parent) const
201 if (!parent.isValid()) {
202 return LAST_ROW + 1;
203 } else if (!parent.parent().isValid()) {
204 switch (parent.row()) {
205 case APPLICATIONS_ROW:
206 return d->appsList.size();
207 break;
208 case BOOKMARKS_ROW:
209 return d->placesModel->rowCount();
210 break;
211 case REMOVABLE_ROW:
212 return d->placesModel->rowCount();
213 break;
214 default:
215 return 0;
219 return 0;
222 int SystemModel::columnCount(const QModelIndex &/*parent*/) const
224 return 1;
227 QVariant SystemModel::data(const QModelIndex &index, int role) const
229 if (!index.isValid()) {
230 return QVariant();
233 if (index.internalId() == 0) {
234 if (role == Qt::DisplayRole) {
235 return d->topLevelSections[index.row()];
236 } else {
237 return QVariant();
241 if (index.internalId() - 1 == APPLICATIONS_ROW) {
242 if (d->appsList.count() <= index.row()) {
243 return QVariant();
246 KService::Ptr service = d->appsList[index.row()];
248 switch (role) {
249 case Qt::DisplayRole:
250 return service->name();
251 case Qt::DecorationRole:
252 return KIcon(service->icon());
253 case SubTitleRole:
254 return service->genericName();
255 case UrlRole:
256 return service->entryPath();
257 default:
258 return QVariant();
262 if (role == UrlRole && !d->placesModel->isHidden(mapToSource(index))) {
263 QModelIndex parent = index.parent();
264 QModelIndex sourceIndex = mapToSource(index);
266 bool isDevice = d->placesModel->isDevice(sourceIndex);
267 bool wellPlaced = false;
269 if (!isDevice && parent.row() == BOOKMARKS_ROW) {
270 wellPlaced = true;
271 } else if (isDevice) {
272 Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
274 Solid::StorageDrive *drive = 0;
275 Solid::Device parentDevice = dev;
276 while (parentDevice.isValid() && !drive) {
277 drive = parentDevice.as<Solid::StorageDrive>();
278 parentDevice = parentDevice.parent();
281 bool fixed = !drive || (!drive->isHotpluggable() && !drive->isRemovable());
283 if (!fixed && parent.row() == REMOVABLE_ROW) {
284 wellPlaced = true;
285 } else if (fixed && parent.row() == FIXED_ROW) {
286 wellPlaced = true;
290 if (wellPlaced) {
291 return d->placesModel->url(sourceIndex).url();
292 } else {
293 return QVariant();
295 } else if (role == DeviceUdiRole) {
296 QModelIndex sourceIndex = mapToSource(index);
298 if (d->placesModel->isDevice(sourceIndex)) {
299 Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
300 return dev.udi();
301 } else {
302 return QVariant();
304 } else if (role == SubTitleRole) {
305 QModelIndex sourceIndex = mapToSource(index);
307 if (d->placesModel->isDevice(sourceIndex)) {
308 Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
309 Solid::StorageAccess *access = dev.as<Solid::StorageAccess>();
311 if (access) {
312 return access->filePath();
314 } else if (index.parent().row() != APPLICATIONS_ROW) {
315 KUrl url = d->placesModel->url(sourceIndex);
316 return url.isLocalFile() ? url.path() : url.prettyUrl();
319 return QVariant();
320 } else if (role == DiskUsedSpaceRole || role == DiskFreeSpaceRole) {
321 QModelIndex sourceIndex = mapToSource(index);
322 QString mp;
324 if (d->placesModel->isDevice(sourceIndex)) {
325 Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
326 Solid::StorageAccess *access = dev.as<Solid::StorageAccess>();
328 if (access) {
329 mp = access->filePath();
333 if (!mp.isEmpty() && d->usageByMountpoint.contains(mp)) {
334 UsageInfo info = d->usageByMountpoint[mp];
336 if (role == DiskUsedSpaceRole) {
337 return info.used;
338 } else {
339 return info.available;
344 return d->placesModel->data(mapToSource(index), role);
347 void SystemModel::startRefreshingUsageInfo()
349 if (!d->mountPointsQueue.isEmpty()) {
350 return;
353 int rowCount = d->placesModel->rowCount();
354 for (int i = 0; i < rowCount; ++i) {
355 QModelIndex index = d->placesModel->index(i, 0);
356 if (d->placesModel->isDevice(index)) {
357 Solid::Device dev = d->placesModel->deviceForIndex(index);
358 Solid::StorageAccess *access = dev.as<Solid::StorageAccess>();
360 if (access && !access->filePath().isEmpty()) {
361 d->mountPointsQueue << access->filePath();
366 if (!d->mountPointsQueue.isEmpty()) {
367 d->queryFreeSpace(d->mountPointsQueue.takeFirst());
371 void SystemModel::reloadApplications()
373 d->loadApplications();
376 void SystemModel::freeSpaceInfoAvailable(const QString& mountPoint, quint64,
377 quint64 kbUsed, quint64 kbAvailable)
379 UsageInfo info;
380 info.used = kbUsed;
381 info.available = kbAvailable;
383 d->usageByMountpoint[mountPoint] = info;
385 // More to process
386 if (!d->mountPointsQueue.isEmpty()) {
387 d->queryFreeSpace(d->mountPointsQueue.takeFirst());
388 return;
391 // We're done, let's emit the changes
392 int rowCount = d->placesModel->rowCount();
393 for (int i = 0; i < rowCount; ++i) {
394 QModelIndex sourceIndex = d->placesModel->index(i, 0);
395 if (d->placesModel->isDevice(sourceIndex)) {
396 Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
397 Solid::StorageAccess *access = dev.as<Solid::StorageAccess>();
399 if (access && d->usageByMountpoint.contains(access->filePath())) {
400 info = d->usageByMountpoint[access->filePath()];
402 if (info.dirty) {
403 info.dirty = false;
404 d->usageByMountpoint[access->filePath()] = info;
405 } else {
406 d->usageByMountpoint.remove(access->filePath());
409 QModelIndex index = mapFromSource(sourceIndex);
410 emit dataChanged(index, index);
416 void Kickoff::SystemModel::sourceDataChanged(const QModelIndex &start, const QModelIndex &end)
418 if (start.parent().isValid()) return;
420 for (int row = BOOKMARKS_ROW; row <= LAST_ROW; ++row) {
421 QModelIndex section = index(row, 0);
423 QModelIndex new_start = index(start.row(), start.column(), section);
424 QModelIndex new_end = index(end.row(), end.column(), section);
425 emit dataChanged(new_start, new_end);
429 void Kickoff::SystemModel::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
431 if (parent.isValid()) return;
433 for (int row = BOOKMARKS_ROW; row <= LAST_ROW; ++row) {
434 QModelIndex section = index(row, 0);
435 beginInsertRows(section, start, end);
439 void Kickoff::SystemModel::sourceRowsInserted(const QModelIndex &parent, int /*start*/, int /*end*/)
441 if (parent.isValid()) return;
443 endInsertRows();
446 void Kickoff::SystemModel::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
448 if (parent.isValid()) return;
450 for (int row = BOOKMARKS_ROW; row <= LAST_ROW; ++row) {
451 QModelIndex section = index(row, 0);
452 beginRemoveRows(section, start, end);
456 void Kickoff::SystemModel::sourceRowsRemoved(const QModelIndex &parent, int /*start*/, int /*end*/)
458 if (parent.isValid()) return;
460 endRemoveRows();
463 #include "systemmodel.moc"