• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.5 API Reference
  • KDE Home
  • Contact Us
 

KFile

  • kfile
kfileplacesview.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE project
2  Copyright (C) 2007 Kevin Ottens <ervin@kde.org>
3  Copyright (C) 2008 Rafael Fernández López <ereslibre@kde.org>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License version 2 as published by the Free Software Foundation.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Library General Public License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  Boston, MA 02110-1301, USA.
18 
19 */
20 
21 #include "kfileplacesview.h"
22 #include "kfileplacesview_p.h"
23 
24 #include <QtCore/QTimeLine>
25 #include <QtCore/QTimer>
26 #include <QtGui/QPainter>
27 #include <QtGui/QAbstractItemDelegate>
28 #include <QtGui/QKeyEvent>
29 #include <QtGui/QApplication>
30 #include <QtGui/QScrollBar>
31 
32 #include <kdebug.h>
33 
34 #include <kmenu.h>
35 #include <kcomponentdata.h>
36 #include <kdirnotify.h>
37 #include <kglobalsettings.h>
38 #include <kiconloader.h>
39 #include <klocale.h>
40 #include <kmessagebox.h>
41 #include <knotification.h>
42 #include <kio/job.h>
43 #include <kio/jobuidelegate.h>
44 #include <kjob.h>
45 #include <kcapacitybar.h>
46 #include <kdiskfreespaceinfo.h>
47 #include <solid/storageaccess.h>
48 #include <solid/storagedrive.h>
49 #include <solid/storagevolume.h>
50 #include <solid/opticaldrive.h>
51 #include <solid/opticaldisc.h>
52 
53 #include "kfileplaceeditdialog.h"
54 #include "kfileplacesmodel.h"
55 
56 #define LATERAL_MARGIN 4
57 #define CAPACITYBAR_HEIGHT 6
58 
59 class KFilePlacesViewDelegate : public QAbstractItemDelegate
60 {
61 public:
62  KFilePlacesViewDelegate(KFilePlacesView *parent);
63  virtual ~KFilePlacesViewDelegate();
64  virtual QSize sizeHint(const QStyleOptionViewItem &option,
65  const QModelIndex &index) const;
66  virtual void paint(QPainter *painter,
67  const QStyleOptionViewItem &option,
68  const QModelIndex &index) const;
69 
70  int iconSize() const;
71  void setIconSize(int newSize);
72 
73  void addAppearingItem(const QModelIndex &index);
74  void setAppearingItemProgress(qreal value);
75  void addDisappearingItem(const QModelIndex &index);
76  void setDisappearingItemProgress(qreal value);
77 
78  void setShowHoverIndication(bool show);
79 
80  void addFadeAnimation(const QModelIndex &index, QTimeLine *timeLine);
81  void removeFadeAnimation(const QModelIndex &index);
82  QModelIndex indexForFadeAnimation(QTimeLine *timeLine) const;
83  QTimeLine *fadeAnimationForIndex(const QModelIndex &index) const;
84 
85  qreal contentsOpacity(const QModelIndex &index) const;
86 
87 private:
88  KFilePlacesView *m_view;
89  int m_iconSize;
90 
91  QList<QPersistentModelIndex> m_appearingItems;
92  int m_appearingIconSize;
93  qreal m_appearingOpacity;
94 
95  QList<QPersistentModelIndex> m_disappearingItems;
96  int m_disappearingIconSize;
97  qreal m_disappearingOpacity;
98 
99  bool m_showHoverIndication;
100 
101  QMap<QPersistentModelIndex, QTimeLine*> m_timeLineMap;
102  QMap<QTimeLine*, QPersistentModelIndex> m_timeLineInverseMap;
103 };
104 
105 KFilePlacesViewDelegate::KFilePlacesViewDelegate(KFilePlacesView *parent) :
106  QAbstractItemDelegate(parent),
107  m_view(parent),
108  m_iconSize(48),
109  m_appearingIconSize(0),
110  m_appearingOpacity(0.0),
111  m_disappearingIconSize(0),
112  m_disappearingOpacity(0.0),
113  m_showHoverIndication(true)
114 {
115 }
116 
117 KFilePlacesViewDelegate::~KFilePlacesViewDelegate()
118 {
119 }
120 
121 QSize KFilePlacesViewDelegate::sizeHint(const QStyleOptionViewItem &option,
122  const QModelIndex &index) const
123 {
124  int iconSize = m_iconSize;
125  if (m_appearingItems.contains(index)) {
126  iconSize = m_appearingIconSize;
127  } else if (m_disappearingItems.contains(index)) {
128  iconSize = m_disappearingIconSize;
129  }
130 
131  const KFilePlacesModel *filePlacesModel = static_cast<const KFilePlacesModel*>(index.model());
132  Solid::Device device = filePlacesModel->deviceForIndex(index);
133 
134  return QSize(option.rect.width(), option.fontMetrics.height() / 2 + qMax(iconSize, option.fontMetrics.height()));
135 }
136 
137 void KFilePlacesViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
138 {
139  painter->save();
140 
141  if (m_appearingItems.contains(index)) {
142  painter->setOpacity(m_appearingOpacity);
143  } else if (m_disappearingItems.contains(index)) {
144  painter->setOpacity(m_disappearingOpacity);
145  }
146 
147  QStyleOptionViewItemV4 opt = option;
148  if (!m_showHoverIndication) {
149  opt.state &= ~QStyle::State_MouseOver;
150  }
151  QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter);
152  const KFilePlacesModel *placesModel = static_cast<const KFilePlacesModel*>(index.model());
153 
154  bool isLTR = option.direction == Qt::LeftToRight;
155 
156  QIcon icon = index.model()->data(index, Qt::DecorationRole).value<QIcon>();
157  QPixmap pm = icon.pixmap(m_iconSize, m_iconSize);
158  QPoint point(isLTR ? option.rect.left() + LATERAL_MARGIN
159  : option.rect.right() - LATERAL_MARGIN - m_iconSize, option.rect.top() + (option.rect.height() - m_iconSize) / 2);
160  painter->drawPixmap(point, pm);
161 
162  if (option.state & QStyle::State_Selected) {
163  QPalette::ColorGroup cg = QPalette::Active;
164  if (!(option.state & QStyle::State_Enabled)) {
165  cg = QPalette::Disabled;
166  } else if (!(option.state & QStyle::State_Active)) {
167  cg = QPalette::Inactive;
168  }
169  painter->setPen(option.palette.color(cg, QPalette::HighlightedText));
170  }
171 
172  QRect rectText;
173 
174  QString mountPointPath = placesModel->url(index).toLocalFile();
175  KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo(mountPointPath);
176  bool drawCapacityBar = info.size() != 0 &&
177  placesModel->data(index, KFilePlacesModel::CapacityBarRecommendedRole).toBool();
178 
179  if (drawCapacityBar && contentsOpacity(index) > 0)
180  {
181  painter->save();
182  painter->setOpacity(painter->opacity() * contentsOpacity(index));
183 
184  int height = option.fontMetrics.height() + CAPACITYBAR_HEIGHT;
185  rectText = QRect(isLTR ? m_iconSize + LATERAL_MARGIN * 2 + option.rect.left()
186  : 0, option.rect.top() + (option.rect.height() / 2 - height / 2), option.rect.width() - m_iconSize - LATERAL_MARGIN * 2, option.fontMetrics.height());
187  painter->drawText(rectText, Qt::AlignLeft | Qt::AlignTop, option.fontMetrics.elidedText(index.model()->data(index).toString(), Qt::ElideRight, rectText.width()));
188  QRect capacityRect(isLTR ? rectText.x() : LATERAL_MARGIN, rectText.bottom() - 1, rectText.width() - LATERAL_MARGIN, CAPACITYBAR_HEIGHT);
189  KCapacityBar capacityBar(KCapacityBar::DrawTextInline);
190  capacityBar.setValue((info.used() * 100) / info.size());
191  capacityBar.drawCapacityBar(painter, capacityRect);
192 
193  painter->restore();
194 
195  painter->save();
196  painter->setOpacity(painter->opacity() * (1 - contentsOpacity(index)));
197  }
198 
199  rectText = QRect(isLTR ? m_iconSize + LATERAL_MARGIN * 2 + option.rect.left()
200  : 0, option.rect.top(), option.rect.width() - m_iconSize - LATERAL_MARGIN * 2, option.rect.height());
201  painter->drawText(rectText, Qt::AlignLeft | Qt::AlignVCenter, option.fontMetrics.elidedText(index.model()->data(index).toString(), Qt::ElideRight, rectText.width()));
202 
203  if (drawCapacityBar && contentsOpacity(index) > 0) {
204  painter->restore();
205  }
206 
207  painter->restore();
208 }
209 
210 int KFilePlacesViewDelegate::iconSize() const
211 {
212  return m_iconSize;
213 }
214 
215 void KFilePlacesViewDelegate::setIconSize(int newSize)
216 {
217  m_iconSize = newSize;
218 }
219 
220 void KFilePlacesViewDelegate::addAppearingItem(const QModelIndex &index)
221 {
222  m_appearingItems << index;
223 }
224 
225 void KFilePlacesViewDelegate::setAppearingItemProgress(qreal value)
226 {
227  if (value<=0.25) {
228  m_appearingOpacity = 0.0;
229  m_appearingIconSize = iconSize()*value*4;
230 
231  if (m_appearingIconSize>=m_iconSize) {
232  m_appearingIconSize = m_iconSize;
233  }
234  } else {
235  m_appearingIconSize = m_iconSize;
236  m_appearingOpacity = (value-0.25)*4/3;
237 
238  if (value>=1.0) {
239  m_appearingItems.clear();
240  }
241  }
242 }
243 
244 void KFilePlacesViewDelegate::addDisappearingItem(const QModelIndex &index)
245 {
246  m_disappearingItems << index;
247 }
248 
249 void KFilePlacesViewDelegate::setDisappearingItemProgress(qreal value)
250 {
251  value = 1.0 - value;
252 
253  if (value<=0.25) {
254  m_disappearingOpacity = 0.0;
255  m_disappearingIconSize = iconSize()*value*4;
256 
257  if (m_disappearingIconSize>=m_iconSize) {
258  m_disappearingIconSize = m_iconSize;
259  }
260 
261  if (value<=0.0) {
262  m_disappearingItems.clear();
263  }
264  } else {
265  m_disappearingIconSize = m_iconSize;
266  m_disappearingOpacity = (value-0.25)*4/3;
267  }
268 }
269 
270 void KFilePlacesViewDelegate::setShowHoverIndication(bool show)
271 {
272  m_showHoverIndication = show;
273 }
274 
275 void KFilePlacesViewDelegate::addFadeAnimation(const QModelIndex &index, QTimeLine *timeLine)
276 {
277  m_timeLineMap.insert(index, timeLine);
278  m_timeLineInverseMap.insert(timeLine, index);
279 }
280 
281 void KFilePlacesViewDelegate::removeFadeAnimation(const QModelIndex &index)
282 {
283  QTimeLine *timeLine = m_timeLineMap.value(index, 0);
284  m_timeLineMap.remove(index);
285  m_timeLineInverseMap.remove(timeLine);
286 }
287 
288 QModelIndex KFilePlacesViewDelegate::indexForFadeAnimation(QTimeLine *timeLine) const
289 {
290  return m_timeLineInverseMap.value(timeLine, QModelIndex());
291 }
292 
293 QTimeLine *KFilePlacesViewDelegate::fadeAnimationForIndex(const QModelIndex &index) const
294 {
295  return m_timeLineMap.value(index, 0);
296 }
297 
298 qreal KFilePlacesViewDelegate::contentsOpacity(const QModelIndex &index) const
299 {
300  QTimeLine *timeLine = fadeAnimationForIndex(index);
301  if (timeLine) {
302  return timeLine->currentValue();
303  }
304  return 0;
305 }
306 
307 class KFilePlacesView::Private
308 {
309 public:
310  Private(KFilePlacesView *parent) : q(parent), watcher(new KFilePlacesEventWatcher(q)) { }
311 
312  enum FadeType {
313  FadeIn = 0,
314  FadeOut
315  };
316 
317  KFilePlacesView * const q;
318 
319  KUrl currentUrl;
320  bool autoResizeItems;
321  bool showAll;
322  bool smoothItemResizing;
323  bool dropOnPlace;
324  bool dragging;
325  Solid::StorageAccess *lastClickedStorage;
326  QPersistentModelIndex lastClickedIndex;
327 
328  QRect dropRect;
329 
330  void setCurrentIndex(const QModelIndex &index);
331  void adaptItemSize();
332  void updateHiddenRows();
333  bool insertAbove(const QRect &itemRect, const QPoint &pos) const;
334  bool insertBelow(const QRect &itemRect, const QPoint &pos) const;
335  int insertIndicatorHeight(int itemHeight) const;
336  void fadeCapacityBar(const QModelIndex &index, FadeType fadeType);
337 
338  void _k_placeClicked(const QModelIndex &index);
339  void _k_placeEntered(const QModelIndex &index);
340  void _k_placeLeft(const QModelIndex &index);
341  void _k_storageSetupDone(const QModelIndex &index, bool success);
342  void _k_adaptItemsUpdate(qreal value);
343  void _k_itemAppearUpdate(qreal value);
344  void _k_itemDisappearUpdate(qreal value);
345  void _k_enableSmoothItemResizing();
346  void _k_trashUpdated(KJob *job);
347  void _k_capacityBarFadeValueChanged();
348  void _k_triggerDevicePolling();
349 
350  QTimeLine adaptItemsTimeline;
351  int oldSize, endSize;
352 
353  QTimeLine itemAppearTimeline;
354  QTimeLine itemDisappearTimeline;
355 
356  KFilePlacesEventWatcher *const watcher;
357  KFilePlacesViewDelegate *delegate;
358  QTimer pollDevices;
359  int pollingRequestCount;
360 };
361 
362 KFilePlacesView::KFilePlacesView(QWidget *parent)
363  : QListView(parent), d(new Private(this))
364 {
365  d->showAll = false;
366  d->smoothItemResizing = false;
367  d->dropOnPlace = false;
368  d->autoResizeItems = true;
369  d->dragging = false;
370  d->lastClickedStorage = 0;
371  d->pollingRequestCount = 0;
372  d->delegate = new KFilePlacesViewDelegate(this);
373 
374  setSelectionRectVisible(false);
375  setSelectionMode(SingleSelection);
376 
377  setDragEnabled(true);
378  setAcceptDrops(true);
379  setMouseTracking(true);
380  setDropIndicatorShown(false);
381  setFrameStyle(QFrame::NoFrame);
382 
383  setResizeMode(Adjust);
384  setItemDelegate(d->delegate);
385 
386  QPalette palette = viewport()->palette();
387  palette.setColor(viewport()->backgroundRole(), Qt::transparent);
388  palette.setColor(viewport()->foregroundRole(), palette.color(QPalette::WindowText));
389  viewport()->setPalette(palette);
390 
391  connect(this, SIGNAL(clicked(QModelIndex)),
392  this, SLOT(_k_placeClicked(QModelIndex)));
393  // Note: Don't connect to the activated() signal, as the behavior when it is
394  // committed depends on the used widget style. The click behavior of
395  // KFilePlacesView should be style independent.
396 
397  connect(&d->adaptItemsTimeline, SIGNAL(valueChanged(qreal)),
398  this, SLOT(_k_adaptItemsUpdate(qreal)));
399  d->adaptItemsTimeline.setDuration(500);
400  d->adaptItemsTimeline.setUpdateInterval(5);
401  d->adaptItemsTimeline.setCurveShape(QTimeLine::EaseInOutCurve);
402 
403  connect(&d->itemAppearTimeline, SIGNAL(valueChanged(qreal)),
404  this, SLOT(_k_itemAppearUpdate(qreal)));
405  d->itemAppearTimeline.setDuration(500);
406  d->itemAppearTimeline.setUpdateInterval(5);
407  d->itemAppearTimeline.setCurveShape(QTimeLine::EaseInOutCurve);
408 
409  connect(&d->itemDisappearTimeline, SIGNAL(valueChanged(qreal)),
410  this, SLOT(_k_itemDisappearUpdate(qreal)));
411  d->itemDisappearTimeline.setDuration(500);
412  d->itemDisappearTimeline.setUpdateInterval(5);
413  d->itemDisappearTimeline.setCurveShape(QTimeLine::EaseInOutCurve);
414 
415  viewport()->installEventFilter(d->watcher);
416  connect(d->watcher, SIGNAL(entryEntered(QModelIndex)),
417  this, SLOT(_k_placeEntered(QModelIndex)));
418  connect(d->watcher, SIGNAL(entryLeft(QModelIndex)),
419  this, SLOT(_k_placeLeft(QModelIndex)));
420 
421  d->pollDevices.setInterval(5000);
422  connect(&d->pollDevices, SIGNAL(timeout()), this, SLOT(_k_triggerDevicePolling()));
423 
424  // FIXME: this is necessary to avoid flashes of black with some widget styles.
425  // could be a bug in Qt (e.g. QAbstractScrollArea) or KFilePlacesView, but has not
426  // yet been tracked down yet. until then, this works and is harmlessly enough.
427  // in fact, some QStyle (Oxygen, Skulpture, others?) do this already internally.
428  // See br #242358 for more information
429  verticalScrollBar()->setAttribute(Qt::WA_OpaquePaintEvent, false);
430 }
431 
432 KFilePlacesView::~KFilePlacesView()
433 {
434  delete d;
435 }
436 
437 void KFilePlacesView::setDropOnPlaceEnabled(bool enabled)
438 {
439  d->dropOnPlace = enabled;
440 }
441 
442 bool KFilePlacesView::isDropOnPlaceEnabled() const
443 {
444  return d->dropOnPlace;
445 }
446 
447 void KFilePlacesView::setAutoResizeItemsEnabled(bool enabled)
448 {
449  d->autoResizeItems = enabled;
450 }
451 
452 bool KFilePlacesView::isAutoResizeItemsEnabled() const
453 {
454  return d->autoResizeItems;
455 }
456 
457 void KFilePlacesView::setUrl(const KUrl &url)
458 {
459  KUrl oldUrl = d->currentUrl;
460  KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
461 
462  if (placesModel==0) return;
463 
464  QModelIndex index = placesModel->closestItem(url);
465  QModelIndex current = selectionModel()->currentIndex();
466 
467  if (index.isValid()) {
468  if (current!=index && placesModel->isHidden(current) && !d->showAll) {
469  KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
470  delegate->addDisappearingItem(current);
471 
472  if (d->itemDisappearTimeline.state()!=QTimeLine::Running) {
473  delegate->setDisappearingItemProgress(0.0);
474  d->itemDisappearTimeline.start();
475  }
476  }
477 
478  if (current!=index && placesModel->isHidden(index) && !d->showAll) {
479  KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
480  delegate->addAppearingItem(index);
481 
482  if (d->itemAppearTimeline.state()!=QTimeLine::Running) {
483  delegate->setAppearingItemProgress(0.0);
484  d->itemAppearTimeline.start();
485  }
486 
487  setRowHidden(index.row(), false);
488  }
489 
490  d->currentUrl = url;
491  selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
492  } else {
493  d->currentUrl = KUrl();
494  selectionModel()->clear();
495  }
496 
497  if (!current.isValid()) {
498  d->updateHiddenRows();
499  }
500 }
501 
502 void KFilePlacesView::setShowAll(bool showAll)
503 {
504  KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
505 
506  if (placesModel==0) return;
507 
508  d->showAll = showAll;
509 
510  KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
511 
512  int rowCount = placesModel->rowCount();
513  QModelIndex current = placesModel->closestItem(d->currentUrl);
514 
515  if (showAll) {
516  d->updateHiddenRows();
517 
518  for (int i=0; i<rowCount; ++i) {
519  QModelIndex index = placesModel->index(i, 0);
520  if (index!=current && placesModel->isHidden(index)) {
521  delegate->addAppearingItem(index);
522  }
523  }
524 
525  if (d->itemAppearTimeline.state()!=QTimeLine::Running) {
526  delegate->setAppearingItemProgress(0.0);
527  d->itemAppearTimeline.start();
528  }
529  } else {
530  for (int i=0; i<rowCount; ++i) {
531  QModelIndex index = placesModel->index(i, 0);
532  if (index!=current && placesModel->isHidden(index)) {
533  delegate->addDisappearingItem(index);
534  }
535  }
536 
537  if (d->itemDisappearTimeline.state()!=QTimeLine::Running) {
538  delegate->setDisappearingItemProgress(0.0);
539  d->itemDisappearTimeline.start();
540  }
541  }
542 }
543 
544 void KFilePlacesView::keyPressEvent(QKeyEvent *event)
545 {
546  QListView::keyPressEvent(event);
547  if ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Enter)) {
548  d->_k_placeClicked(currentIndex());
549  }
550 }
551 
552 void KFilePlacesView::contextMenuEvent(QContextMenuEvent *event)
553 {
554  KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
555  KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
556 
557  if (placesModel==0) return;
558 
559  QModelIndex index = indexAt(event->pos());
560  QString label = placesModel->text(index).replace('&',"&&");
561 
562  KMenu menu;
563 
564  QAction *edit = 0;
565  QAction *hide = 0;
566  QAction *emptyTrash = 0;
567  QAction *eject = 0;
568  QAction *teardown = 0;
569  QAction *add = 0;
570  QAction *mainSeparator = 0;
571 
572  if (index.isValid()) {
573  if (!placesModel->isDevice(index)) {
574  if (placesModel->url(index) == KUrl("trash:/")) {
575  emptyTrash = menu.addAction(KIcon("trash-empty"), i18nc("@action:inmenu", "Empty Trash"));
576  KConfig trashConfig("trashrc", KConfig::SimpleConfig);
577  emptyTrash->setEnabled(!trashConfig.group("Status").readEntry("Empty", true));
578  menu.addSeparator();
579  }
580  add = menu.addAction(KIcon("document-new"), i18n("Add Entry..."));
581  mainSeparator = menu.addSeparator();
582  edit = menu.addAction(KIcon("document-properties"), i18n("&Edit Entry '%1'...", label));
583  } else {
584  eject = placesModel->ejectActionForIndex(index);
585  if (eject!=0) {
586  eject->setParent(&menu);
587  menu.addAction(eject);
588  }
589 
590  teardown = placesModel->teardownActionForIndex(index);
591  if (teardown!=0) {
592  teardown->setParent(&menu);
593  menu.addAction(teardown);
594  }
595 
596  if (teardown!=0 || eject!=0) {
597  mainSeparator = menu.addSeparator();
598  }
599  }
600  if (add == 0) {
601  add = menu.addAction(KIcon("document-new"), i18n("Add Entry..."));
602  }
603 
604  hide = menu.addAction(i18n("&Hide Entry '%1'", label));
605  hide->setCheckable(true);
606  hide->setChecked(placesModel->isHidden(index));
607  } else {
608  add = menu.addAction(KIcon("document-new"), i18n("Add Entry..."));
609  }
610 
611  QAction *showAll = 0;
612  if (placesModel->hiddenCount()>0) {
613  showAll = new QAction(i18n("&Show All Entries"), &menu);
614  showAll->setCheckable(true);
615  showAll->setChecked(d->showAll);
616  if (mainSeparator == 0) {
617  mainSeparator = menu.addSeparator();
618  }
619  menu.insertAction(mainSeparator, showAll);
620  }
621 
622  QAction* remove = 0;
623  if (index.isValid() && !placesModel->isDevice(index)) {
624  remove = menu.addAction( KIcon("edit-delete"), i18n("&Remove Entry '%1'", label));
625  }
626 
627  menu.addActions(actions());
628 
629  if (menu.isEmpty()) {
630  return;
631  }
632 
633  QAction *result = menu.exec(event->globalPos());
634 
635  if (emptyTrash != 0 && result == emptyTrash) {
636  const QString text = i18nc("@info", "Do you really want to empty the Trash? All items will be deleted.");
637  const bool del = KMessageBox::warningContinueCancel(window(),
638  text,
639  QString(),
640  KGuiItem(i18nc("@action:button", "Empty Trash"),
641  KIcon("user-trash"))
642  ) == KMessageBox::Continue;
643  if (del) {
644  QByteArray packedArgs;
645  QDataStream stream(&packedArgs, QIODevice::WriteOnly);
646  stream << int(1);
647  KIO::Job *job = KIO::special(KUrl("trash:/"), packedArgs);
648  KNotification::event("Trash: emptied", QString() , QPixmap() , 0, KNotification::DefaultEvent);
649  job->ui()->setWindow(parentWidget());
650  connect(job, SIGNAL(result(KJob*)), SLOT(_k_trashUpdated(KJob*)));
651  }
652  } else if (edit != 0 && result == edit) {
653  KBookmark bookmark = placesModel->bookmarkForIndex(index);
654  KUrl url = bookmark.url();
655  QString label = bookmark.text();
656  QString iconName = bookmark.icon();
657  bool appLocal = !bookmark.metaDataItem("OnlyInApp").isEmpty();
658 
659  if (KFilePlaceEditDialog::getInformation(true, url, label,
660  iconName, false, appLocal, 64, this))
661  {
662  QString appName;
663  if (appLocal) appName = KGlobal::mainComponent().componentName();
664 
665  placesModel->editPlace(index, label, url, iconName, appName);
666  }
667 
668  } else if (remove != 0 && result == remove) {
669  placesModel->removePlace(index);
670  } else if (hide != 0 && result == hide) {
671  placesModel->setPlaceHidden(index, hide->isChecked());
672  QModelIndex current = placesModel->closestItem(d->currentUrl);
673 
674  if (index!=current && !d->showAll && hide->isChecked()) {
675  delegate->addDisappearingItem(index);
676 
677  if (d->itemDisappearTimeline.state()!=QTimeLine::Running) {
678  delegate->setDisappearingItemProgress(0.0);
679  d->itemDisappearTimeline.start();
680  }
681  }
682  } else if (showAll != 0 && result == showAll) {
683  setShowAll(showAll->isChecked());
684  } else if (teardown != 0 && result == teardown) {
685  placesModel->requestTeardown(index);
686  } else if (eject != 0 && result == eject) {
687  placesModel->requestEject(index);
688  } else if (add != 0 && result == add) {
689  KUrl url = d->currentUrl;
690  QString label;
691  QString iconName = "folder";
692  bool appLocal = true;
693  if (KFilePlaceEditDialog::getInformation(true, url, label,
694  iconName, true, appLocal, 64, this))
695  {
696  QString appName;
697  if (appLocal) appName = KGlobal::mainComponent().componentName();
698 
699  placesModel->addPlace(label, url, iconName, appName, index);
700  }
701  }
702 
703  index = placesModel->closestItem(d->currentUrl);
704  selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
705 }
706 
707 void KFilePlacesView::resizeEvent(QResizeEvent *event)
708 {
709  QListView::resizeEvent(event);
710  d->adaptItemSize();
711 }
712 
713 void KFilePlacesView::showEvent(QShowEvent *event)
714 {
715  QListView::showEvent(event);
716  QTimer::singleShot(100, this, SLOT(_k_enableSmoothItemResizing()));
717 }
718 
719 void KFilePlacesView::hideEvent(QHideEvent *event)
720 {
721  QListView::hideEvent(event);
722  d->smoothItemResizing = false;
723 }
724 
725 void KFilePlacesView::dragEnterEvent(QDragEnterEvent *event)
726 {
727  QListView::dragEnterEvent(event);
728  d->dragging = true;
729 
730  KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
731  delegate->setShowHoverIndication(false);
732 
733  d->dropRect = QRect();
734 }
735 
736 void KFilePlacesView::dragLeaveEvent(QDragLeaveEvent *event)
737 {
738  QListView::dragLeaveEvent(event);
739  d->dragging = false;
740 
741  KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
742  delegate->setShowHoverIndication(true);
743 
744  setDirtyRegion(d->dropRect);
745 }
746 
747 void KFilePlacesView::dragMoveEvent(QDragMoveEvent *event)
748 {
749  QListView::dragMoveEvent(event);
750 
751  // update the drop indicator
752  const QPoint pos = event->pos();
753  const QModelIndex index = indexAt(pos);
754  setDirtyRegion(d->dropRect);
755  if (index.isValid()) {
756  const QRect rect = visualRect(index);
757  const int gap = d->insertIndicatorHeight(rect.height());
758  if (d->insertAbove(rect, pos)) {
759  // indicate that the item will be inserted above the current place
760  d->dropRect = QRect(rect.left(), rect.top() - gap / 2,
761  rect.width(), gap);
762  } else if (d->insertBelow(rect, pos)) {
763  // indicate that the item will be inserted below the current place
764  d->dropRect = QRect(rect.left(), rect.bottom() + 1 - gap / 2,
765  rect.width(), gap);
766  } else {
767  // indicate that the item be dropped above the current place
768  d->dropRect = rect;
769  }
770  }
771 
772  setDirtyRegion(d->dropRect);
773 }
774 
775 void KFilePlacesView::dropEvent(QDropEvent *event)
776 {
777  const QPoint pos = event->pos();
778  const QModelIndex index = indexAt(pos);
779  if (index.isValid()) {
780  const QRect rect = visualRect(index);
781  if (!d->insertAbove(rect, pos) && !d->insertBelow(rect, pos)) {
782  KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
783  Q_ASSERT(placesModel != 0);
784  emit urlsDropped(placesModel->url(index), event, this);
785  event->acceptProposedAction();
786  }
787  }
788 
789  QListView::dropEvent(event);
790  d->dragging = false;
791 
792  KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
793  delegate->setShowHoverIndication(true);
794 }
795 
796 void KFilePlacesView::paintEvent(QPaintEvent* event)
797 {
798  QListView::paintEvent(event);
799  if (d->dragging && !d->dropRect.isEmpty()) {
800  // draw drop indicator
801  QPainter painter(viewport());
802 
803  const QModelIndex index = indexAt(d->dropRect.topLeft());
804  const QRect itemRect = visualRect(index);
805  const bool drawInsertIndicator = !d->dropOnPlace ||
806  d->dropRect.height() <= d->insertIndicatorHeight(itemRect.height());
807 
808  if (drawInsertIndicator) {
809  // draw indicator for inserting items
810  QBrush blendedBrush = viewOptions().palette.brush(QPalette::Normal, QPalette::Highlight);
811  QColor color = blendedBrush.color();
812 
813  const int y = (d->dropRect.top() + d->dropRect.bottom()) / 2;
814  const int thickness = d->dropRect.height() / 2;
815  Q_ASSERT(thickness >= 1);
816  int alpha = 255;
817  const int alphaDec = alpha / (thickness + 1);
818  for (int i = 0; i < thickness; i++) {
819  color.setAlpha(alpha);
820  alpha -= alphaDec;
821  painter.setPen(color);
822  painter.drawLine(d->dropRect.left(), y - i, d->dropRect.right(), y - i);
823  painter.drawLine(d->dropRect.left(), y + i, d->dropRect.right(), y + i);
824  }
825  } else {
826  // draw indicator for copying/moving/linking to items
827  QStyleOptionViewItemV4 opt;
828  opt.initFrom(this);
829  opt.rect = itemRect;
830  opt.state = QStyle::State_Enabled | QStyle::State_MouseOver;
831  style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, &painter, this);
832  }
833  }
834 }
835 
836 void KFilePlacesView::setModel(QAbstractItemModel *model)
837 {
838  QListView::setModel(model);
839  d->updateHiddenRows();
840  // Uses Qt::QueuedConnection to delay the time when the slot will be
841  // called. In case of an item move the remove+add will be done before
842  // we adapt the item size (otherwise we'd get it wrong as we'd execute
843  // it after the remove only).
844  connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
845  this, SLOT(adaptItemSize()), Qt::QueuedConnection);
846  connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
847  d->watcher, SLOT(currentIndexChanged(QModelIndex)));
848 }
849 
850 void KFilePlacesView::rowsInserted(const QModelIndex &parent, int start, int end)
851 {
852  QListView::rowsInserted(parent, start, end);
853  setUrl(d->currentUrl);
854 
855  KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(itemDelegate());
856  KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
857 
858  for (int i=start; i<=end; ++i) {
859  QModelIndex index = placesModel->index(i, 0, parent);
860  if (d->showAll || !placesModel->isHidden(index)) {
861  delegate->addAppearingItem(index);
862  } else {
863  setRowHidden(i, true);
864  }
865  }
866 
867  if (d->itemAppearTimeline.state()!=QTimeLine::Running) {
868  delegate->setAppearingItemProgress(0.0);
869  d->itemAppearTimeline.start();
870  }
871 
872  d->adaptItemSize();
873 }
874 
875 QSize KFilePlacesView::sizeHint() const
876 {
877  KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(model());
878  if (!placesModel) {
879  return QListView::sizeHint();
880  }
881  const int height = QListView::sizeHint().height();
882  QFontMetrics fm = d->q->fontMetrics();
883  int textWidth = 0;
884 
885  for (int i=0; i<placesModel->rowCount(); ++i) {
886  QModelIndex index = placesModel->index(i, 0);
887  if (!placesModel->isHidden(index))
888  textWidth = qMax(textWidth,fm.width(index.data(Qt::DisplayRole).toString()));
889  }
890 
891  const int iconSize = KIconLoader::global()->currentSize(KIconLoader::Dialog);
892  return QSize(iconSize + textWidth + fm.height() / 2, height);
893 }
894 
895 void KFilePlacesView::Private::setCurrentIndex(const QModelIndex &index)
896 {
897  KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
898 
899  if (placesModel==0) return;
900 
901  KUrl url = placesModel->url(index);
902 
903  if (url.isValid()) {
904  currentUrl = url;
905  updateHiddenRows();
906  emit q->urlChanged(url);
907  if (showAll) {
908  q->setShowAll(false);
909  }
910  } else {
911  q->setUrl(currentUrl);
912  }
913 }
914 
915 void KFilePlacesView::Private::adaptItemSize()
916 {
917  KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(q->itemDelegate());
918  if (!delegate) return;
919 
920  if (!autoResizeItems) {
921  int size = q->iconSize().width(); // Assume width == height
922  delegate->setIconSize(size);
923  q->scheduleDelayedItemsLayout();
924  return;
925  }
926 
927  KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
928 
929  if (placesModel==0) return;
930 
931  int rowCount = placesModel->rowCount();
932 
933  if (!showAll) {
934  rowCount-= placesModel->hiddenCount();
935 
936  QModelIndex current = placesModel->closestItem(currentUrl);
937 
938  if (placesModel->isHidden(current)) {
939  rowCount++;
940  }
941  }
942 
943  if (rowCount==0) return; // We've nothing to display anyway
944 
945  const int minSize = 16;
946  const int maxSize = 64;
947 
948  int textWidth = 0;
949  QFontMetrics fm = q->fontMetrics();
950  for (int i=0; i<placesModel->rowCount(); ++i) {
951  QModelIndex index = placesModel->index(i, 0);
952 
953  if (!placesModel->isHidden(index))
954  textWidth = qMax(textWidth,fm.width(index.data(Qt::DisplayRole).toString()));
955  }
956 
957  const int margin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, q) + 1;
958  const int maxWidth = q->viewport()->width() - textWidth - 4 * margin - 1;
959  const int maxHeight = ((q->height() - (fm.height() / 2) * rowCount) / rowCount) - 1;
960 
961  int size = qMin(maxHeight, maxWidth);
962 
963  if (size<minSize) {
964  size = minSize;
965  } else if (size>maxSize) {
966  size = maxSize;
967  } else {
968  // Make it a multiple of 16
969  size &= ~0xf;
970  }
971 
972  if (size==delegate->iconSize()) return;
973 
974  if (smoothItemResizing) {
975  oldSize = delegate->iconSize();
976  endSize = size;
977  if (adaptItemsTimeline.state()!=QTimeLine::Running) {
978  adaptItemsTimeline.start();
979  }
980  } else {
981  delegate->setIconSize(size);
982  q->scheduleDelayedItemsLayout();
983  }
984 }
985 
986 void KFilePlacesView::Private::updateHiddenRows()
987 {
988  KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
989 
990  if (placesModel==0) return;
991 
992  int rowCount = placesModel->rowCount();
993  QModelIndex current = placesModel->closestItem(currentUrl);
994 
995  for (int i=0; i<rowCount; ++i) {
996  QModelIndex index = placesModel->index(i, 0);
997  if (index!=current && placesModel->isHidden(index) && !showAll) {
998  q->setRowHidden(i, true);
999  } else {
1000  q->setRowHidden(i, false);
1001  }
1002  }
1003 
1004  adaptItemSize();
1005 }
1006 
1007 bool KFilePlacesView::Private::insertAbove(const QRect &itemRect, const QPoint &pos) const
1008 {
1009  if (dropOnPlace) {
1010  return pos.y() < itemRect.top() + insertIndicatorHeight(itemRect.height()) / 2;
1011  }
1012 
1013  return pos.y() < itemRect.top() + (itemRect.height() / 2);
1014 }
1015 
1016 bool KFilePlacesView::Private::insertBelow(const QRect &itemRect, const QPoint &pos) const
1017 {
1018  if (dropOnPlace) {
1019  return pos.y() > itemRect.bottom() - insertIndicatorHeight(itemRect.height()) / 2;
1020  }
1021 
1022  return pos.y() >= itemRect.top() + (itemRect.height() / 2);
1023 }
1024 
1025 int KFilePlacesView::Private::insertIndicatorHeight(int itemHeight) const
1026 {
1027  const int min = 4;
1028  const int max = 12;
1029 
1030  int height = itemHeight / 4;
1031  if (height < min) {
1032  height = min;
1033  } else if (height > max) {
1034  height = max;
1035  }
1036  return height;
1037 }
1038 
1039 void KFilePlacesView::Private::fadeCapacityBar(const QModelIndex &index, FadeType fadeType)
1040 {
1041  QTimeLine *timeLine = delegate->fadeAnimationForIndex(index);
1042  delete timeLine;
1043  delegate->removeFadeAnimation(index);
1044  timeLine = new QTimeLine(250, q);
1045  connect(timeLine, SIGNAL(valueChanged(qreal)), q, SLOT(_k_capacityBarFadeValueChanged()));
1046  if (fadeType == FadeIn) {
1047  timeLine->setDirection(QTimeLine::Forward);
1048  timeLine->setCurrentTime(0);
1049  } else {
1050  timeLine->setDirection(QTimeLine::Backward);
1051  timeLine->setCurrentTime(250);
1052  }
1053  delegate->addFadeAnimation(index, timeLine);
1054  timeLine->start();
1055 }
1056 
1057 void KFilePlacesView::Private::_k_placeClicked(const QModelIndex &index)
1058 {
1059  KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
1060 
1061  if (placesModel==0) return;
1062 
1063  lastClickedIndex = QPersistentModelIndex();
1064 
1065  if (placesModel->setupNeeded(index)) {
1066  QObject::connect(placesModel, SIGNAL(setupDone(QModelIndex,bool)),
1067  q, SLOT(_k_storageSetupDone(QModelIndex,bool)));
1068 
1069  lastClickedIndex = index;
1070  placesModel->requestSetup(index);
1071  return;
1072  }
1073 
1074  setCurrentIndex(index);
1075 }
1076 
1077 void KFilePlacesView::Private::_k_placeEntered(const QModelIndex &index)
1078 {
1079  fadeCapacityBar(index, FadeIn);
1080  pollingRequestCount++;
1081  if (pollingRequestCount == 1) {
1082  pollDevices.start();
1083  }
1084 }
1085 
1086 void KFilePlacesView::Private::_k_placeLeft(const QModelIndex &index)
1087 {
1088  fadeCapacityBar(index, FadeOut);
1089  pollingRequestCount--;
1090  if (!pollingRequestCount) {
1091  pollDevices.stop();
1092  }
1093 }
1094 
1095 void KFilePlacesView::Private::_k_storageSetupDone(const QModelIndex &index, bool success)
1096 {
1097  if (index!=lastClickedIndex) {
1098  return;
1099  }
1100 
1101  KFilePlacesModel *placesModel = qobject_cast<KFilePlacesModel*>(q->model());
1102 
1103  QObject::disconnect(placesModel, SIGNAL(setupDone(QModelIndex,bool)),
1104  q, SLOT(_k_storageSetupDone(QModelIndex,bool)));
1105 
1106  if (success) {
1107  setCurrentIndex(lastClickedIndex);
1108  } else {
1109  q->setUrl(currentUrl);
1110  }
1111 
1112  lastClickedIndex = QPersistentModelIndex();
1113 }
1114 
1115 void KFilePlacesView::Private::_k_adaptItemsUpdate(qreal value)
1116 {
1117  int add = (endSize-oldSize)*value;
1118 
1119  int size = oldSize+add;
1120 
1121  KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(q->itemDelegate());
1122  delegate->setIconSize(size);
1123  q->scheduleDelayedItemsLayout();
1124 }
1125 
1126 void KFilePlacesView::Private::_k_itemAppearUpdate(qreal value)
1127 {
1128  KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(q->itemDelegate());
1129 
1130  delegate->setAppearingItemProgress(value);
1131  q->scheduleDelayedItemsLayout();
1132 }
1133 
1134 void KFilePlacesView::Private::_k_itemDisappearUpdate(qreal value)
1135 {
1136  KFilePlacesViewDelegate *delegate = dynamic_cast<KFilePlacesViewDelegate*>(q->itemDelegate());
1137 
1138  delegate->setDisappearingItemProgress(value);
1139 
1140  if (value>=1.0) {
1141  updateHiddenRows();
1142  }
1143 
1144  q->scheduleDelayedItemsLayout();
1145 }
1146 
1147 void KFilePlacesView::Private::_k_enableSmoothItemResizing()
1148 {
1149  smoothItemResizing = true;
1150 }
1151 
1152 void KFilePlacesView::Private::_k_trashUpdated(KJob *job)
1153 {
1154  if (job->error()) {
1155  static_cast<KIO::Job*>(job)->ui()->showErrorMessage();
1156  }
1157  org::kde::KDirNotify::emitFilesAdded("trash:/");
1158 }
1159 
1160 void KFilePlacesView::Private::_k_capacityBarFadeValueChanged()
1161 {
1162  const QModelIndex index = delegate->indexForFadeAnimation(static_cast<QTimeLine*>(q->sender()));
1163  if (!index.isValid()) {
1164  return;
1165  }
1166  q->update(index);
1167 }
1168 
1169 void KFilePlacesView::Private::_k_triggerDevicePolling()
1170 {
1171  const QModelIndex hoveredIndex = watcher->hoveredIndex();
1172  if (hoveredIndex.isValid()) {
1173  const KFilePlacesModel *placesModel = static_cast<const KFilePlacesModel*>(hoveredIndex.model());
1174  if (placesModel->isDevice(hoveredIndex)) {
1175  q->update(hoveredIndex);
1176  }
1177  }
1178  const QModelIndex focusedIndex = watcher->focusedIndex();
1179  if (focusedIndex.isValid() && focusedIndex != hoveredIndex) {
1180  const KFilePlacesModel *placesModel = static_cast<const KFilePlacesModel*>(focusedIndex.model());
1181  if (placesModel->isDevice(focusedIndex)) {
1182  q->update(focusedIndex);
1183  }
1184  }
1185 }
1186 
1187 void KFilePlacesView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
1188 {
1189  QListView::dataChanged(topLeft, bottomRight);
1190  d->adaptItemSize();
1191 }
1192 
1193 #include "kfileplacesview.moc"
1194 #include "kfileplacesview_p.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Fri Dec 7 2012 16:21:07 by doxygen 1.8.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KFile

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

kdelibs-4.8.5 API Reference

Skip menu "kdelibs-4.8.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal