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