• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KFile

kfileplacesview.cpp

Go to the documentation of this file.
00001 /*  This file is part of the KDE project
00002     Copyright (C) 2007 Kevin Ottens <ervin@kde.org>
00003     Copyright (C) 2008 Rafael Fernández López <ereslibre@kde.org>
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License version 2 as published by the Free Software Foundation.
00008 
00009     This library is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     Library General Public License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to
00016     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017     Boston, MA 02110-1301, USA.
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     // update the drop indicator
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             // indicate that the item will be inserted above the current place
00734             d->dropRect = QRect(rect.left(), rect.top() - gap / 2,
00735                                 rect.width(), gap);
00736         } else if (d->insertBelow(rect, pos)) {
00737             // indicate that the item will be inserted below the current place
00738             d->dropRect = QRect(rect.left(), rect.bottom() + 1 -  gap / 2,
00739                                 rect.width(), gap);
00740         } else {
00741             // indicate that the item be dropped above the current place
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         // draw drop indicator
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             // draw indicator for inserting items
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             // draw indicator for copying/moving/linking to items
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(); // Assume width == height
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; // We've nothing to display anyway
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         // Make it a multiple of 16
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"

KFile

Skip menu "KFile"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal