00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "systemmodel.h"
00022
00023
00024 #include <QFile>
00025 #include <QHash>
00026 #include <QTimer>
00027
00028
00029 #include <KConfigGroup>
00030 #include <KDebug>
00031 #include <KDiskFreeSpaceInfo>
00032 #include <KLocalizedString>
00033 #include <KIcon>
00034 #include <KGlobal>
00035 #include <KUrl>
00036 #include <KServiceTypeTrader>
00037 #include <KStandardDirs>
00038 #include <KSycoca>
00039 #include <kfileplacesmodel.h>
00040 #include <solid/device.h>
00041 #include <solid/deviceinterface.h>
00042 #include <solid/devicenotifier.h>
00043 #include <solid/storageaccess.h>
00044 #include <solid/storagedrive.h>
00045
00046
00047 #include "core/models.h"
00048 #include "core/systemmodel.h"
00049
00050 using namespace Kickoff;
00051
00052 static const int APPLICATIONS_ROW = 0;
00053 static const int BOOKMARKS_ROW = 1;
00054 static const int REMOVABLE_ROW = 2;
00055 static const int FIXED_ROW = 3;
00056 static const int LAST_ROW = FIXED_ROW;
00057
00058 struct UsageInfo {
00059 UsageInfo()
00060 : used(0),
00061 available(0),
00062 dirty(true) {}
00063
00064 quint64 used;
00065 quint64 available;
00066 bool dirty;
00067 };
00068
00069 class SystemModel::Private
00070 {
00071 public:
00072 Private(SystemModel *parent)
00073 : q(parent)
00074 , placesModel(new KFilePlacesModel(parent)) {
00075 q->setSourceModel(placesModel);
00076
00077 connect(placesModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)),
00078 q, SLOT(sourceDataChanged(QModelIndex, QModelIndex)));
00079 connect(placesModel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)),
00080 q, SLOT(sourceRowsAboutToBeInserted(QModelIndex, int, int)));
00081 connect(placesModel, SIGNAL(rowsInserted(QModelIndex, int, int)),
00082 q, SLOT(sourceRowsInserted(QModelIndex, int, int)));
00083 connect(placesModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
00084 q, SLOT(sourceRowsAboutToBeRemoved(QModelIndex, int, int)));
00085 connect(placesModel, SIGNAL(rowsRemoved(QModelIndex, int, int)),
00086 q, SLOT(sourceRowsRemoved(QModelIndex, int, int)));
00087
00088 topLevelSections << i18n("Applications")
00089 << i18n("Places")
00090 << i18n("Removable Storage")
00091 << i18n("Storage");
00092 loadApplications();
00093 connect(&refreshTimer, SIGNAL(timeout()),
00094 q, SLOT(startRefreshingUsageInfo()));
00095 refreshTimer.start(10000);
00096 QTimer::singleShot(0, q, SLOT(startRefreshingUsageInfo()));
00097 connect(KSycoca::self(), SIGNAL(databaseChanged()), q, SLOT(reloadApplications()));
00098 }
00099
00100 void queryFreeSpace(const QString& mountPoint) {
00101 KDiskFreeSpaceInfo freeSpace = KDiskFreeSpaceInfo::freeSpaceInfo(mountPoint);
00102 if (freeSpace.isValid())
00103 q->freeSpaceInfoAvailable(freeSpace.mountPoint(), freeSpace.size() / 1024,
00104 freeSpace.used() / 1024, freeSpace.available() / 1024);
00105 }
00106
00107 void loadApplications() {
00108 QStringList apps = Kickoff::systemApplicationList();
00109 appsList.clear();
00110
00111 foreach(const QString &app, apps) {
00112 KService::Ptr service = KService::serviceByStorageId(app);
00113
00114 if (!service) {
00115 continue;
00116 }
00117
00118 appsList << service;
00119 }
00120
00121 }
00122
00123 SystemModel * const q;
00124 KFilePlacesModel *placesModel;
00125 QStringList topLevelSections;
00126 KService::List appsList;
00127 QList<QString> mountPointsQueue;
00128 QMap<QString, UsageInfo> usageByMountpoint;
00129 QTimer refreshTimer;
00130 };
00131
00132 SystemModel::SystemModel(QObject *parent)
00133 : KickoffProxyModel(parent)
00134 , d(new Private(this))
00135 {
00136 }
00137
00138 SystemModel::~SystemModel()
00139 {
00140 delete d;
00141 }
00142
00143 QModelIndex SystemModel::mapFromSource(const QModelIndex &sourceIndex) const
00144 {
00145 if (!sourceIndex.isValid()) return QModelIndex();
00146
00147 QModelIndex parent;
00148
00149 if (!d->placesModel->isDevice(sourceIndex)) {
00150 parent = index(BOOKMARKS_ROW, 0);
00151 } else {
00152 Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
00153
00154 Solid::StorageDrive *drive = 0;
00155 Solid::Device parentDevice = dev;
00156 while (parentDevice.isValid() && !drive) {
00157 drive = parentDevice.as<Solid::StorageDrive>();
00158 parentDevice = parentDevice.parent();
00159 }
00160
00161 if (drive && (drive->isHotpluggable() || drive->isRemovable())) {
00162 parent = index(REMOVABLE_ROW, 0);
00163 } else {
00164 parent = index(FIXED_ROW, 0);
00165 }
00166 }
00167
00168 return index(sourceIndex.row(), 0, parent);
00169 }
00170
00171 QModelIndex SystemModel::mapToSource(const QModelIndex &proxyIndex) const
00172 {
00173 if (!proxyIndex.isValid() || !proxyIndex.parent().isValid()) {
00174 return QModelIndex();
00175 }
00176
00177 return d->placesModel->index(proxyIndex.row(), proxyIndex.column());
00178 }
00179
00180 QModelIndex SystemModel::index(int row, int column, const QModelIndex &parent) const
00181 {
00182 if (!parent.isValid()) {
00183 return createIndex(row, column, 0);
00184 }
00185
00186
00187 return createIndex(row, column, parent.row() + 1);
00188 }
00189
00190 QModelIndex SystemModel::parent(const QModelIndex &item) const
00191 {
00192 if (item.internalId() > 0) {
00193 return index(item.internalId() - 1, 0);
00194 } else {
00195 return QModelIndex();
00196 }
00197 }
00198
00199 int SystemModel::rowCount(const QModelIndex &parent) const
00200 {
00201 if (!parent.isValid()) {
00202 return LAST_ROW + 1;
00203 } else if (!parent.parent().isValid()) {
00204 switch (parent.row()) {
00205 case APPLICATIONS_ROW:
00206 return d->appsList.size();
00207 break;
00208 case BOOKMARKS_ROW:
00209 return d->placesModel->rowCount();
00210 break;
00211 case REMOVABLE_ROW:
00212 return d->placesModel->rowCount();
00213 break;
00214 default:
00215 return 0;
00216 }
00217 }
00218
00219 return 0;
00220 }
00221
00222 int SystemModel::columnCount(const QModelIndex &) const
00223 {
00224 return 1;
00225 }
00226
00227 QVariant SystemModel::data(const QModelIndex &index, int role) const
00228 {
00229 if (!index.isValid()) {
00230 return QVariant();
00231 }
00232
00233 if (index.internalId() == 0) {
00234 if (role == Qt::DisplayRole) {
00235 return d->topLevelSections[index.row()];
00236 } else {
00237 return QVariant();
00238 }
00239 }
00240
00241 if (index.internalId() - 1 == APPLICATIONS_ROW) {
00242 if (d->appsList.count() <= index.row()) {
00243 return QVariant();
00244 }
00245
00246 KService::Ptr service = d->appsList[index.row()];
00247
00248 switch (role) {
00249 case Qt::DisplayRole:
00250 return service->name();
00251 case Qt::DecorationRole:
00252 return KIcon(service->icon());
00253 case SubTitleRole:
00254 return service->genericName();
00255 case UrlRole:
00256 return service->entryPath();
00257 default:
00258 return QVariant();
00259 }
00260 }
00261
00262 if (role == UrlRole && !d->placesModel->isHidden(mapToSource(index))) {
00263 QModelIndex parent = index.parent();
00264 QModelIndex sourceIndex = mapToSource(index);
00265
00266 bool isDevice = d->placesModel->isDevice(sourceIndex);
00267 bool wellPlaced = false;
00268
00269 if (!isDevice && parent.row() == BOOKMARKS_ROW) {
00270 wellPlaced = true;
00271 } else if (isDevice) {
00272 Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
00273
00274 Solid::StorageDrive *drive = 0;
00275 Solid::Device parentDevice = dev;
00276 while (parentDevice.isValid() && !drive) {
00277 drive = parentDevice.as<Solid::StorageDrive>();
00278 parentDevice = parentDevice.parent();
00279 }
00280
00281 bool fixed = !drive || (!drive->isHotpluggable() && !drive->isRemovable());
00282
00283 if (!fixed && parent.row() == REMOVABLE_ROW) {
00284 wellPlaced = true;
00285 } else if (fixed && parent.row() == FIXED_ROW) {
00286 wellPlaced = true;
00287 }
00288 }
00289
00290 if (wellPlaced) {
00291 return d->placesModel->url(sourceIndex).url();
00292 } else {
00293 return QVariant();
00294 }
00295 } else if (role == DeviceUdiRole) {
00296 QModelIndex sourceIndex = mapToSource(index);
00297
00298 if (d->placesModel->isDevice(sourceIndex)) {
00299 Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
00300 return dev.udi();
00301 } else {
00302 return QVariant();
00303 }
00304 } else if (role == SubTitleRole) {
00305 QModelIndex sourceIndex = mapToSource(index);
00306
00307 if (d->placesModel->isDevice(sourceIndex)) {
00308 Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
00309 Solid::StorageAccess *access = dev.as<Solid::StorageAccess>();
00310
00311 if (access) {
00312 return access->filePath();
00313 }
00314 } else if (index.parent().row() != APPLICATIONS_ROW) {
00315 KUrl url = d->placesModel->url(sourceIndex);
00316 return url.isLocalFile() ? url.path() : url.prettyUrl();
00317 }
00318
00319 return QVariant();
00320 } else if (role == DiskUsedSpaceRole || role == DiskFreeSpaceRole) {
00321 QModelIndex sourceIndex = mapToSource(index);
00322 QString mp;
00323
00324 if (d->placesModel->isDevice(sourceIndex)) {
00325 Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
00326 Solid::StorageAccess *access = dev.as<Solid::StorageAccess>();
00327
00328 if (access) {
00329 mp = access->filePath();
00330 }
00331 }
00332
00333 if (!mp.isEmpty() && d->usageByMountpoint.contains(mp)) {
00334 UsageInfo info = d->usageByMountpoint[mp];
00335
00336 if (role == DiskUsedSpaceRole) {
00337 return info.used;
00338 } else {
00339 return info.available;
00340 }
00341 }
00342 }
00343
00344 return d->placesModel->data(mapToSource(index), role);
00345 }
00346
00347 void SystemModel::startRefreshingUsageInfo()
00348 {
00349 if (!d->mountPointsQueue.isEmpty()) {
00350 return;
00351 }
00352
00353 int rowCount = d->placesModel->rowCount();
00354 for (int i = 0; i < rowCount; ++i) {
00355 QModelIndex index = d->placesModel->index(i, 0);
00356 if (d->placesModel->isDevice(index)) {
00357 Solid::Device dev = d->placesModel->deviceForIndex(index);
00358 Solid::StorageAccess *access = dev.as<Solid::StorageAccess>();
00359
00360 if (access && !access->filePath().isEmpty()) {
00361 d->mountPointsQueue << access->filePath();
00362 }
00363 }
00364 }
00365
00366 if (!d->mountPointsQueue.isEmpty()) {
00367 d->queryFreeSpace(d->mountPointsQueue.takeFirst());
00368 }
00369 }
00370
00371 void SystemModel::reloadApplications()
00372 {
00373 d->loadApplications();
00374 }
00375
00376 void SystemModel::freeSpaceInfoAvailable(const QString& mountPoint, quint64,
00377 quint64 kbUsed, quint64 kbAvailable)
00378 {
00379 UsageInfo info;
00380 info.used = kbUsed;
00381 info.available = kbAvailable;
00382
00383 d->usageByMountpoint[mountPoint] = info;
00384
00385
00386 if (!d->mountPointsQueue.isEmpty()) {
00387 d->queryFreeSpace(d->mountPointsQueue.takeFirst());
00388 return;
00389 }
00390
00391
00392 int rowCount = d->placesModel->rowCount();
00393 for (int i = 0; i < rowCount; ++i) {
00394 QModelIndex sourceIndex = d->placesModel->index(i, 0);
00395 if (d->placesModel->isDevice(sourceIndex)) {
00396 Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
00397 Solid::StorageAccess *access = dev.as<Solid::StorageAccess>();
00398
00399 if (access && d->usageByMountpoint.contains(access->filePath())) {
00400 info = d->usageByMountpoint[access->filePath()];
00401
00402 if (info.dirty) {
00403 info.dirty = false;
00404 d->usageByMountpoint[access->filePath()] = info;
00405 } else {
00406 d->usageByMountpoint.remove(access->filePath());
00407 }
00408
00409 QModelIndex index = mapFromSource(sourceIndex);
00410 emit dataChanged(index, index);
00411 }
00412 }
00413 }
00414 }
00415
00416 void Kickoff::SystemModel::sourceDataChanged(const QModelIndex &start, const QModelIndex &end)
00417 {
00418 if (start.parent().isValid()) return;
00419
00420 for (int row = BOOKMARKS_ROW; row <= LAST_ROW; ++row) {
00421 QModelIndex section = index(row, 0);
00422
00423 QModelIndex new_start = index(start.row(), start.column(), section);
00424 QModelIndex new_end = index(end.row(), end.column(), section);
00425 emit dataChanged(new_start, new_end);
00426 }
00427 }
00428
00429 void Kickoff::SystemModel::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
00430 {
00431 if (parent.isValid()) return;
00432
00433 for (int row = BOOKMARKS_ROW; row <= LAST_ROW; ++row) {
00434 QModelIndex section = index(row, 0);
00435 beginInsertRows(section, start, end);
00436 }
00437 }
00438
00439 void Kickoff::SystemModel::sourceRowsInserted(const QModelIndex &parent, int , int )
00440 {
00441 if (parent.isValid()) return;
00442
00443 endInsertRows();
00444 }
00445
00446 void Kickoff::SystemModel::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
00447 {
00448 if (parent.isValid()) return;
00449
00450 for (int row = BOOKMARKS_ROW; row <= LAST_ROW; ++row) {
00451 QModelIndex section = index(row, 0);
00452 beginRemoveRows(section, start, end);
00453 }
00454 }
00455
00456 void Kickoff::SystemModel::sourceRowsRemoved(const QModelIndex &parent, int , int )
00457 {
00458 if (parent.isValid()) return;
00459
00460 endRemoveRows();
00461 }
00462
00463 #include "systemmodel.moc"