00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "kfileplacesview.h"
00022 #include "kfileplacesview_p.h"
00023
00024 #include <QtCore/QTimeLine>
00025 #include <QtCore/QTimer>
00026 #include <QtGui/QMenu>
00027 #include <QtGui/QPainter>
00028 #include <QtGui/QAbstractItemDelegate>
00029 #include <QtGui/QKeyEvent>
00030 #include <QtGui/QApplication>
00031
00032 #include <kdebug.h>
00033
00034 #include <kcomponentdata.h>
00035 #include <kdirnotify.h>
00036 #include <kglobalsettings.h>
00037 #include <kiconloader.h>
00038 #include <klocale.h>
00039 #include <kmessagebox.h>
00040 #include <knotification.h>
00041 #include <kio/job.h>
00042 #include <kio/jobuidelegate.h>
00043 #include <kjob.h>
00044 #include <kcapacitybar.h>
00045 #include <kdiskfreespaceinfo.h>
00046 #include <solid/storageaccess.h>
00047 #include <solid/storagedrive.h>
00048 #include <solid/storagevolume.h>
00049 #include <solid/opticaldrive.h>
00050 #include <solid/opticaldisc.h>
00051
00052 #include "kfileplaceeditdialog.h"
00053 #include "kfileplacesmodel.h"
00054
00055 #define LATERAL_MARGIN 4
00056 #define CAPACITYBAR_HEIGHT 6
00057
00058 class KFilePlacesViewDelegate : public QAbstractItemDelegate
00059 {
00060 public:
00061 KFilePlacesViewDelegate(KFilePlacesView *parent);
00062 virtual ~KFilePlacesViewDelegate();
00063 virtual QSize sizeHint(const QStyleOptionViewItem &option,
00064 const QModelIndex &index) const;
00065 virtual void paint(QPainter *painter,
00066 const QStyleOptionViewItem &option,
00067 const QModelIndex &index) const;
00068
00069 int iconSize() const;
00070 void setIconSize(int newSize);
00071
00072 void addAppearingItem(const QModelIndex &index);
00073 void setAppearingItemProgress(qreal value);
00074 void addDisappearingItem(const QModelIndex &index);
00075 void setDisappearingItemProgress(qreal value);
00076
00077 void setShowHoverIndication(bool show);
00078
00079 void addFadeAnimation(const QModelIndex &index, QTimeLine *timeLine);
00080 void removeFadeAnimation(const QModelIndex &index);
00081 QModelIndex indexForFadeAnimation(QTimeLine *timeLine) const;
00082 QTimeLine *fadeAnimationForIndex(const QModelIndex &index) const;
00083
00084 float contentsOpacity(const QModelIndex &index) const;
00085
00086 private:
00087 KFilePlacesView *m_view;
00088 int m_iconSize;
00089
00090 QList<QPersistentModelIndex> m_appearingItems;
00091 int m_appearingIconSize;
00092 qreal m_appearingOpacity;
00093
00094 QList<QPersistentModelIndex> m_disappearingItems;
00095 int m_disappearingIconSize;
00096 qreal m_disappearingOpacity;
00097
00098 bool m_showHoverIndication;
00099
00100 QMap<QPersistentModelIndex, QTimeLine*> m_timeLineMap;
00101 QMap<QTimeLine*, QPersistentModelIndex> m_timeLineInverseMap;
00102 };
00103
00104 KFilePlacesViewDelegate::KFilePlacesViewDelegate(KFilePlacesView *parent) :
00105 QAbstractItemDelegate(parent),
00106 m_view(parent),
00107 m_iconSize(48),
00108 m_appearingIconSize(0),
00109 m_appearingOpacity(0.0),
00110 m_disappearingIconSize(0),
00111 m_disappearingOpacity(0.0),
00112 m_showHoverIndication(true)
00113 {
00114 }
00115
00116 KFilePlacesViewDelegate::~KFilePlacesViewDelegate()
00117 {
00118 }
00119
00120 QSize KFilePlacesViewDelegate::sizeHint(const QStyleOptionViewItem &option,
00121 const QModelIndex &index) const
00122 {
00123 int iconSize = m_iconSize;
00124 if (m_appearingItems.contains(index)) {
00125 iconSize = m_appearingIconSize;
00126 } else if (m_disappearingItems.contains(index)) {
00127 iconSize = m_disappearingIconSize;
00128 }
00129
00130 const KFilePlacesModel *filePlacesModel = static_cast<const KFilePlacesModel*>(index.model());
00131 Solid::Device device = filePlacesModel->deviceForIndex(index);
00132
00133 return QSize(option.rect.width(), 2 * KDialog::marginHint() + qMax(iconSize, option.fontMetrics.height()));
00134 }
00135
00136 void KFilePlacesViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
00137 {
00138 painter->save();
00139
00140 if (m_appearingItems.contains(index)) {
00141 painter->setOpacity(m_appearingOpacity);
00142 } else if (m_disappearingItems.contains(index)) {
00143 painter->setOpacity(m_disappearingOpacity);
00144 }
00145
00146 QStyleOptionViewItemV4 opt = option;
00147 if (!m_showHoverIndication) {
00148 opt.state &= ~QStyle::State_MouseOver;
00149 }
00150 QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter);
00151 const KFilePlacesModel *placesModel = static_cast<const KFilePlacesModel*>(index.model());
00152 bool isRemovableDevice = false;
00153 Solid::Device device;
00154 if (placesModel->isDevice(index)) {
00155 device = placesModel->deviceForIndex(index);
00156 if ((device.is<Solid::StorageAccess>() && device.as<Solid::StorageAccess>()->isAccessible() ||
00157 device.parent().is<Solid::StorageAccess>() && device.parent().as<Solid::StorageAccess>()->isAccessible()) &&
00158 (device.is<Solid::StorageDrive>() && device.as<Solid::StorageDrive>()->isRemovable() ||
00159 device.parent().is<Solid::StorageDrive>() && device.parent().as<Solid::StorageDrive>()->isRemovable()) &&
00160 (device.is<Solid::StorageDrive>() && device.as<Solid::StorageDrive>()->driveType() != Solid::StorageDrive::CdromDrive ||
00161 device.parent().is<Solid::StorageDrive>() && device.parent().as<Solid::StorageDrive>()->driveType() != Solid::StorageDrive::CdromDrive)) {
00162 isRemovableDevice = true;
00163 }
00164 }
00165
00166 bool isLTR = option.direction == Qt::LeftToRight;
00167
00168 QIcon icon = index.model()->data(index, Qt::DecorationRole).value<QIcon>();
00169 QPixmap pm = icon.pixmap(m_iconSize, m_iconSize);
00170 QPoint point(isLTR ? option.rect.left() + LATERAL_MARGIN
00171 : option.rect.right() - LATERAL_MARGIN - m_iconSize, option.rect.top() + (option.rect.height() - m_iconSize) / 2);
00172 painter->drawPixmap(point, pm);
00173
00174 if (option.state & QStyle::State_Selected) {
00175 painter->setPen(option.palette.highlightedText().color());
00176 }
00177
00178 QRect rectText;
00179 if (isRemovableDevice) {
00180 painter->save();
00181 painter->setOpacity(painter->opacity() * contentsOpacity(index));
00182
00183 int height = option.fontMetrics.height() + CAPACITYBAR_HEIGHT;
00184 rectText = QRect(isLTR ? m_iconSize + LATERAL_MARGIN * 2 + option.rect.left()
00185 : 0, option.rect.top() + (option.rect.height() / 2 - height / 2), option.rect.width() - m_iconSize - LATERAL_MARGIN * 2, option.fontMetrics.height());
00186 painter->drawText(rectText, Qt::AlignLeft | Qt::AlignTop, option.fontMetrics.elidedText(index.model()->data(index).toString(), Qt::ElideRight, rectText.width()));
00187 QRect capacityRect(isLTR ? rectText.x() : LATERAL_MARGIN, rectText.bottom() - 1, rectText.width() - LATERAL_MARGIN, CAPACITYBAR_HEIGHT);
00188 Solid::StorageAccess *storage = device.as<Solid::StorageAccess>();
00189 KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo(storage->filePath());
00190 if (info.size()) {
00191 KCapacityBar capacityBar(KCapacityBar::DrawTextInline);
00192 capacityBar.setValue((info.used() * 100) / info.size());
00193 capacityBar.drawCapacityBar(painter, capacityRect);
00194 }
00195
00196 painter->restore();
00197
00198 painter->save();
00199 painter->setOpacity(painter->opacity() * (1 - contentsOpacity(index)));
00200 }
00201
00202 rectText = QRect(isLTR ? m_iconSize + LATERAL_MARGIN * 2 + option.rect.left()
00203 : 0, option.rect.top(), option.rect.width() - m_iconSize - LATERAL_MARGIN * 2, option.rect.height());
00204 painter->drawText(rectText, Qt::AlignLeft | Qt::AlignVCenter, option.fontMetrics.elidedText(index.model()->data(index).toString(), Qt::ElideRight, rectText.width()));
00205
00206 if (isRemovableDevice) {
00207 painter->restore();
00208 }
00209
00210 painter->restore();
00211 }
00212
00213 int KFilePlacesViewDelegate::iconSize() const
00214 {
00215 return m_iconSize;
00216 }
00217
00218 void KFilePlacesViewDelegate::setIconSize(int newSize)
00219 {
00220 m_iconSize = newSize;
00221 }
00222
00223 void KFilePlacesViewDelegate::addAppearingItem(const QModelIndex &index)
00224 {
00225 m_appearingItems << index;
00226 }
00227
00228 void KFilePlacesViewDelegate::setAppearingItemProgress(qreal value)
00229 {
00230 if (value<=0.25) {
00231 m_appearingOpacity = 0.0;
00232 m_appearingIconSize = iconSize()*value*4;
00233
00234 if (m_appearingIconSize>=m_iconSize) {
00235 m_appearingIconSize = m_iconSize;
00236 }
00237 } else {
00238 m_appearingIconSize = m_iconSize;
00239 m_appearingOpacity = (value-0.25)*4/3;
00240
00241 if (value>=1.0) {
00242 m_appearingItems.clear();
00243 }
00244 }
00245 }
00246
00247 void KFilePlacesViewDelegate::addDisappearingItem(const QModelIndex &index)
00248 {
00249 m_disappearingItems << index;
00250 }
00251
00252 void KFilePlacesViewDelegate::setDisappearingItemProgress(qreal value)
00253 {
00254 value = 1.0 - value;
00255
00256 if (value<=0.25) {
00257 m_disappearingOpacity = 0.0;
00258 m_disappearingIconSize = iconSize()*value*4;
00259
00260 if (m_disappearingIconSize>=m_iconSize) {
00261 m_disappearingIconSize = m_iconSize;
00262 }
00263
00264 if (value<=0.0) {
00265 m_disappearingItems.clear();
00266 }
00267 } else {
00268 m_disappearingIconSize = m_iconSize;
00269 m_disappearingOpacity = (value-0.25)*4/3;
00270 }
00271 }
00272
00273 void KFilePlacesViewDelegate::setShowHoverIndication(bool show)
00274 {
00275 m_showHoverIndication = show;
00276 }
00277
00278 void KFilePlacesViewDelegate::addFadeAnimation(const QModelIndex &index, QTimeLine *timeLine)
00279 {
00280 m_timeLineMap.insert(index, timeLine);
00281 m_timeLineInverseMap.insert(timeLine, index);
00282 }
00283
00284 void KFilePlacesViewDelegate::removeFadeAnimation(const QModelIndex &index)
00285 {
00286 QTimeLine *timeLine = m_timeLineMap.value(index, 0);
00287 m_timeLineMap.remove(index);
00288 m_timeLineInverseMap.remove(timeLine);
00289 }
00290
00291 QModelIndex KFilePlacesViewDelegate::indexForFadeAnimation(QTimeLine *timeLine) const
00292 {
00293 return m_timeLineInverseMap.value(timeLine, QModelIndex());
00294 }
00295
00296 QTimeLine *KFilePlacesViewDelegate::fadeAnimationForIndex(const QModelIndex &index) const
00297 {
00298 return m_timeLineMap.value(index, 0);
00299 }
00300
00301 float KFilePlacesViewDelegate::contentsOpacity(const QModelIndex &index) const
00302 {
00303 QTimeLine *timeLine = fadeAnimationForIndex(index);
00304 if (timeLine) {
00305 return timeLine->currentValue();
00306 }
00307 return 0;
00308 }
00309
00310 class KFilePlacesView::Private
00311 {
00312 public:
00313 Private(KFilePlacesView *parent) : q(parent), watcher(new KFilePlacesEventWatcher(q)) { }
00314
00315 enum FadeType {
00316 FadeIn = 0,
00317 FadeOut
00318 };
00319
00320 KFilePlacesView * const q;
00321
00322 KUrl currentUrl;
00323 bool autoResizeItems;
00324 bool showAll;
00325 bool smoothItemResizing;
00326 bool dropOnPlace;
00327 bool dragging;
00328 Solid::StorageAccess *lastClickedStorage;
00329 QPersistentModelIndex lastClickedIndex;
00330
00331 QRect dropRect;
00332
00333 void setCurrentIndex(const QModelIndex &index);
00334 void adaptItemSize();
00335 void updateHiddenRows();
00336 bool insertAbove(const QRect &itemRect, const QPoint &pos) const;
00337 bool insertBelow(const QRect &itemRect, const QPoint &pos) const;
00338 int insertIndicatorHeight(int itemHeight) const;
00339 void fadeCapacityBar(const QModelIndex &index, FadeType fadeType);
00340
00341 void _k_placeClicked(const QModelIndex &index);
00342 void _k_placeEntered(const QModelIndex &index);
00343 void _k_placeLeft(const QModelIndex &index);
00344 void _k_storageSetupDone(const QModelIndex &index, bool success);
00345 void _k_adaptItemsUpdate(qreal value);
00346 void _k_itemAppearUpdate(qreal value);
00347 void _k_itemDisappearUpdate(qreal value);
00348 void _k_enableSmoothItemResizing();
00349 void _k_trashUpdated(KJob *job);
00350 void _k_capacityBarFadeValueChanged();
00351 void _k_triggerDevicePolling();
00352
00353 QTimeLine adaptItemsTimeline;
00354 int oldSize, endSize;
00355
00356 QTimeLine itemAppearTimeline;
00357 QTimeLine itemDisappearTimeline;
00358
00359 KFilePlacesEventWatcher *const watcher;
00360 KFilePlacesViewDelegate *delegate;
00361 QTimer pollDevices;
00362 int pollingRequestCount;
00363 };
00364
00365 KFilePlacesView::KFilePlacesView(QWidget *parent)
00366 : QListView(parent), d(new Private(this))
00367 {
00368 d->showAll = false;
00369 d->smoothItemResizing = false;
00370 d->dropOnPlace = false;
00371 d->autoResizeItems = true;
00372 d->dragging = false;
00373 d->lastClickedStorage = 0;
00374 d->pollingRequestCount = 0;
00375 d->delegate = new KFilePlacesViewDelegate(this);
00376
00377 setSelectionRectVisible(false);
00378 setSelectionMode(SingleSelection);
00379
00380 setDragEnabled(true);
00381 setAcceptDrops(true);
00382 setMouseTracking(true);
00383 setDropIndicatorShown(false);
00384 setFrameStyle(QFrame::NoFrame);
00385
00386 setResizeMode(Adjust);
00387 setItemDelegate(d->delegate);
00388
00389 QPalette palette = viewport()->palette();
00390 palette.setColor(viewport()->backgroundRole(), Qt::transparent);
00391 viewport()->setPalette(palette);
00392
00393 connect(this, SIGNAL(clicked(const QModelIndex&)),
00394 this, SLOT(_k_placeClicked(const QModelIndex&)));
00395
00396 connect(&d->adaptItemsTimeline, SIGNAL(valueChanged(qreal)),
00397 this, SLOT(_k_adaptItemsUpdate(qreal)));
00398 d->adaptItemsTimeline.setDuration(500);
00399 d->adaptItemsTimeline.setUpdateInterval(5);
00400 d->adaptItemsTimeline.setCurveShape(QTimeLine::EaseInOutCurve);
00401
00402 connect(&d->itemAppearTimeline, SIGNAL(valueChanged(qreal)),
00403 this, SLOT(_k_itemAppearUpdate(qreal)));
00404 d->itemAppearTimeline.setDuration(500);
00405 d->itemAppearTimeline.setUpdateInterval(5);
00406 d->itemAppearTimeline.setCurveShape(QTimeLine::EaseInOutCurve);
00407
00408 connect(&d->itemDisappearTimeline, SIGNAL(valueChanged(qreal)),
00409 this, SLOT(_k_itemDisappearUpdate(qreal)));
00410 d->itemDisappearTimeline.setDuration(500);
00411 d->itemDisappearTimeline.setUpdateInterval(5);
00412 d->itemDisappearTimeline.setCurveShape(QTimeLine::EaseInOutCurve);
00413
00414 viewport()->installEventFilter(d->watcher);
00415 connect(d->watcher, SIGNAL(entryEntered(const QModelIndex&)),
00416 this, SLOT(_k_placeEntered(const QModelIndex&)));
00417 connect(d->watcher, SIGNAL(entryLeft(const QModelIndex&)),
00418 this, SLOT(_k_placeLeft(const QModelIndex&)));
00419
00420 d->pollDevices.setInterval(5000);
00421 connect(&d->pollDevices, SIGNAL(timeout()), this, SLOT(_k_triggerDevicePolling()));
00422 }
00423
00424 KFilePlacesView::~KFilePlacesView()
00425 {
00426 delete d;
00427 }
00428
00429 void KFilePlacesView::setDropOnPlaceEnabled(bool enabled)
00430 {
00431 d->dropOnPlace = enabled;
00432 }
00433
00434 bool KFilePlacesView::isDropOnPlaceEnabled() const
00435 {
00436 return d->dropOnPlace;
00437 }
00438
00439 void KFilePlacesView::setAutoResizeItemsEnabled(bool enabled)
00440 {
00441 d->autoResizeItems = enabled;
00442 }
00443
00444 bool KFilePlacesView::isAutoResizeItemsEnabled() const
00445 {
00446 return d->autoResizeItems;
00447 }
00448
00449 void KFilePlacesView::setUrl(const KUrl &url)
00450 {
00451 KUrl oldUrl = d->currentUrl;
00452 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
00453
00454 if (placesModel==0) return;
00455
00456 QModelIndex index = placesModel->closestItem(url);
00457 QModelIndex current = selectionModel()->currentIndex();
00458
00459 if (index.isValid()) {
00460 if (current!=index && placesModel->isHidden(current) && !d->showAll) {
00461 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00462 delegate->addDisappearingItem(current);
00463
00464 if (d->itemDisappearTimeline.state()!=QTimeLine::Running) {
00465 delegate->setDisappearingItemProgress(0.0);
00466 d->itemDisappearTimeline.start();
00467 }
00468 }
00469
00470 if (current!=index && placesModel->isHidden(index) && !d->showAll) {
00471 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00472 delegate->addAppearingItem(index);
00473
00474 if (d->itemAppearTimeline.state()!=QTimeLine::Running) {
00475 delegate->setAppearingItemProgress(0.0);
00476 d->itemAppearTimeline.start();
00477 }
00478
00479 setRowHidden(index.row(), false);
00480 }
00481
00482 d->currentUrl = url;
00483 selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
00484 } else {
00485 d->currentUrl = KUrl();
00486 selectionModel()->clear();
00487 }
00488
00489 if (!current.isValid()) {
00490 d->updateHiddenRows();
00491 }
00492 }
00493
00494 void KFilePlacesView::setShowAll(bool showAll)
00495 {
00496 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
00497
00498 if (placesModel==0) return;
00499
00500 d->showAll = showAll;
00501
00502 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00503
00504 int rowCount = placesModel->rowCount();
00505 QModelIndex current = placesModel->closestItem(d->currentUrl);
00506
00507 if (showAll) {
00508 d->updateHiddenRows();
00509
00510 for (int i=0; i<rowCount; ++i) {
00511 QModelIndex index = placesModel->index(i, 0);
00512 if (index!=current && placesModel->isHidden(index)) {
00513 delegate->addAppearingItem(index);
00514 }
00515 }
00516
00517 if (d->itemAppearTimeline.state()!=QTimeLine::Running) {
00518 delegate->setAppearingItemProgress(0.0);
00519 d->itemAppearTimeline.start();
00520 }
00521 } else {
00522 for (int i=0; i<rowCount; ++i) {
00523 QModelIndex index = placesModel->index(i, 0);
00524 if (index!=current && placesModel->isHidden(index)) {
00525 delegate->addDisappearingItem(index);
00526 }
00527 }
00528
00529 if (d->itemDisappearTimeline.state()!=QTimeLine::Running) {
00530 delegate->setDisappearingItemProgress(0.0);
00531 d->itemDisappearTimeline.start();
00532 }
00533 }
00534 }
00535
00536 void KFilePlacesView::contextMenuEvent(QContextMenuEvent *event)
00537 {
00538 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
00539 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00540
00541 if (placesModel==0) return;
00542
00543 QModelIndex index = indexAt(event->pos());
00544 QString label = placesModel->text(index).replace('&',"&&");
00545
00546 QMenu menu;
00547
00548 QAction *edit = 0;
00549 QAction *hide = 0;
00550 QAction *emptyTrash = 0;
00551 QAction* eject = 0;
00552 QAction* teardown = 0;
00553 QAction* add = 0;
00554
00555 if (index.isValid()) {
00556 if (!placesModel->isDevice(index)) {
00557 if (placesModel->url(index) == KUrl("trash:/")) {
00558 emptyTrash = menu.addAction(KIcon("trash-empty"), i18nc("@action:inmenu", "Empty Trash"));
00559 KConfig trashConfig("trashrc", KConfig::SimpleConfig);
00560 emptyTrash->setEnabled(!trashConfig.group("Status").readEntry("Empty", true));
00561 menu.addSeparator();
00562 }
00563
00564 edit = menu.addAction(KIcon("document-properties"), i18n("&Edit '%1'...", label));
00565 } else {
00566 eject = placesModel->ejectActionForIndex(index);
00567 if (eject!=0) {
00568 eject->setParent(&menu);
00569 menu.addAction(eject);
00570 }
00571
00572 teardown = placesModel->teardownActionForIndex(index);
00573 if (teardown!=0) {
00574 teardown->setParent(&menu);
00575 menu.addAction(teardown);
00576 }
00577
00578 if (teardown!=0 || eject!=0) {
00579 menu.addSeparator();
00580 }
00581 }
00582
00583 hide = menu.addAction(i18n("&Hide '%1'", label));
00584 hide->setCheckable(true);
00585 hide->setChecked(placesModel->isHidden(index));
00586 } else {
00587 add = menu.addAction(KIcon("document-new"), i18n("Add Entry..."));
00588 }
00589
00590 QAction *showAll = 0;
00591 if (placesModel->hiddenCount()>0) {
00592 showAll = menu.addAction(i18n("&Show All Entries"));
00593 showAll->setCheckable(true);
00594 showAll->setChecked(d->showAll);
00595 }
00596
00597 QAction* remove = 0;
00598 if (index.isValid()) {
00599 if (!placesModel->isDevice(index)) {
00600 menu.addSeparator();
00601 remove = menu.addAction( KIcon("edit-delete"), i18n("&Remove '%1'", label));
00602 }
00603 }
00604
00605 if (menu.isEmpty()) return;
00606
00607 QAction *result = menu.exec(event->globalPos());
00608
00609 if (emptyTrash != 0 && result == emptyTrash) {
00610 const QString text = i18nc("@info", "Do you really want to empty the Trash? All items will be deleted.");
00611 const bool del = KMessageBox::warningContinueCancel(window(),
00612 text,
00613 QString(),
00614 KGuiItem(i18nc("@action:button", "Empty Trash"),
00615 KIcon("user-trash"))
00616 ) == KMessageBox::Continue;
00617 if (del) {
00618 QByteArray packedArgs;
00619 QDataStream stream(&packedArgs, QIODevice::WriteOnly);
00620 stream << int(1);
00621 KIO::Job *job = KIO::special(KUrl("trash:/"), packedArgs);
00622 KNotification::event("Trash: emptied", QString() , QPixmap() , 0, KNotification::DefaultEvent);
00623 job->ui()->setWindow(parentWidget());
00624 connect(job, SIGNAL(result(KJob*)), SLOT(_k_trashUpdated(KJob*)));
00625 }
00626 } else if (edit != 0 && result == edit) {
00627 KBookmark bookmark = placesModel->bookmarkForIndex(index);
00628 KUrl url = bookmark.url();
00629 QString description = bookmark.text();
00630 QString iconName = bookmark.icon();
00631 bool appLocal = !bookmark.metaDataItem("OnlyInApp").isEmpty();
00632
00633 if (KFilePlaceEditDialog::getInformation(true, url, description,
00634 iconName, appLocal, 64, this))
00635 {
00636 QString appName;
00637 if (appLocal) appName = KGlobal::mainComponent().componentName();
00638
00639 placesModel->editPlace(index, description, url, iconName, appName);
00640 }
00641
00642 } else if (remove != 0 && result == remove) {
00643 placesModel->removePlace(index);
00644 } else if (hide != 0 && result == hide) {
00645 placesModel->setPlaceHidden(index, hide->isChecked());
00646 QModelIndex current = placesModel->closestItem(d->currentUrl);
00647
00648 if (index!=current && !d->showAll && hide->isChecked()) {
00649 delegate->addDisappearingItem(index);
00650
00651 if (d->itemDisappearTimeline.state()!=QTimeLine::Running) {
00652 delegate->setDisappearingItemProgress(0.0);
00653 d->itemDisappearTimeline.start();
00654 }
00655 }
00656 } else if (showAll != 0 && result == showAll) {
00657 setShowAll(showAll->isChecked());
00658 } else if (teardown != 0 && result == teardown) {
00659 placesModel->requestTeardown(index);
00660 } else if (eject != 0 && result == eject) {
00661 placesModel->requestEject(index);
00662 } else if (add != 0 && result == add) {
00663 KUrl url = QDir::homePath();
00664 QString description = i18n("Enter a description");
00665 QString iconName = "folder";
00666 bool appLocal = true;
00667 if (KFilePlaceEditDialog::getInformation(true, url, description,
00668 iconName, appLocal, 64, this))
00669 {
00670 QString appName;
00671 if (appLocal) appName = KGlobal::mainComponent().componentName();
00672
00673 placesModel->addPlace(description, url, iconName, appName);
00674 }
00675 }
00676
00677 index = placesModel->closestItem(d->currentUrl);
00678 selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
00679 }
00680
00681 void KFilePlacesView::resizeEvent(QResizeEvent *event)
00682 {
00683 QListView::resizeEvent(event);
00684 d->adaptItemSize();
00685 }
00686
00687 void KFilePlacesView::showEvent(QShowEvent *event)
00688 {
00689 QListView::showEvent(event);
00690 QTimer::singleShot(100, this, SLOT(_k_enableSmoothItemResizing()));
00691 }
00692
00693 void KFilePlacesView::hideEvent(QHideEvent *event)
00694 {
00695 QListView::hideEvent(event);
00696 d->smoothItemResizing = false;
00697 }
00698
00699 void KFilePlacesView::dragEnterEvent(QDragEnterEvent *event)
00700 {
00701 QListView::dragEnterEvent(event);
00702 d->dragging = true;
00703
00704 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00705 delegate->setShowHoverIndication(false);
00706
00707 d->dropRect = QRect();
00708 }
00709
00710 void KFilePlacesView::dragLeaveEvent(QDragLeaveEvent *event)
00711 {
00712 QListView::dragLeaveEvent(event);
00713 d->dragging = false;
00714
00715 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00716 delegate->setShowHoverIndication(true);
00717
00718 setDirtyRegion(d->dropRect);
00719 }
00720
00721 void KFilePlacesView::dragMoveEvent(QDragMoveEvent *event)
00722 {
00723 QListView::dragMoveEvent(event);
00724
00725
00726 const QPoint pos = event->pos();
00727 const QModelIndex index = indexAt(pos);
00728 setDirtyRegion(d->dropRect);
00729 if (index.isValid()) {
00730 const QRect rect = visualRect(index);
00731 const int gap = d->insertIndicatorHeight(rect.height());
00732 if (d->insertAbove(rect, pos)) {
00733
00734 d->dropRect = QRect(rect.left(), rect.top() - gap / 2,
00735 rect.width(), gap);
00736 } else if (d->insertBelow(rect, pos)) {
00737
00738 d->dropRect = QRect(rect.left(), rect.bottom() + 1 - gap / 2,
00739 rect.width(), gap);
00740 } else {
00741
00742 d->dropRect = rect;
00743 }
00744 }
00745
00746 setDirtyRegion(d->dropRect);
00747 }
00748
00749 void KFilePlacesView::dropEvent(QDropEvent *event)
00750 {
00751 const QPoint pos = event->pos();
00752 const QModelIndex index = indexAt(pos);
00753 if (index.isValid()) {
00754 const QRect rect = visualRect(index);
00755 if (!d->insertAbove(rect, pos) && !d->insertBelow(rect, pos)) {
00756 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
00757 Q_ASSERT(placesModel != 0);
00758 emit urlsDropped(placesModel->url(index), event, this);
00759 event->acceptProposedAction();
00760 }
00761 }
00762
00763 QListView::dropEvent(event);
00764 d->dragging = false;
00765
00766 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00767 delegate->setShowHoverIndication(true);
00768 }
00769
00770 void KFilePlacesView::paintEvent(QPaintEvent* event)
00771 {
00772 QListView::paintEvent(event);
00773 if (d->dragging && !d->dropRect.isEmpty()) {
00774
00775 QPainter painter(viewport());
00776
00777 const QModelIndex index = indexAt(d->dropRect.topLeft());
00778 const QRect itemRect = visualRect(index);
00779 const bool drawInsertIndicator = !d->dropOnPlace ||
00780 d->dropRect.height() <= d->insertIndicatorHeight(itemRect.height());
00781
00782 if (drawInsertIndicator) {
00783
00784 QBrush blendedBrush = viewOptions().palette.brush(QPalette::Normal, QPalette::Highlight);
00785 QColor color = blendedBrush.color();
00786
00787 const int y = (d->dropRect.top() + d->dropRect.bottom()) / 2;
00788 const int thickness = d->dropRect.height() / 2;
00789 Q_ASSERT(thickness >= 1);
00790 int alpha = 255;
00791 const int alphaDec = alpha / (thickness + 1);
00792 for (int i = 0; i < thickness; i++) {
00793 color.setAlpha(alpha);
00794 alpha -= alphaDec;
00795 painter.setPen(color);
00796 painter.drawLine(d->dropRect.left(), y - i, d->dropRect.right(), y - i);
00797 painter.drawLine(d->dropRect.left(), y + i, d->dropRect.right(), y + i);
00798 }
00799 } else {
00800
00801 QStyleOptionViewItemV4 opt;
00802 opt.initFrom(this);
00803 opt.rect = itemRect;
00804 opt.state = QStyle::State_Enabled | QStyle::State_MouseOver;
00805 style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, &painter, this);
00806 }
00807 }
00808 }
00809
00810 void KFilePlacesView::setModel(QAbstractItemModel *model)
00811 {
00812 QListView::setModel(model);
00813 d->updateHiddenRows();
00814 connect(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)),
00815 this, SLOT(adaptItemSize()));
00816 connect(selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)),
00817 d->watcher, SLOT(currentIndexChanged(const QModelIndex&)));
00818 }
00819
00820 void KFilePlacesView::rowsInserted(const QModelIndex &parent, int start, int end)
00821 {
00822 QListView::rowsInserted(parent, start, end);
00823 setUrl(d->currentUrl);
00824
00825 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00826 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
00827
00828 for (int i=start; i<=end; ++i) {
00829 QModelIndex index = placesModel->index(i, 0, parent);
00830 if (d->showAll || !placesModel->isHidden(index)) {
00831 delegate->addAppearingItem(index);
00832 } else {
00833 setRowHidden(i, true);
00834 }
00835 }
00836
00837 if (d->itemAppearTimeline.state()!=QTimeLine::Running) {
00838 delegate->setAppearingItemProgress(0.0);
00839 d->itemAppearTimeline.start();
00840 }
00841
00842 d->adaptItemSize();
00843 }
00844
00845 QSize KFilePlacesView::sizeHint() const
00846 {
00847 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
00848 const int height = QListView::sizeHint().height();
00849 QFontMetrics fm = d->q->fontMetrics();
00850 int textWidth = 0;
00851
00852 for (int i=0; i<placesModel->rowCount(); ++i) {
00853 QModelIndex index = placesModel->index(i, 0);
00854 if (!placesModel->isHidden(index))
00855 textWidth = qMax(textWidth,fm.width(index.data(Qt::DisplayRole).toString()));
00856 }
00857
00858 const int iconSize = KIconLoader::global()->currentSize(KIconLoader::Dialog);
00859 return QSize(iconSize + textWidth + 2*KDialog::marginHint(), height);
00860 }
00861
00862 void KFilePlacesView::Private::setCurrentIndex(const QModelIndex &index)
00863 {
00864 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
00865
00866 if (placesModel==0) return;
00867
00868 KUrl url = placesModel->url(index);
00869
00870 if (url.isValid()) {
00871 currentUrl = url;
00872 updateHiddenRows();
00873 emit q->urlChanged(url);
00874 } else {
00875 q->setUrl(currentUrl);
00876 }
00877 }
00878
00879 void KFilePlacesView::Private::adaptItemSize()
00880 {
00881 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(q->itemDelegate());
00882 if (!delegate) return;
00883
00884 if (!autoResizeItems) {
00885 int size = q->iconSize().width();
00886 delegate->setIconSize(size);
00887 q->scheduleDelayedItemsLayout();
00888 return;
00889 }
00890
00891 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
00892
00893 if (placesModel==0) return;
00894
00895 int rowCount = placesModel->rowCount();
00896
00897 if (!showAll) {
00898 rowCount-= placesModel->hiddenCount();
00899
00900 QModelIndex current = placesModel->closestItem(currentUrl);
00901
00902 if (placesModel->isHidden(current)) {
00903 rowCount++;
00904 }
00905 }
00906
00907 if (rowCount==0) return;
00908
00909 const int minSize = 16;
00910 const int maxSize = 64;
00911
00912 int textWidth = 0;
00913 QFontMetrics fm = q->fontMetrics();
00914 for (int i=0; i<placesModel->rowCount(); ++i) {
00915 QModelIndex index = placesModel->index(i, 0);
00916
00917 if (!placesModel->isHidden(index))
00918 textWidth = qMax(textWidth,fm.width(index.data(Qt::DisplayRole).toString()));
00919 }
00920
00921 const int margin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, q) + 1;
00922 const int maxWidth = q->viewport()->width() - textWidth - 4 * margin - 1;
00923 const int maxHeight = ((q->height() - 2 * KDialog::marginHint() * rowCount) / rowCount) - 1;
00924
00925 int size = qMin(maxHeight, maxWidth);
00926
00927 if (size<minSize) {
00928 size = minSize;
00929 } else if (size>maxSize) {
00930 size = maxSize;
00931 } else {
00932
00933 size &= ~0xf;
00934 }
00935
00936 if (size==delegate->iconSize()) return;
00937
00938 if (smoothItemResizing) {
00939 oldSize = delegate->iconSize();
00940 endSize = size;
00941 if (adaptItemsTimeline.state()!=QTimeLine::Running) {
00942 adaptItemsTimeline.start();
00943 }
00944 } else {
00945 delegate->setIconSize(size);
00946 q->scheduleDelayedItemsLayout();
00947 }
00948 }
00949
00950 void KFilePlacesView::Private::updateHiddenRows()
00951 {
00952 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
00953
00954 if (placesModel==0) return;
00955
00956 int rowCount = placesModel->rowCount();
00957 QModelIndex current = placesModel->closestItem(currentUrl);
00958
00959 for (int i=0; i<rowCount; ++i) {
00960 QModelIndex index = placesModel->index(i, 0);
00961 if (index!=current && placesModel->isHidden(index) && !showAll) {
00962 q->setRowHidden(i, true);
00963 } else {
00964 q->setRowHidden(i, false);
00965 }
00966 }
00967
00968 adaptItemSize();
00969 }
00970
00971 bool KFilePlacesView::Private::insertAbove(const QRect &itemRect, const QPoint &pos) const
00972 {
00973 if (dropOnPlace) {
00974 return pos.y() < itemRect.top() + insertIndicatorHeight(itemRect.height()) / 2;
00975 }
00976
00977 return pos.y() < itemRect.top() + (itemRect.height() / 2);
00978 }
00979
00980 bool KFilePlacesView::Private::insertBelow(const QRect &itemRect, const QPoint &pos) const
00981 {
00982 if (dropOnPlace) {
00983 return pos.y() > itemRect.bottom() - insertIndicatorHeight(itemRect.height()) / 2;
00984 }
00985
00986 return pos.y() >= itemRect.top() + (itemRect.height() / 2);
00987 }
00988
00989 int KFilePlacesView::Private::insertIndicatorHeight(int itemHeight) const
00990 {
00991 const int min = 4;
00992 const int max = 12;
00993
00994 int height = itemHeight / 4;
00995 if (height < min) {
00996 height = min;
00997 } else if (height > max) {
00998 height = max;
00999 }
01000 return height;
01001 }
01002
01003 void KFilePlacesView::Private::fadeCapacityBar(const QModelIndex &index, FadeType fadeType)
01004 {
01005 QTimeLine *timeLine = delegate->fadeAnimationForIndex(index);
01006 delete timeLine;
01007 delegate->removeFadeAnimation(index);
01008 timeLine = new QTimeLine(250, q);
01009 connect(timeLine, SIGNAL(valueChanged(qreal)), q, SLOT(_k_capacityBarFadeValueChanged()));
01010 if (fadeType == FadeIn) {
01011 timeLine->setDirection(QTimeLine::Forward);
01012 timeLine->setCurrentTime(0);
01013 } else {
01014 timeLine->setDirection(QTimeLine::Backward);
01015 timeLine->setCurrentTime(250);
01016 }
01017 delegate->addFadeAnimation(index, timeLine);
01018 timeLine->start();
01019 }
01020
01021 void KFilePlacesView::Private::_k_placeClicked(const QModelIndex &index)
01022 {
01023 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
01024
01025 if (placesModel==0) return;
01026
01027 lastClickedIndex = QPersistentModelIndex();
01028
01029 if (placesModel->setupNeeded(index)) {
01030 QObject::connect(placesModel, SIGNAL(setupDone(const QModelIndex &, bool)),
01031 q, SLOT(_k_storageSetupDone(const QModelIndex &, bool)));
01032
01033 lastClickedIndex = index;
01034 placesModel->requestSetup(index);
01035 return;
01036 }
01037
01038 setCurrentIndex(index);
01039 }
01040
01041 void KFilePlacesView::Private::_k_placeEntered(const QModelIndex &index)
01042 {
01043 fadeCapacityBar(index, FadeIn);
01044 pollingRequestCount++;
01045 if (pollingRequestCount == 1) {
01046 pollDevices.start();
01047 }
01048 }
01049
01050 void KFilePlacesView::Private::_k_placeLeft(const QModelIndex &index)
01051 {
01052 fadeCapacityBar(index, FadeOut);
01053 pollingRequestCount--;
01054 if (!pollingRequestCount) {
01055 pollDevices.stop();
01056 }
01057 }
01058
01059 void KFilePlacesView::Private::_k_storageSetupDone(const QModelIndex &index, bool success)
01060 {
01061 if (index!=lastClickedIndex) {
01062 return;
01063 }
01064
01065 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
01066
01067 QObject::disconnect(placesModel, SIGNAL(setupDone(const QModelIndex &, bool)),
01068 q, SLOT(_k_storageSetupDone(const QModelIndex &, bool)));
01069
01070 if (success) {
01071 setCurrentIndex(lastClickedIndex);
01072 } else {
01073 q->setUrl(currentUrl);
01074 }
01075
01076 lastClickedIndex = QPersistentModelIndex();
01077 }
01078
01079 void KFilePlacesView::Private::_k_adaptItemsUpdate(qreal value)
01080 {
01081 int add = (endSize-oldSize)*value;
01082
01083 int size = oldSize+add;
01084
01085 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(q->itemDelegate());
01086 delegate->setIconSize(size);
01087 q->scheduleDelayedItemsLayout();
01088 }
01089
01090 void KFilePlacesView::Private::_k_itemAppearUpdate(qreal value)
01091 {
01092 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(q->itemDelegate());
01093
01094 delegate->setAppearingItemProgress(value);
01095 q->scheduleDelayedItemsLayout();
01096 }
01097
01098 void KFilePlacesView::Private::_k_itemDisappearUpdate(qreal value)
01099 {
01100 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(q->itemDelegate());
01101
01102 delegate->setDisappearingItemProgress(value);
01103
01104 if (value>=1.0) {
01105 updateHiddenRows();
01106 }
01107
01108 q->scheduleDelayedItemsLayout();
01109 }
01110
01111 void KFilePlacesView::Private::_k_enableSmoothItemResizing()
01112 {
01113 smoothItemResizing = true;
01114 }
01115
01116 void KFilePlacesView::Private::_k_trashUpdated(KJob *job)
01117 {
01118 if (job->error()) {
01119 static_cast<KIO::Job*>(job)->ui()->showErrorMessage();
01120 }
01121 org::kde::KDirNotify::emitFilesAdded("trash:/");
01122 }
01123
01124 void KFilePlacesView::Private::_k_capacityBarFadeValueChanged()
01125 {
01126 const QModelIndex index = delegate->indexForFadeAnimation(static_cast<QTimeLine*>(q->sender()));
01127 if (!index.isValid()) {
01128 return;
01129 }
01130 q->update(index);
01131 }
01132
01133 void KFilePlacesView::Private::_k_triggerDevicePolling()
01134 {
01135 const QModelIndex hoveredIndex = watcher->hoveredIndex();
01136 if (hoveredIndex.isValid()) {
01137 const KFilePlacesModel *placesModel = static_cast<const KFilePlacesModel*>(hoveredIndex.model());
01138 if (placesModel->isDevice(hoveredIndex)) {
01139 q->update(hoveredIndex);
01140 }
01141 }
01142 const QModelIndex focusedIndex = watcher->focusedIndex();
01143 if (focusedIndex.isValid() && focusedIndex != hoveredIndex) {
01144 const KFilePlacesModel *placesModel = static_cast<const KFilePlacesModel*>(focusedIndex.model());
01145 if (placesModel->isDevice(focusedIndex)) {
01146 q->update(focusedIndex);
01147 }
01148 }
01149 }
01150
01151 void KFilePlacesView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
01152 {
01153 QListView::dataChanged(topLeft, bottomRight);
01154 }
01155
01156 #include "kfileplacesview.moc"
01157 #include "kfileplacesview_p.moc"