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/QPainter>
00027 #include <QtGui/QAbstractItemDelegate>
00028 #include <QtGui/QKeyEvent>
00029 #include <QtGui/QApplication>
00030
00031 #include <kdebug.h>
00032
00033 #include <kmenu.h>
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 qreal 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(), option.fontMetrics.height() / 2 + 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 && contentsOpacity(index) > 0) {
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 && contentsOpacity(index) > 0) {
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 qreal 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 palette.setColor(viewport()->foregroundRole(), palette.color(QPalette::WindowText));
00392 viewport()->setPalette(palette);
00393
00394 connect(this, SIGNAL(clicked(const QModelIndex&)),
00395 this, SLOT(_k_placeClicked(const QModelIndex&)));
00396
00397
00398
00399
00400 connect(&d->adaptItemsTimeline, SIGNAL(valueChanged(qreal)),
00401 this, SLOT(_k_adaptItemsUpdate(qreal)));
00402 d->adaptItemsTimeline.setDuration(500);
00403 d->adaptItemsTimeline.setUpdateInterval(5);
00404 d->adaptItemsTimeline.setCurveShape(QTimeLine::EaseInOutCurve);
00405
00406 connect(&d->itemAppearTimeline, SIGNAL(valueChanged(qreal)),
00407 this, SLOT(_k_itemAppearUpdate(qreal)));
00408 d->itemAppearTimeline.setDuration(500);
00409 d->itemAppearTimeline.setUpdateInterval(5);
00410 d->itemAppearTimeline.setCurveShape(QTimeLine::EaseInOutCurve);
00411
00412 connect(&d->itemDisappearTimeline, SIGNAL(valueChanged(qreal)),
00413 this, SLOT(_k_itemDisappearUpdate(qreal)));
00414 d->itemDisappearTimeline.setDuration(500);
00415 d->itemDisappearTimeline.setUpdateInterval(5);
00416 d->itemDisappearTimeline.setCurveShape(QTimeLine::EaseInOutCurve);
00417
00418 viewport()->installEventFilter(d->watcher);
00419 connect(d->watcher, SIGNAL(entryEntered(const QModelIndex&)),
00420 this, SLOT(_k_placeEntered(const QModelIndex&)));
00421 connect(d->watcher, SIGNAL(entryLeft(const QModelIndex&)),
00422 this, SLOT(_k_placeLeft(const QModelIndex&)));
00423
00424 d->pollDevices.setInterval(5000);
00425 connect(&d->pollDevices, SIGNAL(timeout()), this, SLOT(_k_triggerDevicePolling()));
00426 }
00427
00428 KFilePlacesView::~KFilePlacesView()
00429 {
00430 delete d;
00431 }
00432
00433 void KFilePlacesView::setDropOnPlaceEnabled(bool enabled)
00434 {
00435 d->dropOnPlace = enabled;
00436 }
00437
00438 bool KFilePlacesView::isDropOnPlaceEnabled() const
00439 {
00440 return d->dropOnPlace;
00441 }
00442
00443 void KFilePlacesView::setAutoResizeItemsEnabled(bool enabled)
00444 {
00445 d->autoResizeItems = enabled;
00446 }
00447
00448 bool KFilePlacesView::isAutoResizeItemsEnabled() const
00449 {
00450 return d->autoResizeItems;
00451 }
00452
00453 void KFilePlacesView::setUrl(const KUrl &url)
00454 {
00455 KUrl oldUrl = d->currentUrl;
00456 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
00457
00458 if (placesModel==0) return;
00459
00460 QModelIndex index = placesModel->closestItem(url);
00461 QModelIndex current = selectionModel()->currentIndex();
00462
00463 if (index.isValid()) {
00464 if (current!=index && placesModel->isHidden(current) && !d->showAll) {
00465 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00466 delegate->addDisappearingItem(current);
00467
00468 if (d->itemDisappearTimeline.state()!=QTimeLine::Running) {
00469 delegate->setDisappearingItemProgress(0.0);
00470 d->itemDisappearTimeline.start();
00471 }
00472 }
00473
00474 if (current!=index && placesModel->isHidden(index) && !d->showAll) {
00475 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00476 delegate->addAppearingItem(index);
00477
00478 if (d->itemAppearTimeline.state()!=QTimeLine::Running) {
00479 delegate->setAppearingItemProgress(0.0);
00480 d->itemAppearTimeline.start();
00481 }
00482
00483 setRowHidden(index.row(), false);
00484 }
00485
00486 d->currentUrl = url;
00487 selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
00488 } else {
00489 d->currentUrl = KUrl();
00490 selectionModel()->clear();
00491 }
00492
00493 if (!current.isValid()) {
00494 d->updateHiddenRows();
00495 }
00496 }
00497
00498 void KFilePlacesView::setShowAll(bool showAll)
00499 {
00500 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
00501
00502 if (placesModel==0) return;
00503
00504 d->showAll = showAll;
00505
00506 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00507
00508 int rowCount = placesModel->rowCount();
00509 QModelIndex current = placesModel->closestItem(d->currentUrl);
00510
00511 if (showAll) {
00512 d->updateHiddenRows();
00513
00514 for (int i=0; i<rowCount; ++i) {
00515 QModelIndex index = placesModel->index(i, 0);
00516 if (index!=current && placesModel->isHidden(index)) {
00517 delegate->addAppearingItem(index);
00518 }
00519 }
00520
00521 if (d->itemAppearTimeline.state()!=QTimeLine::Running) {
00522 delegate->setAppearingItemProgress(0.0);
00523 d->itemAppearTimeline.start();
00524 }
00525 } else {
00526 for (int i=0; i<rowCount; ++i) {
00527 QModelIndex index = placesModel->index(i, 0);
00528 if (index!=current && placesModel->isHidden(index)) {
00529 delegate->addDisappearingItem(index);
00530 }
00531 }
00532
00533 if (d->itemDisappearTimeline.state()!=QTimeLine::Running) {
00534 delegate->setDisappearingItemProgress(0.0);
00535 d->itemDisappearTimeline.start();
00536 }
00537 }
00538 }
00539
00540 void KFilePlacesView::keyPressEvent(QKeyEvent *event)
00541 {
00542 QListView::keyPressEvent(event);
00543 if ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Enter)) {
00544 d->_k_placeClicked(currentIndex());
00545 }
00546 }
00547
00548 void KFilePlacesView::contextMenuEvent(QContextMenuEvent *event)
00549 {
00550 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
00551 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00552
00553 if (placesModel==0) return;
00554
00555 QModelIndex index = indexAt(event->pos());
00556 QString label = placesModel->text(index).replace('&',"&&");
00557
00558 KMenu menu;
00559
00560 QAction *edit = 0;
00561 QAction *hide = 0;
00562 QAction *emptyTrash = 0;
00563 QAction *eject = 0;
00564 QAction *teardown = 0;
00565 QAction *add = 0;
00566 QAction *mainSeparator = 0;
00567
00568 if (index.isValid()) {
00569 if (!placesModel->isDevice(index)) {
00570 if (placesModel->url(index) == KUrl("trash:/")) {
00571 emptyTrash = menu.addAction(KIcon("trash-empty"), i18nc("@action:inmenu", "Empty Trash"));
00572 KConfig trashConfig("trashrc", KConfig::SimpleConfig);
00573 emptyTrash->setEnabled(!trashConfig.group("Status").readEntry("Empty", true));
00574 menu.addSeparator();
00575 }
00576 add = menu.addAction(KIcon("document-new"), i18n("Add Entry..."));
00577 mainSeparator = menu.addSeparator();
00578 edit = menu.addAction(KIcon("document-properties"), i18n("&Edit '%1'...", label));
00579 } else {
00580 eject = placesModel->ejectActionForIndex(index);
00581 if (eject!=0) {
00582 eject->setParent(&menu);
00583 menu.addAction(eject);
00584 }
00585
00586 teardown = placesModel->teardownActionForIndex(index);
00587 if (teardown!=0) {
00588 teardown->setParent(&menu);
00589 menu.addAction(teardown);
00590 }
00591
00592 if (teardown!=0 || eject!=0) {
00593 mainSeparator = menu.addSeparator();
00594 }
00595 }
00596 if (add == 0) {
00597 add = menu.addAction(KIcon("document-new"), i18n("Add Entry..."));
00598 }
00599
00600 hide = menu.addAction(i18n("&Hide '%1'", label));
00601 hide->setCheckable(true);
00602 hide->setChecked(placesModel->isHidden(index));
00603 } else {
00604 add = menu.addAction(KIcon("document-new"), i18n("Add Entry..."));
00605 }
00606
00607 QAction *showAll = 0;
00608 if (placesModel->hiddenCount()>0) {
00609 showAll = new QAction(i18n("&Show All Entries"), &menu);
00610 showAll->setCheckable(true);
00611 showAll->setChecked(d->showAll);
00612 if (mainSeparator == 0) {
00613 mainSeparator = menu.addSeparator();
00614 }
00615 menu.insertAction(mainSeparator, showAll);
00616 }
00617
00618 QAction* remove = 0;
00619 if (index.isValid() && !placesModel->isDevice(index)) {
00620 remove = menu.addAction( KIcon("edit-delete"), i18n("&Remove '%1'", label));
00621 }
00622
00623 if (menu.isEmpty()) {
00624 return;
00625 }
00626
00627 QAction *result = menu.exec(event->globalPos());
00628
00629 if (emptyTrash != 0 && result == emptyTrash) {
00630 const QString text = i18nc("@info", "Do you really want to empty the Trash? All items will be deleted.");
00631 const bool del = KMessageBox::warningContinueCancel(window(),
00632 text,
00633 QString(),
00634 KGuiItem(i18nc("@action:button", "Empty Trash"),
00635 KIcon("user-trash"))
00636 ) == KMessageBox::Continue;
00637 if (del) {
00638 QByteArray packedArgs;
00639 QDataStream stream(&packedArgs, QIODevice::WriteOnly);
00640 stream << int(1);
00641 KIO::Job *job = KIO::special(KUrl("trash:/"), packedArgs);
00642 KNotification::event("Trash: emptied", QString() , QPixmap() , 0, KNotification::DefaultEvent);
00643 job->ui()->setWindow(parentWidget());
00644 connect(job, SIGNAL(result(KJob*)), SLOT(_k_trashUpdated(KJob*)));
00645 }
00646 } else if (edit != 0 && result == edit) {
00647 KBookmark bookmark = placesModel->bookmarkForIndex(index);
00648 KUrl url = bookmark.url();
00649 QString description = bookmark.text();
00650 QString iconName = bookmark.icon();
00651 bool appLocal = !bookmark.metaDataItem("OnlyInApp").isEmpty();
00652
00653 if (KFilePlaceEditDialog::getInformation(true, url, description,
00654 iconName, appLocal, 64, this))
00655 {
00656 QString appName;
00657 if (appLocal) appName = KGlobal::mainComponent().componentName();
00658
00659 placesModel->editPlace(index, description, url, iconName, appName);
00660 }
00661
00662 } else if (remove != 0 && result == remove) {
00663 placesModel->removePlace(index);
00664 } else if (hide != 0 && result == hide) {
00665 placesModel->setPlaceHidden(index, hide->isChecked());
00666 QModelIndex current = placesModel->closestItem(d->currentUrl);
00667
00668 if (index!=current && !d->showAll && hide->isChecked()) {
00669 delegate->addDisappearingItem(index);
00670
00671 if (d->itemDisappearTimeline.state()!=QTimeLine::Running) {
00672 delegate->setDisappearingItemProgress(0.0);
00673 d->itemDisappearTimeline.start();
00674 }
00675 }
00676 } else if (showAll != 0 && result == showAll) {
00677 setShowAll(showAll->isChecked());
00678 } else if (teardown != 0 && result == teardown) {
00679 placesModel->requestTeardown(index);
00680 } else if (eject != 0 && result == eject) {
00681 placesModel->requestEject(index);
00682 } else if (add != 0 && result == add) {
00683 KUrl url = QDir::homePath();
00684 QString description = i18n("Enter a description");
00685 QString iconName = "folder";
00686 bool appLocal = true;
00687 if (KFilePlaceEditDialog::getInformation(true, url, description,
00688 iconName, appLocal, 64, this))
00689 {
00690 QString appName;
00691 if (appLocal) appName = KGlobal::mainComponent().componentName();
00692
00693 placesModel->addPlace(description, url, iconName, appName);
00694 }
00695 }
00696
00697 index = placesModel->closestItem(d->currentUrl);
00698 selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
00699 }
00700
00701 void KFilePlacesView::resizeEvent(QResizeEvent *event)
00702 {
00703 QListView::resizeEvent(event);
00704 d->adaptItemSize();
00705 }
00706
00707 void KFilePlacesView::showEvent(QShowEvent *event)
00708 {
00709 QListView::showEvent(event);
00710 QTimer::singleShot(100, this, SLOT(_k_enableSmoothItemResizing()));
00711 }
00712
00713 void KFilePlacesView::hideEvent(QHideEvent *event)
00714 {
00715 QListView::hideEvent(event);
00716 d->smoothItemResizing = false;
00717 }
00718
00719 void KFilePlacesView::dragEnterEvent(QDragEnterEvent *event)
00720 {
00721 QListView::dragEnterEvent(event);
00722 d->dragging = true;
00723
00724 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00725 delegate->setShowHoverIndication(false);
00726
00727 d->dropRect = QRect();
00728 }
00729
00730 void KFilePlacesView::dragLeaveEvent(QDragLeaveEvent *event)
00731 {
00732 QListView::dragLeaveEvent(event);
00733 d->dragging = false;
00734
00735 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00736 delegate->setShowHoverIndication(true);
00737
00738 setDirtyRegion(d->dropRect);
00739 }
00740
00741 void KFilePlacesView::dragMoveEvent(QDragMoveEvent *event)
00742 {
00743 QListView::dragMoveEvent(event);
00744
00745
00746 const QPoint pos = event->pos();
00747 const QModelIndex index = indexAt(pos);
00748 setDirtyRegion(d->dropRect);
00749 if (index.isValid()) {
00750 const QRect rect = visualRect(index);
00751 const int gap = d->insertIndicatorHeight(rect.height());
00752 if (d->insertAbove(rect, pos)) {
00753
00754 d->dropRect = QRect(rect.left(), rect.top() - gap / 2,
00755 rect.width(), gap);
00756 } else if (d->insertBelow(rect, pos)) {
00757
00758 d->dropRect = QRect(rect.left(), rect.bottom() + 1 - gap / 2,
00759 rect.width(), gap);
00760 } else {
00761
00762 d->dropRect = rect;
00763 }
00764 }
00765
00766 setDirtyRegion(d->dropRect);
00767 }
00768
00769 void KFilePlacesView::dropEvent(QDropEvent *event)
00770 {
00771 const QPoint pos = event->pos();
00772 const QModelIndex index = indexAt(pos);
00773 if (index.isValid()) {
00774 const QRect rect = visualRect(index);
00775 if (!d->insertAbove(rect, pos) && !d->insertBelow(rect, pos)) {
00776 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
00777 Q_ASSERT(placesModel != 0);
00778 emit urlsDropped(placesModel->url(index), event, this);
00779 event->acceptProposedAction();
00780 }
00781 }
00782
00783 QListView::dropEvent(event);
00784 d->dragging = false;
00785
00786 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00787 delegate->setShowHoverIndication(true);
00788 }
00789
00790 void KFilePlacesView::paintEvent(QPaintEvent* event)
00791 {
00792 QListView::paintEvent(event);
00793 if (d->dragging && !d->dropRect.isEmpty()) {
00794
00795 QPainter painter(viewport());
00796
00797 const QModelIndex index = indexAt(d->dropRect.topLeft());
00798 const QRect itemRect = visualRect(index);
00799 const bool drawInsertIndicator = !d->dropOnPlace ||
00800 d->dropRect.height() <= d->insertIndicatorHeight(itemRect.height());
00801
00802 if (drawInsertIndicator) {
00803
00804 QBrush blendedBrush = viewOptions().palette.brush(QPalette::Normal, QPalette::Highlight);
00805 QColor color = blendedBrush.color();
00806
00807 const int y = (d->dropRect.top() + d->dropRect.bottom()) / 2;
00808 const int thickness = d->dropRect.height() / 2;
00809 Q_ASSERT(thickness >= 1);
00810 int alpha = 255;
00811 const int alphaDec = alpha / (thickness + 1);
00812 for (int i = 0; i < thickness; i++) {
00813 color.setAlpha(alpha);
00814 alpha -= alphaDec;
00815 painter.setPen(color);
00816 painter.drawLine(d->dropRect.left(), y - i, d->dropRect.right(), y - i);
00817 painter.drawLine(d->dropRect.left(), y + i, d->dropRect.right(), y + i);
00818 }
00819 } else {
00820
00821 QStyleOptionViewItemV4 opt;
00822 opt.initFrom(this);
00823 opt.rect = itemRect;
00824 opt.state = QStyle::State_Enabled | QStyle::State_MouseOver;
00825 style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, &painter, this);
00826 }
00827 }
00828 }
00829
00830 void KFilePlacesView::setModel(QAbstractItemModel *model)
00831 {
00832 QListView::setModel(model);
00833 d->updateHiddenRows();
00834 connect(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)),
00835 this, SLOT(adaptItemSize()));
00836 connect(selectionModel(), SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)),
00837 d->watcher, SLOT(currentIndexChanged(const QModelIndex&)));
00838 }
00839
00840 void KFilePlacesView::rowsInserted(const QModelIndex &parent, int start, int end)
00841 {
00842 QListView::rowsInserted(parent, start, end);
00843 setUrl(d->currentUrl);
00844
00845 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
00846 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
00847
00848 for (int i=start; i<=end; ++i) {
00849 QModelIndex index = placesModel->index(i, 0, parent);
00850 if (d->showAll || !placesModel->isHidden(index)) {
00851 delegate->addAppearingItem(index);
00852 } else {
00853 setRowHidden(i, true);
00854 }
00855 }
00856
00857 if (d->itemAppearTimeline.state()!=QTimeLine::Running) {
00858 delegate->setAppearingItemProgress(0.0);
00859 d->itemAppearTimeline.start();
00860 }
00861
00862 d->adaptItemSize();
00863 }
00864
00865 QSize KFilePlacesView::sizeHint() const
00866 {
00867 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
00868 if (!placesModel) {
00869 return QListView::sizeHint();
00870 }
00871 const int height = QListView::sizeHint().height();
00872 QFontMetrics fm = d->q->fontMetrics();
00873 int textWidth = 0;
00874
00875 for (int i=0; i<placesModel->rowCount(); ++i) {
00876 QModelIndex index = placesModel->index(i, 0);
00877 if (!placesModel->isHidden(index))
00878 textWidth = qMax(textWidth,fm.width(index.data(Qt::DisplayRole).toString()));
00879 }
00880
00881 const int iconSize = KIconLoader::global()->currentSize(KIconLoader::Dialog);
00882 return QSize(iconSize + textWidth + fm.height() / 2, height);
00883 }
00884
00885 void KFilePlacesView::Private::setCurrentIndex(const QModelIndex &index)
00886 {
00887 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
00888
00889 if (placesModel==0) return;
00890
00891 KUrl url = placesModel->url(index);
00892
00893 if (url.isValid()) {
00894 currentUrl = url;
00895 updateHiddenRows();
00896 emit q->urlChanged(url);
00897 if (showAll) {
00898 q->setShowAll(false);
00899 }
00900 } else {
00901 q->setUrl(currentUrl);
00902 }
00903 }
00904
00905 void KFilePlacesView::Private::adaptItemSize()
00906 {
00907 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(q->itemDelegate());
00908 if (!delegate) return;
00909
00910 if (!autoResizeItems) {
00911 int size = q->iconSize().width();
00912 delegate->setIconSize(size);
00913 q->scheduleDelayedItemsLayout();
00914 return;
00915 }
00916
00917 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
00918
00919 if (placesModel==0) return;
00920
00921 int rowCount = placesModel->rowCount();
00922
00923 if (!showAll) {
00924 rowCount-= placesModel->hiddenCount();
00925
00926 QModelIndex current = placesModel->closestItem(currentUrl);
00927
00928 if (placesModel->isHidden(current)) {
00929 rowCount++;
00930 }
00931 }
00932
00933 if (rowCount==0) return;
00934
00935 const int minSize = 16;
00936 const int maxSize = 64;
00937
00938 int textWidth = 0;
00939 QFontMetrics fm = q->fontMetrics();
00940 for (int i=0; i<placesModel->rowCount(); ++i) {
00941 QModelIndex index = placesModel->index(i, 0);
00942
00943 if (!placesModel->isHidden(index))
00944 textWidth = qMax(textWidth,fm.width(index.data(Qt::DisplayRole).toString()));
00945 }
00946
00947 const int margin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, q) + 1;
00948 const int maxWidth = q->viewport()->width() - textWidth - 4 * margin - 1;
00949 const int maxHeight = ((q->height() - (fm.height() / 2) * rowCount) / rowCount) - 1;
00950
00951 int size = qMin(maxHeight, maxWidth);
00952
00953 if (size<minSize) {
00954 size = minSize;
00955 } else if (size>maxSize) {
00956 size = maxSize;
00957 } else {
00958
00959 size &= ~0xf;
00960 }
00961
00962 if (size==delegate->iconSize()) return;
00963
00964 if (smoothItemResizing) {
00965 oldSize = delegate->iconSize();
00966 endSize = size;
00967 if (adaptItemsTimeline.state()!=QTimeLine::Running) {
00968 adaptItemsTimeline.start();
00969 }
00970 } else {
00971 delegate->setIconSize(size);
00972 q->scheduleDelayedItemsLayout();
00973 }
00974 }
00975
00976 void KFilePlacesView::Private::updateHiddenRows()
00977 {
00978 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
00979
00980 if (placesModel==0) return;
00981
00982 int rowCount = placesModel->rowCount();
00983 QModelIndex current = placesModel->closestItem(currentUrl);
00984
00985 for (int i=0; i<rowCount; ++i) {
00986 QModelIndex index = placesModel->index(i, 0);
00987 if (index!=current && placesModel->isHidden(index) && !showAll) {
00988 q->setRowHidden(i, true);
00989 } else {
00990 q->setRowHidden(i, false);
00991 }
00992 }
00993
00994 adaptItemSize();
00995 }
00996
00997 bool KFilePlacesView::Private::insertAbove(const QRect &itemRect, const QPoint &pos) const
00998 {
00999 if (dropOnPlace) {
01000 return pos.y() < itemRect.top() + insertIndicatorHeight(itemRect.height()) / 2;
01001 }
01002
01003 return pos.y() < itemRect.top() + (itemRect.height() / 2);
01004 }
01005
01006 bool KFilePlacesView::Private::insertBelow(const QRect &itemRect, const QPoint &pos) const
01007 {
01008 if (dropOnPlace) {
01009 return pos.y() > itemRect.bottom() - insertIndicatorHeight(itemRect.height()) / 2;
01010 }
01011
01012 return pos.y() >= itemRect.top() + (itemRect.height() / 2);
01013 }
01014
01015 int KFilePlacesView::Private::insertIndicatorHeight(int itemHeight) const
01016 {
01017 const int min = 4;
01018 const int max = 12;
01019
01020 int height = itemHeight / 4;
01021 if (height < min) {
01022 height = min;
01023 } else if (height > max) {
01024 height = max;
01025 }
01026 return height;
01027 }
01028
01029 void KFilePlacesView::Private::fadeCapacityBar(const QModelIndex &index, FadeType fadeType)
01030 {
01031 QTimeLine *timeLine = delegate->fadeAnimationForIndex(index);
01032 delete timeLine;
01033 delegate->removeFadeAnimation(index);
01034 timeLine = new QTimeLine(250, q);
01035 connect(timeLine, SIGNAL(valueChanged(qreal)), q, SLOT(_k_capacityBarFadeValueChanged()));
01036 if (fadeType == FadeIn) {
01037 timeLine->setDirection(QTimeLine::Forward);
01038 timeLine->setCurrentTime(0);
01039 } else {
01040 timeLine->setDirection(QTimeLine::Backward);
01041 timeLine->setCurrentTime(250);
01042 }
01043 delegate->addFadeAnimation(index, timeLine);
01044 timeLine->start();
01045 }
01046
01047 void KFilePlacesView::Private::_k_placeClicked(const QModelIndex &index)
01048 {
01049 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
01050
01051 if (placesModel==0) return;
01052
01053 lastClickedIndex = QPersistentModelIndex();
01054
01055 if (placesModel->setupNeeded(index)) {
01056 QObject::connect(placesModel, SIGNAL(setupDone(const QModelIndex &, bool)),
01057 q, SLOT(_k_storageSetupDone(const QModelIndex &, bool)));
01058
01059 lastClickedIndex = index;
01060 placesModel->requestSetup(index);
01061 return;
01062 }
01063
01064 setCurrentIndex(index);
01065 }
01066
01067 void KFilePlacesView::Private::_k_placeEntered(const QModelIndex &index)
01068 {
01069 fadeCapacityBar(index, FadeIn);
01070 pollingRequestCount++;
01071 if (pollingRequestCount == 1) {
01072 pollDevices.start();
01073 }
01074 }
01075
01076 void KFilePlacesView::Private::_k_placeLeft(const QModelIndex &index)
01077 {
01078 fadeCapacityBar(index, FadeOut);
01079 pollingRequestCount--;
01080 if (!pollingRequestCount) {
01081 pollDevices.stop();
01082 }
01083 }
01084
01085 void KFilePlacesView::Private::_k_storageSetupDone(const QModelIndex &index, bool success)
01086 {
01087 if (index!=lastClickedIndex) {
01088 return;
01089 }
01090
01091 KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
01092
01093 QObject::disconnect(placesModel, SIGNAL(setupDone(const QModelIndex &, bool)),
01094 q, SLOT(_k_storageSetupDone(const QModelIndex &, bool)));
01095
01096 if (success) {
01097 setCurrentIndex(lastClickedIndex);
01098 } else {
01099 q->setUrl(currentUrl);
01100 }
01101
01102 lastClickedIndex = QPersistentModelIndex();
01103 }
01104
01105 void KFilePlacesView::Private::_k_adaptItemsUpdate(qreal value)
01106 {
01107 int add = (endSize-oldSize)*value;
01108
01109 int size = oldSize+add;
01110
01111 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(q->itemDelegate());
01112 delegate->setIconSize(size);
01113 q->scheduleDelayedItemsLayout();
01114 }
01115
01116 void KFilePlacesView::Private::_k_itemAppearUpdate(qreal value)
01117 {
01118 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(q->itemDelegate());
01119
01120 delegate->setAppearingItemProgress(value);
01121 q->scheduleDelayedItemsLayout();
01122 }
01123
01124 void KFilePlacesView::Private::_k_itemDisappearUpdate(qreal value)
01125 {
01126 KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(q->itemDelegate());
01127
01128 delegate->setDisappearingItemProgress(value);
01129
01130 if (value>=1.0) {
01131 updateHiddenRows();
01132 }
01133
01134 q->scheduleDelayedItemsLayout();
01135 }
01136
01137 void KFilePlacesView::Private::_k_enableSmoothItemResizing()
01138 {
01139 smoothItemResizing = true;
01140 }
01141
01142 void KFilePlacesView::Private::_k_trashUpdated(KJob *job)
01143 {
01144 if (job->error()) {
01145 static_cast<KIO::Job*>(job)->ui()->showErrorMessage();
01146 }
01147 org::kde::KDirNotify::emitFilesAdded("trash:/");
01148 }
01149
01150 void KFilePlacesView::Private::_k_capacityBarFadeValueChanged()
01151 {
01152 const QModelIndex index = delegate->indexForFadeAnimation(static_cast<QTimeLine*>(q->sender()));
01153 if (!index.isValid()) {
01154 return;
01155 }
01156 q->update(index);
01157 }
01158
01159 void KFilePlacesView::Private::_k_triggerDevicePolling()
01160 {
01161 const QModelIndex hoveredIndex = watcher->hoveredIndex();
01162 if (hoveredIndex.isValid()) {
01163 const KFilePlacesModel *placesModel = static_cast<const KFilePlacesModel*>(hoveredIndex.model());
01164 if (placesModel->isDevice(hoveredIndex)) {
01165 q->update(hoveredIndex);
01166 }
01167 }
01168 const QModelIndex focusedIndex = watcher->focusedIndex();
01169 if (focusedIndex.isValid() && focusedIndex != hoveredIndex) {
01170 const KFilePlacesModel *placesModel = static_cast<const KFilePlacesModel*>(focusedIndex.model());
01171 if (placesModel->isDevice(focusedIndex)) {
01172 q->update(focusedIndex);
01173 }
01174 }
01175 }
01176
01177 void KFilePlacesView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
01178 {
01179 QListView::dataChanged(topLeft, bottomRight);
01180 d->adaptItemSize();
01181 }
01182
01183 #include "kfileplacesview.moc"
01184 #include "kfileplacesview_p.moc"