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"
29 #include <KConfigGroup>
31 #include <KDiskFreeSpaceInfo>
32 #include <KLocalizedString>
36 #include <KServiceTypeTrader>
37 #include <KStandardDirs>
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>
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
;
69 class SystemModel::Private
72 Private(SystemModel
*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")
90 << i18n("Removable Storage")
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();
111 foreach(const QString
&app
, apps
) {
112 KService::Ptr service
= KService::serviceByStorageId(app
);
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
;
132 SystemModel::SystemModel(QObject
*parent
)
133 : KickoffProxyModel(parent
)
134 , d(new Private(this))
138 SystemModel::~SystemModel()
143 QModelIndex
SystemModel::mapFromSource(const QModelIndex
&sourceIndex
) const
145 if (!sourceIndex
.isValid()) return QModelIndex();
149 if (!d
->placesModel
->isDevice(sourceIndex
)) {
150 parent
= index(BOOKMARKS_ROW
, 0);
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);
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);
195 return QModelIndex();
199 int SystemModel::rowCount(const QModelIndex
&parent
) const
201 if (!parent
.isValid()) {
203 } else if (!parent
.parent().isValid()) {
204 switch (parent
.row()) {
205 case APPLICATIONS_ROW
:
206 return d
->appsList
.size();
209 return d
->placesModel
->rowCount();
212 return d
->placesModel
->rowCount();
222 int SystemModel::columnCount(const QModelIndex
&/*parent*/) const
227 QVariant
SystemModel::data(const QModelIndex
&index
, int role
) const
229 if (!index
.isValid()) {
233 if (index
.internalId() == 0) {
234 if (role
== Qt::DisplayRole
) {
235 return d
->topLevelSections
[index
.row()];
241 if (index
.internalId() - 1 == APPLICATIONS_ROW
) {
242 if (d
->appsList
.count() <= index
.row()) {
246 KService::Ptr service
= d
->appsList
[index
.row()];
249 case Qt::DisplayRole
:
250 return service
->name();
251 case Qt::DecorationRole
:
252 return KIcon(service
->icon());
254 return service
->genericName();
256 return service
->entryPath();
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
) {
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
) {
285 } else if (fixed
&& parent
.row() == FIXED_ROW
) {
291 return d
->placesModel
->url(sourceIndex
).url();
295 } else if (role
== DeviceUdiRole
) {
296 QModelIndex sourceIndex
= mapToSource(index
);
298 if (d
->placesModel
->isDevice(sourceIndex
)) {
299 Solid::Device dev
= d
->placesModel
->deviceForIndex(sourceIndex
);
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
>();
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();
320 } else if (role
== DiskUsedSpaceRole
|| role
== DiskFreeSpaceRole
) {
321 QModelIndex sourceIndex
= mapToSource(index
);
324 if (d
->placesModel
->isDevice(sourceIndex
)) {
325 Solid::Device dev
= d
->placesModel
->deviceForIndex(sourceIndex
);
326 Solid::StorageAccess
*access
= dev
.as
<Solid::StorageAccess
>();
329 mp
= access
->filePath();
333 if (!mp
.isEmpty() && d
->usageByMountpoint
.contains(mp
)) {
334 UsageInfo info
= d
->usageByMountpoint
[mp
];
336 if (role
== DiskUsedSpaceRole
) {
339 return info
.available
;
344 return d
->placesModel
->data(mapToSource(index
), role
);
347 void SystemModel::startRefreshingUsageInfo()
349 if (!d
->mountPointsQueue
.isEmpty()) {
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
)
381 info
.available
= kbAvailable
;
383 d
->usageByMountpoint
[mountPoint
] = info
;
386 if (!d
->mountPointsQueue
.isEmpty()) {
387 d
->queryFreeSpace(d
->mountPointsQueue
.takeFirst());
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()];
404 d
->usageByMountpoint
[access
->filePath()] = info
;
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;
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;
463 #include "systemmodel.moc"