1/* This file is part of the KDE project
2 Copyright (C) 2007 Kevin Ottens <ervin@kde.org>
3 Copyright (C) 2007 David Faure <faure@kde.org>
4
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 version 2 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18
19*/
20#include "kfileplacesmodel.h"
21#include "kfileplacesitem_p.h"
22#include "kfileplacessharedbookmarks_p.h"
23
24#ifdef _WIN32_WCE
25#include "Windows.h"
26#include "WinBase.h"
27#include <QtCore/QDir>
28#endif
29
30#include <QtCore/QMimeData>
31#include <QtCore/QTimer>
32#include <QtCore/QFile>
33#include <QtGui/QColor>
34#include <QtGui/QAction>
35
36#include <kfileitem.h>
37#include <kglobal.h>
38#include <klocale.h>
39#include <kuser.h>
40#include <kstandarddirs.h>
41#include <kcomponentdata.h>
42#include <kicon.h>
43#include <kmimetype.h>
44#include <kdebug.h>
45
46#include <kbookmarkmanager.h>
47#include <kbookmark.h>
48
49#include <kio/netaccess.h>
50#include <kprotocolinfo.h>
51
52#include <solid/devicenotifier.h>
53#include <solid/storageaccess.h>
54#include <solid/storagedrive.h>
55#include <solid/storagevolume.h>
56#include <solid/opticaldrive.h>
57#include <solid/opticaldisc.h>
58#include <solid/portablemediaplayer.h>
59#include <solid/predicate.h>
60
61class KFilePlacesModel::Private
62{
63public:
64 Private(KFilePlacesModel *self) : q(self), bookmarkManager(0), sharedBookmarks(0) {}
65 ~Private()
66 {
67 delete sharedBookmarks;
68 qDeleteAll(items);
69 }
70
71 KFilePlacesModel *q;
72
73 QList<KFilePlacesItem*> items;
74 QSet<QString> availableDevices;
75 QMap<QObject*, QPersistentModelIndex> setupInProgress;
76
77 Solid::Predicate predicate;
78 KBookmarkManager *bookmarkManager;
79 KFilePlacesSharedBookmarks * sharedBookmarks;
80
81 void reloadAndSignal();
82 QList<KFilePlacesItem *> loadBookmarkList();
83
84 void _k_initDeviceList();
85 void _k_deviceAdded(const QString &udi);
86 void _k_deviceRemoved(const QString &udi);
87 void _k_itemChanged(const QString &udi);
88 void _k_reloadBookmarks();
89 void _k_storageSetupDone(Solid::ErrorType error, QVariant errorData);
90 void _k_storageTeardownDone(Solid::ErrorType error, QVariant errorData);
91};
92
93KFilePlacesModel::KFilePlacesModel(QObject *parent)
94 : QAbstractItemModel(parent), d(new Private(this))
95{
96 const QString file = KStandardDirs::locateLocal("data", "kfileplaces/bookmarks.xml");
97 d->bookmarkManager = KBookmarkManager::managerForFile(file, "kfilePlaces");
98
99 // Let's put some places in there if it's empty. We have a corner case here:
100 // Given you have bookmarked some folders (which have been saved on
101 // ~/.local/share/user-places.xbel (according to freedesktop bookmarks spec), and
102 // deleted the home directory ~/.kde, the call managerForFile() will return the
103 // bookmark manager for the fallback "kfilePlaces", making root.first().isNull() being
104 // false (you have your own items bookmarked), resulting on only being added your own
105 // bookmarks, and not the default ones too. So, we also check if kfileplaces/bookmarks.xml
106 // file exists, and if it doesn't, we also add the default places. (ereslibre)
107 KBookmarkGroup root = d->bookmarkManager->root();
108 if (root.first().isNull() || !QFile::exists(file)) {
109
110 // NOTE: The context for these I18N_NOOP2 calls has to be "KFile System Bookmarks".
111 // The real i18nc call is made later, with this context, so the two must match.
112 //
113 // createSystemBookmark actually does nothing with its third argument,
114 // but we have to give it something so the I18N_NOOP2 calls stay here for now.
115 //
116 // (coles, 13th May 2009)
117
118 KFilePlacesItem::createSystemBookmark(d->bookmarkManager,
119 "Home", I18N_NOOP2("KFile System Bookmarks", "Home"),
120 KUrl(KUser().homeDir()), "user-home");
121 KFilePlacesItem::createSystemBookmark(d->bookmarkManager,
122 "Network", I18N_NOOP2("KFile System Bookmarks", "Network"),
123 KUrl("remote:/"), "network-workgroup");
124#if defined(_WIN32_WCE)
125 // adding drives
126 foreach ( const QFileInfo& info, QDir::drives() ) {
127 QString driveIcon = "drive-harddisk";
128 KFilePlacesItem::createSystemBookmark(d->bookmarkManager,
129 info.absoluteFilePath(), info.absoluteFilePath(),
130 KUrl(info.absoluteFilePath()), driveIcon);
131 }
132#elif !defined(Q_OS_WIN)
133 KFilePlacesItem::createSystemBookmark(d->bookmarkManager,
134 "Root", I18N_NOOP2("KFile System Bookmarks", "Root"),
135 KUrl("/"), "folder-red");
136#endif
137 KFilePlacesItem::createSystemBookmark(d->bookmarkManager,
138 "Trash", I18N_NOOP2("KFile System Bookmarks", "Trash"),
139 KUrl("trash:/"), "user-trash");
140
141 // Force bookmarks to be saved. If on open/save dialog and the bookmarks are not saved, QFile::exists
142 // will always return false, which opening/closing all the time the open/save dialog would case the
143 // bookmarks to be added once each time, having lots of times each bookmark. This forces the defaults
144 // to be saved on the bookmarks.xml file. Of course, the complete list of bookmarks (those that come from
145 // user-places.xbel will be filled later). (ereslibre)
146 d->bookmarkManager->saveAs(file);
147 }
148
149 // create after, so if we have own places, they are added afterwards, in case of equal priorities
150 d->sharedBookmarks = new KFilePlacesSharedBookmarks(d->bookmarkManager);
151
152 QString predicate("[[[[ StorageVolume.ignored == false AND [ StorageVolume.usage == 'FileSystem' OR StorageVolume.usage == 'Encrypted' ]]"
153 " OR "
154 "[ IS StorageAccess AND StorageDrive.driveType == 'Floppy' ]]"
155 " OR "
156 "OpticalDisc.availableContent & 'Audio' ]"
157 " OR "
158 "StorageAccess.ignored == false ]");
159
160 if (KProtocolInfo::isKnownProtocol("mtp")) {
161 predicate.prepend("[");
162 predicate.append(" OR PortableMediaPlayer.supportedProtocols == 'mtp']");
163 }
164
165 d->predicate = Solid::Predicate::fromString(predicate);
166
167 Q_ASSERT(d->predicate.isValid());
168
169 connect(d->bookmarkManager, SIGNAL(changed(QString,QString)),
170 this, SLOT(_k_reloadBookmarks()));
171 connect(d->bookmarkManager, SIGNAL(bookmarksChanged(QString)),
172 this, SLOT(_k_reloadBookmarks()));
173
174 d->_k_reloadBookmarks();
175 QTimer::singleShot(0, this, SLOT(_k_initDeviceList()));
176}
177
178KFilePlacesModel::~KFilePlacesModel()
179{
180 delete d;
181}
182
183KUrl KFilePlacesModel::url(const QModelIndex &index) const
184{
185 return KUrl(data(index, UrlRole).toUrl());
186}
187
188bool KFilePlacesModel::setupNeeded(const QModelIndex &index) const
189{
190 return data(index, SetupNeededRole).toBool();
191}
192
193KIcon KFilePlacesModel::icon(const QModelIndex &index) const
194{
195 return KIcon(data(index, Qt::DecorationRole).value<QIcon>());
196}
197
198QString KFilePlacesModel::text(const QModelIndex &index) const
199{
200 return data(index, Qt::DisplayRole).toString();
201}
202
203bool KFilePlacesModel::isHidden(const QModelIndex &index) const
204{
205 return data(index, HiddenRole).toBool();
206}
207
208bool KFilePlacesModel::isDevice(const QModelIndex &index) const
209{
210 if (!index.isValid())
211 return false;
212
213 KFilePlacesItem *item = static_cast<KFilePlacesItem*>(index.internalPointer());
214
215 return item->isDevice();
216}
217
218Solid::Device KFilePlacesModel::deviceForIndex(const QModelIndex &index) const
219{
220 if (!index.isValid())
221 return Solid::Device();
222
223 KFilePlacesItem *item = static_cast<KFilePlacesItem*>(index.internalPointer());
224
225 if (item->isDevice()) {
226 return item->device();
227 } else {
228 return Solid::Device();
229 }
230}
231
232KBookmark KFilePlacesModel::bookmarkForIndex(const QModelIndex &index) const
233{
234 if (!index.isValid())
235 return KBookmark();
236
237 KFilePlacesItem *item = static_cast<KFilePlacesItem*>(index.internalPointer());
238
239 if (!item->isDevice()) {
240 return item->bookmark();
241 } else {
242 return KBookmark();
243 }
244}
245
246QVariant KFilePlacesModel::data(const QModelIndex &index, int role) const
247{
248 if (!index.isValid())
249 return QVariant();
250
251 KFilePlacesItem *item = static_cast<KFilePlacesItem*>(index.internalPointer());
252 return item->data(role);
253}
254
255QModelIndex KFilePlacesModel::index(int row, int column, const QModelIndex &parent) const
256{
257 if (row<0 || column!=0 || row>=d->items.size())
258 return QModelIndex();
259
260 if (parent.isValid())
261 return QModelIndex();
262
263 return createIndex(row, column, d->items.at(row));
264}
265
266QModelIndex KFilePlacesModel::parent(const QModelIndex &child) const
267{
268 Q_UNUSED(child);
269 return QModelIndex();
270}
271
272int KFilePlacesModel::rowCount(const QModelIndex &parent) const
273{
274 if (parent.isValid())
275 return 0;
276 else
277 return d->items.size();
278}
279
280int KFilePlacesModel::columnCount(const QModelIndex &parent) const
281{
282 Q_UNUSED(parent)
283 // We only know 1 piece of information for a particular entry
284 return 1;
285}
286
287QModelIndex KFilePlacesModel::closestItem(const KUrl &url) const
288{
289 int foundRow = -1;
290 int maxLength = 0;
291
292 // Search the item which is equal to the URL or at least is a parent URL.
293 // If there are more than one possible item URL candidates, choose the item
294 // which covers the bigger range of the URL.
295 for (int row = 0; row<d->items.size(); ++row) {
296 KFilePlacesItem *item = d->items[row];
297 KUrl itemUrl = KUrl(item->data(UrlRole).toUrl());
298
299 if (itemUrl.isParentOf(url)) {
300 const int length = itemUrl.prettyUrl().length();
301 if (length > maxLength) {
302 foundRow = row;
303 maxLength = length;
304 }
305 }
306 }
307
308 if (foundRow==-1)
309 return QModelIndex();
310 else
311 return createIndex(foundRow, 0, d->items[foundRow]);
312}
313
314void KFilePlacesModel::Private::_k_initDeviceList()
315{
316 Solid::DeviceNotifier *notifier = Solid::DeviceNotifier::instance();
317
318 connect(notifier, SIGNAL(deviceAdded(QString)),
319 q, SLOT(_k_deviceAdded(QString)));
320 connect(notifier, SIGNAL(deviceRemoved(QString)),
321 q, SLOT(_k_deviceRemoved(QString)));
322
323 const QList<Solid::Device> &deviceList = Solid::Device::listFromQuery(predicate);
324
325 foreach(const Solid::Device &device, deviceList) {
326 availableDevices << device.udi();
327 }
328
329 _k_reloadBookmarks();
330}
331
332void KFilePlacesModel::Private::_k_deviceAdded(const QString &udi)
333{
334 Solid::Device d(udi);
335
336 if (predicate.matches(d)) {
337 availableDevices << udi;
338 _k_reloadBookmarks();
339 }
340}
341
342void KFilePlacesModel::Private::_k_deviceRemoved(const QString &udi)
343{
344 if (availableDevices.contains(udi)) {
345 availableDevices.remove(udi);
346 _k_reloadBookmarks();
347 }
348}
349
350void KFilePlacesModel::Private::_k_itemChanged(const QString &id)
351{
352 for (int row = 0; row<items.size(); ++row) {
353 if (items.at(row)->id()==id) {
354 QModelIndex index = q->index(row, 0);
355 emit q->dataChanged(index, index);
356 }
357 }
358}
359
360void KFilePlacesModel::Private::_k_reloadBookmarks()
361{
362 QList<KFilePlacesItem*> currentItems = loadBookmarkList();
363
364 QList<KFilePlacesItem*>::Iterator it_i = items.begin();
365 QList<KFilePlacesItem*>::Iterator it_c = currentItems.begin();
366
367 QList<KFilePlacesItem*>::Iterator end_i = items.end();
368 QList<KFilePlacesItem*>::Iterator end_c = currentItems.end();
369
370 while (it_i!=end_i || it_c!=end_c) {
371 if (it_i==end_i && it_c!=end_c) {
372 int row = items.count();
373
374 q->beginInsertRows(QModelIndex(), row, row);
375 it_i = items.insert(it_i, *it_c);
376 ++it_i;
377 it_c = currentItems.erase(it_c);
378
379 end_i = items.end();
380 end_c = currentItems.end();
381 q->endInsertRows();
382
383 } else if (it_i!=end_i && it_c==end_c) {
384 int row = items.indexOf(*it_i);
385
386 q->beginRemoveRows(QModelIndex(), row, row);
387 delete *it_i;
388 it_i = items.erase(it_i);
389
390 end_i = items.end();
391 end_c = currentItems.end();
392 q->endRemoveRows();
393
394 } else if ((*it_i)->id()==(*it_c)->id()) {
395 bool shouldEmit = !((*it_i)->bookmark()==(*it_c)->bookmark());
396 (*it_i)->setBookmark((*it_c)->bookmark());
397 if (shouldEmit) {
398 int row = items.indexOf(*it_i);
399 QModelIndex idx = q->index(row, 0);
400 emit q->dataChanged(idx, idx);
401 }
402 ++it_i;
403 ++it_c;
404 } else if ((*it_i)->id()!=(*it_c)->id()) {
405 int row = items.indexOf(*it_i);
406
407 if (it_i+1!=end_i && (*(it_i+1))->id()==(*it_c)->id()) { // if the next one matches, it's a remove
408 q->beginRemoveRows(QModelIndex(), row, row);
409 delete *it_i;
410 it_i = items.erase(it_i);
411
412 end_i = items.end();
413 end_c = currentItems.end();
414 q->endRemoveRows();
415 } else {
416 q->beginInsertRows(QModelIndex(), row, row);
417 it_i = items.insert(it_i, *it_c);
418 ++it_i;
419 it_c = currentItems.erase(it_c);
420
421 end_i = items.end();
422 end_c = currentItems.end();
423 q->endInsertRows();
424 }
425 }
426 }
427
428 qDeleteAll(currentItems);
429 currentItems.clear();
430}
431
432QList<KFilePlacesItem *> KFilePlacesModel::Private::loadBookmarkList()
433{
434 QList<KFilePlacesItem*> items;
435
436 KBookmarkGroup root = bookmarkManager->root();
437 KBookmark bookmark = root.first();
438 QSet<QString> devices = availableDevices;
439
440 while (!bookmark.isNull()) {
441 QString udi = bookmark.metaDataItem("UDI");
442 QString appName = bookmark.metaDataItem("OnlyInApp");
443 bool deviceAvailable = devices.remove(udi);
444
445 bool allowedHere = appName.isEmpty() || (appName==KGlobal::mainComponent().componentName());
446
447 if ((udi.isEmpty() && allowedHere) || deviceAvailable) {
448 KFilePlacesItem *item;
449 if (deviceAvailable) {
450 item = new KFilePlacesItem(bookmarkManager, bookmark.address(), udi);
451 // TODO: Update bookmark internal element
452 } else {
453 item = new KFilePlacesItem(bookmarkManager, bookmark.address());
454 }
455 connect(item, SIGNAL(itemChanged(QString)),
456 q, SLOT(_k_itemChanged(QString)));
457 items << item;
458 }
459
460 bookmark = root.next(bookmark);
461 }
462
463 // Add bookmarks for the remaining devices, they were previously unknown
464 foreach (const QString &udi, devices) {
465 bookmark = KFilePlacesItem::createDeviceBookmark(bookmarkManager, udi);
466 if (!bookmark.isNull()) {
467 KFilePlacesItem *item = new KFilePlacesItem(bookmarkManager,
468 bookmark.address(), udi);
469 connect(item, SIGNAL(itemChanged(QString)),
470 q, SLOT(_k_itemChanged(QString)));
471 // TODO: Update bookmark internal element
472 items << item;
473 }
474 }
475
476 return items;
477}
478
479void KFilePlacesModel::Private::reloadAndSignal()
480{
481 bookmarkManager->emitChanged(bookmarkManager->root()); // ... we'll get relisted anyway
482}
483
484Qt::DropActions KFilePlacesModel::supportedDropActions() const
485{
486 return Qt::ActionMask;
487}
488
489Qt::ItemFlags KFilePlacesModel::flags(const QModelIndex &index) const
490{
491 Qt::ItemFlags res = Qt::ItemIsSelectable|Qt::ItemIsEnabled;
492
493 if (index.isValid())
494 res|= Qt::ItemIsDragEnabled;
495
496 if (!index.isValid())
497 res|= Qt::ItemIsDropEnabled;
498
499 return res;
500}
501
502static QString _k_internalMimetype(const KFilePlacesModel * const self)
503{
504 return QString("application/x-kfileplacesmodel-")+QString::number((qptrdiff)self);
505}
506
507QStringList KFilePlacesModel::mimeTypes() const
508{
509 QStringList types;
510
511 types << _k_internalMimetype(this) << "text/uri-list";
512
513 return types;
514}
515
516QMimeData *KFilePlacesModel::mimeData(const QModelIndexList &indexes) const
517{
518 KUrl::List urls;
519 QByteArray itemData;
520
521 QDataStream stream(&itemData, QIODevice::WriteOnly);
522
523 foreach (const QModelIndex &index, indexes) {
524 KUrl itemUrl = url(index);
525 if (itemUrl.isValid())
526 urls << itemUrl;
527 stream << index.row();
528 }
529
530 QMimeData *mimeData = new QMimeData();
531
532 if (!urls.isEmpty())
533 urls.populateMimeData(mimeData);
534
535 mimeData->setData(_k_internalMimetype(this), itemData);
536
537 return mimeData;
538}
539
540bool KFilePlacesModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
541 int row, int column, const QModelIndex &parent)
542{
543 if (action == Qt::IgnoreAction)
544 return true;
545
546 if (column > 0)
547 return false;
548
549 if (row==-1 && parent.isValid()) {
550 return false; // Don't allow to move an item onto another one,
551 // too easy for the user to mess something up
552 // If we really really want to allow copying files this way,
553 // let's do it in the views to get the good old drop menu
554 }
555
556
557 KBookmark afterBookmark;
558
559 if (row==-1) {
560 // The dropped item is moved or added to the last position
561
562 KFilePlacesItem *lastItem = d->items.last();
563 afterBookmark = lastItem->bookmark();
564
565 } else {
566 // The dropped item is moved or added before position 'row', ie after position 'row-1'
567
568 if (row>0) {
569 KFilePlacesItem *afterItem = d->items[row-1];
570 afterBookmark = afterItem->bookmark();
571 }
572 }
573
574 if (data->hasFormat(_k_internalMimetype(this))) {
575 // The operation is an internal move
576 QByteArray itemData = data->data(_k_internalMimetype(this));
577 QDataStream stream(&itemData, QIODevice::ReadOnly);
578 int itemRow;
579
580 stream >> itemRow;
581
582 KFilePlacesItem *item = d->items[itemRow];
583 KBookmark bookmark = item->bookmark();
584
585 int destRow = row == -1 ? d->items.count() : row;
586 // The item is not moved when the drop indicator is on either item edge
587 if (itemRow == destRow || itemRow + 1 == destRow) {
588 return false;
589 }
590
591 beginMoveRows(QModelIndex(), itemRow, itemRow, QModelIndex(), destRow);
592 d->bookmarkManager->root().moveBookmark(bookmark, afterBookmark);
593 // Move item ourselves so that _k_reloadBookmarks() does not consider
594 // the move as a remove + insert.
595 //
596 // 2nd argument of QList::move() expects the final destination index,
597 // but 'row' is the value of the destination index before the moved
598 // item has been removed from its original position. That is why we
599 // adjust if necessary.
600 d->items.move(itemRow, itemRow < destRow ? (destRow - 1) : destRow);
601 endMoveRows();
602 } else if (data->hasFormat("text/uri-list")) {
603 // The operation is an add
604 KUrl::List urls = KUrl::List::fromMimeData(data);
605
606 KBookmarkGroup group = d->bookmarkManager->root();
607
608 foreach (const KUrl &url, urls) {
609 // TODO: use KIO::stat in order to get the UDS_DISPLAY_NAME too
610 KMimeType::Ptr mimetype = KMimeType::mimeType(KIO::NetAccess::mimetype(url, 0));
611
612 if (!mimetype) {
613 kWarning() << "URL not added to Places as mimetype could not be determined!";
614 continue;
615 }
616
617 if (!mimetype->is("inode/directory")) {
618 // Only directories are allowed
619 continue;
620 }
621
622 KBookmark bookmark = KFilePlacesItem::createBookmark(d->bookmarkManager,
623 url.fileName(), url,
624 mimetype->iconName(url));
625 group.moveBookmark(bookmark, afterBookmark);
626 afterBookmark = bookmark;
627 }
628
629 } else {
630 // Oops, shouldn't happen thanks to mimeTypes()
631 kWarning() << ": received wrong mimedata, " << data->formats();
632 return false;
633 }
634
635 d->reloadAndSignal();
636
637 return true;
638}
639
640void KFilePlacesModel::addPlace(const QString &text, const KUrl &url,
641 const QString &iconName, const QString &appName)
642{
643 addPlace(text, url, iconName, appName, QModelIndex());
644}
645
646void KFilePlacesModel::addPlace(const QString &text, const KUrl &url,
647 const QString &iconName, const QString &appName,
648 const QModelIndex &after)
649{
650 KBookmark bookmark = KFilePlacesItem::createBookmark(d->bookmarkManager,
651 text, url, iconName);
652
653 if (!appName.isEmpty()) {
654 bookmark.setMetaDataItem("OnlyInApp", appName);
655 }
656
657 if (after.isValid()) {
658 KFilePlacesItem *item = static_cast<KFilePlacesItem*>(after.internalPointer());
659 d->bookmarkManager->root().moveBookmark(bookmark, item->bookmark());
660 }
661
662 d->reloadAndSignal();
663}
664
665void KFilePlacesModel::editPlace(const QModelIndex &index, const QString &text, const KUrl &url,
666 const QString &iconName, const QString &appName)
667{
668 if (!index.isValid()) return;
669
670 KFilePlacesItem *item = static_cast<KFilePlacesItem*>(index.internalPointer());
671
672 if (item->isDevice()) return;
673
674 KBookmark bookmark = item->bookmark();
675
676 if (bookmark.isNull()) return;
677
678 bookmark.setFullText(text);
679 bookmark.setUrl(url);
680 bookmark.setIcon(iconName);
681 bookmark.setMetaDataItem("OnlyInApp", appName);
682
683 d->reloadAndSignal();
684 emit dataChanged(index, index);
685}
686
687void KFilePlacesModel::removePlace(const QModelIndex &index) const
688{
689 if (!index.isValid()) return;
690
691 KFilePlacesItem *item = static_cast<KFilePlacesItem*>(index.internalPointer());
692
693 if (item->isDevice()) return;
694
695 KBookmark bookmark = item->bookmark();
696
697 if (bookmark.isNull()) return;
698
699 d->bookmarkManager->root().deleteBookmark(bookmark);
700 d->reloadAndSignal();
701}
702
703void KFilePlacesModel::setPlaceHidden(const QModelIndex &index, bool hidden)
704{
705 if (!index.isValid()) return;
706
707 KFilePlacesItem *item = static_cast<KFilePlacesItem*>(index.internalPointer());
708
709 KBookmark bookmark = item->bookmark();
710
711 if (bookmark.isNull()) return;
712
713 bookmark.setMetaDataItem("IsHidden", (hidden ? "true" : "false"));
714
715 d->reloadAndSignal();
716 emit dataChanged(index, index);
717}
718
719int KFilePlacesModel::hiddenCount() const
720{
721 int rows = rowCount();
722 int hidden = 0;
723
724 for (int i=0; i<rows; ++i) {
725 if (isHidden(index(i, 0))) {
726 hidden++;
727 }
728 }
729
730 return hidden;
731}
732
733QAction *KFilePlacesModel::teardownActionForIndex(const QModelIndex &index) const
734{
735 Solid::Device device = deviceForIndex(index);
736
737 if (device.is<Solid::StorageAccess>() && device.as<Solid::StorageAccess>()->isAccessible()) {
738
739 Solid::StorageDrive *drive = device.as<Solid::StorageDrive>();
740
741 if (drive==0) {
742 drive = device.parent().as<Solid::StorageDrive>();
743 }
744
745 bool hotpluggable = false;
746 bool removable = false;
747
748 if (drive!=0) {
749 hotpluggable = drive->isHotpluggable();
750 removable = drive->isRemovable();
751 }
752
753 QString iconName;
754 QString text;
755 QString label = data(index, Qt::DisplayRole).toString().replace('&',"&&");
756
757 if (device.is<Solid::OpticalDisc>()) {
758 text = i18n("&Release '%1'", label);
759 } else if (removable || hotpluggable) {
760 text = i18n("&Safely Remove '%1'", label);
761 iconName = "media-eject";
762 } else {
763 text = i18n("&Unmount '%1'", label);
764 iconName = "media-eject";
765 }
766
767 if (!iconName.isEmpty()) {
768 return new QAction(KIcon(iconName), text, 0);
769 } else {
770 return new QAction(text, 0);
771 }
772 }
773
774 return 0;
775}
776
777QAction *KFilePlacesModel::ejectActionForIndex(const QModelIndex &index) const
778{
779 Solid::Device device = deviceForIndex(index);
780
781 if (device.is<Solid::OpticalDisc>()) {
782
783 QString label = data(index, Qt::DisplayRole).toString().replace('&',"&&");
784 QString text = i18n("&Eject '%1'", label);
785
786 return new QAction(KIcon("media-eject"), text, 0);
787 }
788
789 return 0;
790}
791
792void KFilePlacesModel::requestTeardown(const QModelIndex &index)
793{
794 Solid::Device device = deviceForIndex(index);
795 Solid::StorageAccess *access = device.as<Solid::StorageAccess>();
796
797 if (access!=0) {
798 connect(access, SIGNAL(teardownDone(Solid::ErrorType,QVariant,QString)),
799 this, SLOT(_k_storageTeardownDone(Solid::ErrorType,QVariant)));
800
801 access->teardown();
802 }
803}
804
805void KFilePlacesModel::requestEject(const QModelIndex &index)
806{
807 Solid::Device device = deviceForIndex(index);
808
809 Solid::OpticalDrive *drive = device.parent().as<Solid::OpticalDrive>();
810
811 if (drive!=0) {
812 connect(drive, SIGNAL(ejectDone(Solid::ErrorType,QVariant,QString)),
813 this, SLOT(_k_storageTeardownDone(Solid::ErrorType,QVariant)));
814
815 drive->eject();
816 } else {
817 QString label = data(index, Qt::DisplayRole).toString().replace('&',"&&");
818 QString message = i18n("The device '%1' is not a disk and cannot be ejected.", label);
819 emit errorMessage(message);
820 }
821}
822
823void KFilePlacesModel::requestSetup(const QModelIndex &index)
824{
825 Solid::Device device = deviceForIndex(index);
826
827 if (device.is<Solid::StorageAccess>()
828 && !d->setupInProgress.contains(device.as<Solid::StorageAccess>())
829 && !device.as<Solid::StorageAccess>()->isAccessible()) {
830
831 Solid::StorageAccess *access = device.as<Solid::StorageAccess>();
832
833 d->setupInProgress[access] = index;
834
835 connect(access, SIGNAL(setupDone(Solid::ErrorType,QVariant,QString)),
836 this, SLOT(_k_storageSetupDone(Solid::ErrorType,QVariant)));
837
838 access->setup();
839 }
840}
841
842void KFilePlacesModel::Private::_k_storageSetupDone(Solid::ErrorType error, QVariant errorData)
843{
844 QPersistentModelIndex index = setupInProgress.take(q->sender());
845
846 if (!index.isValid()) {
847 return;
848 }
849
850 if (!error) {
851 emit q->setupDone(index, true);
852 } else {
853 if (errorData.isValid()) {
854 emit q->errorMessage(i18n("An error occurred while accessing '%1', the system responded: %2",
855 q->text(index),
856 errorData.toString()));
857 } else {
858 emit q->errorMessage(i18n("An error occurred while accessing '%1'",
859 q->text(index)));
860 }
861 emit q->setupDone(index, false);
862 }
863
864}
865
866void KFilePlacesModel::Private::_k_storageTeardownDone(Solid::ErrorType error, QVariant errorData)
867{
868 if (error && errorData.isValid()) {
869 emit q->errorMessage(errorData.toString());
870 }
871}
872
873#include "kfileplacesmodel.moc"
874