2 * Copyright © 2008 Fredrik Höglund <fredrik@kde.org>
3 * Copyright © 2008 Rafael Fernández López <ereslibre@kde.org>
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 as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 #include <QApplication>
28 #include <QGraphicsView>
29 #include <QGraphicsSceneHoverEvent>
30 #include <QGraphicsSceneMouseEvent>
31 #include <QGraphicsProxyWidget>
32 #include <QItemSelectionModel>
34 #include <QPaintEngine>
36 #include <QStyleOptionGraphicsItem>
39 #include <KFileItemDelegate>
40 #include <KGlobalSettings>
42 #include <konqmimedata.h>
43 #include <konq_operations.h>
45 #include "dirlister.h"
46 #include "proxymodel.h"
47 #include "previewpluginsmodel.h"
49 #include "plasma/containment.h"
50 #include "plasma/corona.h"
51 #include "plasma/paintutils.h"
52 #include "plasma/theme.h"
55 IconView::IconView(QGraphicsWidget
*parent
)
56 : AbstractItemView(parent
),
60 m_layoutBroken(false),
61 m_needPostLayoutPass(false),
62 m_initialListing(true),
63 m_positionsLoaded(false),
65 m_dragInProgress(false),
70 m_flow(QListView::LeftToRight
)
72 setAcceptHoverEvents(true);
74 setCacheMode(NoCache
);
78 int size
= style()->pixelMetric(QStyle::PM_LargeIconSize
);
79 m_iconSize
= QSize(size
, size
);
80 m_gridSize
= QSize(size
* 2, size
* 2);
82 getContentsMargins(&m_margins
[Plasma::LeftMargin
], &m_margins
[Plasma::TopMargin
],
83 &m_margins
[Plasma::RightMargin
], &m_margins
[Plasma::BottomMargin
]);
90 void IconView::setModel(QAbstractItemModel
*model
)
92 AbstractItemView::setModel(model
);
94 KDirLister
*lister
= m_dirModel
->dirLister();
95 connect(lister
, SIGNAL(started(KUrl
)), SLOT(listingStarted(KUrl
)));
96 connect(lister
, SIGNAL(clear()), SLOT(listingClear()));
97 connect(lister
, SIGNAL(completed()), SLOT(listingCompleted()));
98 connect(lister
, SIGNAL(canceled()), SLOT(listingCanceled()));
99 connect(lister
, SIGNAL(showErrorMessage(QString
)), SLOT(listingError(QString
)));
100 connect(lister
, SIGNAL(itemsDeleted(KFileItemList
)), SLOT(itemsDeleted(KFileItemList
)));
103 m_layoutBroken
= false;
105 if (m_model
->rowCount() > 0) {
106 m_delayedLayoutTimer
.start(10, this);
111 void IconView::setIconSize(const QSize
&size
)
113 if (size
!= m_iconSize
) {
116 // Schedule a full relayout
117 if (m_validRows
> 0) {
119 m_delayedLayoutTimer
.start(10, this);
125 void IconView::setGridSize(const QSize
&size
)
127 if (size
!= m_gridSize
) {
130 // Schedule a full relayout
131 if (m_validRows
> 0) {
133 m_delayedLayoutTimer
.start(10, this);
139 QSize
IconView::gridSize() const
144 void IconView::setWordWrap(bool on
)
146 if (m_wordWrap
!= on
) {
149 // Schedule a full relayout
150 if (m_validRows
> 0) {
152 m_delayedLayoutTimer
.start(10, this);
158 bool IconView::wordWrap() const
163 void IconView::setFlow(QListView::Flow flow
)
165 if (m_flow
!= flow
) {
168 // Schedule a full relayout
169 if (!m_layoutBroken
&& m_validRows
> 0) {
171 m_delayedLayoutTimer
.start(10, this);
177 QListView::Flow
IconView::flow() const
182 void IconView::setAlignToGrid(bool on
)
184 if (on
&& !m_alignToGrid
&& m_validRows
> 0) {
191 bool IconView::alignToGrid() const
193 return m_alignToGrid
;
196 void IconView::setIconsMoveable(bool on
)
201 bool IconView::iconsMoveable() const
203 return !m_iconsLocked
;
206 void IconView::setDrawShadows(bool on
)
208 if (m_drawShadows
!= on
) {
210 markAreaDirty(visibleArea());
215 bool IconView::drawShadows() const
217 return m_drawShadows
;
220 void IconView::setIconPositionsData(const QStringList
&data
)
223 if (data
.size() < 5 || data
.at(0).toInt() != 1 || ((data
.size() - 2) % 3) ||
224 data
.at(1).toInt() != ((data
.size() - 2) / 3)) {
228 const QPoint offset
= contentsRect().topLeft().toPoint();
229 for (int i
= 2; i
< data
.size(); i
+= 3) {
230 const QString
&name
= data
.at(i
);
231 int x
= data
.at(i
+ 1).toInt();
232 int y
= data
.at(i
+ 2).toInt();
233 m_savedPositions
.insert(name
, QPoint(x
, y
) + offset
);
237 QStringList
IconView::iconPositionsData() const
241 if (m_layoutBroken
&& !m_initialListing
&& m_validRows
== m_items
.size()) {
243 data
<< QString::number(version
);
244 data
<< QString::number(m_items
.size());
246 const QPoint offset
= contentsRect().topLeft().toPoint();
247 const QSize size
= gridSize();
248 for (int i
= 0; i
< m_items
.size(); i
++) {
249 QModelIndex index
= m_model
->index(i
, 0);
250 KFileItem item
= m_model
->itemForIndex(index
);
252 data
<< QString::number(m_items
[i
].rect
.x() - offset
.x());
253 data
<< QString::number(m_items
[i
].rect
.y() - offset
.y());
260 void IconView::rowsInserted(const QModelIndex
&parent
, int first
, int last
)
263 m_regionCache
.clear();
265 if (!m_layoutBroken
|| m_initialListing
) {
266 if (first
< m_validRows
) {
269 m_delayedLayoutTimer
.start(10, this);
272 const QStyleOptionViewItemV4 option
= viewOptions();
273 const QRect cr
= contentsRect().toRect();
274 const QSize grid
= gridSize();
275 QPoint pos
= QPoint();
277 m_items
.insert(first
, last
- first
+ 1, ViewItem());
279 // If a single item was inserted and we have a saved position from a deleted file,
280 // reuse that position.
281 if (first
== last
&& !m_lastDeletedPos
.isNull()) {
282 m_items
[first
].rect
= QRect(m_lastDeletedPos
, grid
);
283 m_items
[first
].layouted
= true;
284 m_items
[first
].needSizeAdjust
= true;
285 markAreaDirty(m_items
[first
].rect
);
286 m_lastDeletedPos
= QPoint();
287 m_validRows
= m_items
.size();
291 // Lay out the newly inserted files
292 for (int i
= first
; i
<= last
; i
++) {
293 pos
= findNextEmptyPosition(pos
, grid
, cr
);
294 m_items
[i
].rect
= QRect(pos
, grid
);
295 m_items
[i
].layouted
= true;
296 m_items
[i
].needSizeAdjust
= true;
297 markAreaDirty(m_items
[i
].rect
);
300 m_validRows
= m_items
.size();
305 void IconView::rowsRemoved(const QModelIndex
&parent
, int first
, int last
)
309 m_regionCache
.clear();
311 if (!m_layoutBroken
) {
312 if (first
< m_validRows
) {
315 if (m_model
->rowCount() > 0) {
316 m_delayedLayoutTimer
.start(10, this);
319 // All the items were removed
322 markAreaDirty(visibleArea());
325 for (int i
= first
; i
<= last
; i
++) {
326 markAreaDirty(m_items
[i
].rect
);
328 // When a single item is removed, we'll save the position and use it for the next new item.
329 // The reason for this is that when a file is renamed, it will first be removed from the view
330 // and then reinserted.
332 const QSize size
= gridSize();
333 m_lastDeletedPos
.rx() = m_items
[first
].rect
.x() - (size
.width() - m_items
[first
].rect
.width()) / 2;
334 m_lastDeletedPos
.ry() = m_items
[first
].rect
.y();
336 m_items
.remove(first
, last
- first
+ 1);
337 m_validRows
= m_items
.size();
342 void IconView::modelReset()
344 m_savedPositions
.clear();
345 m_layoutBroken
= false;
348 m_delayedLayoutTimer
.start(10, this);
352 void IconView::layoutChanged()
354 m_savedPositions
.clear();
355 m_layoutBroken
= false;
358 m_delayedLayoutTimer
.start(10, this);
362 void IconView::dataChanged(const QModelIndex
&topLeft
, const QModelIndex
&bottomRight
)
364 const QStyleOptionViewItemV4 option
= viewOptions();
365 const QSize grid
= gridSize();
366 m_regionCache
.clear();
368 // Update the size of the items and center them in the grid cell
369 for (int i
= topLeft
.row(); i
<= bottomRight
.row() && i
< m_items
.size(); i
++) {
370 if (!m_items
[i
].layouted
) {
373 m_items
[i
].rect
.setSize(grid
);
374 m_items
[i
].needSizeAdjust
= true;
375 markAreaDirty(m_items
[i
].rect
);
379 void IconView::listingStarted(const KUrl
&url
)
383 // Reset any error message that may have resulted from an earlier listing
384 if (!m_errorMessage
.isEmpty()) {
385 m_errorMessage
.clear();
392 void IconView::listingClear()
394 m_initialListing
= true;
395 markAreaDirty(visibleArea());
400 void IconView::listingCompleted()
402 m_delayedCacheClearTimer
.start(5000, this);
403 m_initialListing
= false;
405 if (m_validRows
== m_model
->rowCount()) {
410 void IconView::listingCanceled()
412 m_delayedCacheClearTimer
.start(5000, this);
413 m_initialListing
= false;
415 if (m_validRows
== m_model
->rowCount()) {
420 void IconView::listingError(const QString
&message
)
422 m_errorMessage
= message
;
423 markAreaDirty(visibleArea());
426 if (m_validRows
== m_model
->rowCount()) {
431 void IconView::itemsDeleted(const KFileItemList
&items
)
433 // Check if the root item was deleted
434 if (items
.contains(m_dirModel
->dirLister()->rootItem())) {
435 const QString path
= m_dirModel
->dirLister()->url().path();
436 listingError(KIO::buildErrorString(KIO::ERR_DOES_NOT_EXIST
, path
));
440 int IconView::columnsForWidth(qreal width
) const
445 qreal available
= width
- 2 * margin
;
446 return qFloor(available
/ (gridSize().width() + spacing
));
449 int IconView::rowsForHeight(qreal height
) const
454 qreal available
= height
- 2 * margin
;
455 return qFloor(available
/ (gridSize().height() + spacing
));
458 QPoint
inline IconView::nextGridPosition(const QPoint
&lastPos
, const QSize
&grid
, const QRect
&contentRect
) const
463 if (lastPos
.isNull()) {
464 return QPoint(contentRect
.left() + margin
, contentRect
.top() + margin
);
467 QPoint pos
= lastPos
;
469 if (m_flow
== QListView::LeftToRight
) {
470 pos
.rx() += grid
.width() + spacing
;
471 if ((pos
.x() + grid
.width() + 10) >= (contentRect
.right() - m_scrollBar
->geometry().width() - margin
)) {
472 pos
.ry() += grid
.height() + spacing
;
473 pos
.rx() = contentRect
.left() + margin
;
476 pos
.ry() += grid
.height() + spacing
;
477 if ((pos
.y() + grid
.height() + 10) >= (contentRect
.bottom() - margin
)) {
478 pos
.rx() += grid
.width() + spacing
;
479 pos
.ry() = contentRect
.top() + margin
;
486 QPoint
IconView::findNextEmptyPosition(const QPoint
&prevPos
, const QSize
&gridSize
, const QRect
&contentRect
) const
488 QPoint pos
= prevPos
;
494 pos
= nextGridPosition(pos
, gridSize
, contentRect
);
495 const QRect
r(pos
, gridSize
);
496 for (int i
= 0; i
< m_items
.count(); i
++) {
497 if (m_items
.at(i
).rect
.intersects(r
)) {
507 void IconView::layoutItems()
509 QStyleOptionViewItemV4 option
= viewOptions();
510 m_items
.resize(m_model
->rowCount());
511 m_regionCache
.clear();
513 const QRect visibleRect
= mapToViewport(contentsRect()).toAlignedRect();
514 const QRect rect
= contentsRect().toRect();
515 const QSize grid
= gridSize();
516 int maxWidth
= rect
.width() - m_scrollBar
->geometry().width();
517 int maxHeight
= rect
.height();
518 m_columns
= columnsForWidth(maxWidth
);
519 m_rows
= rowsForHeight(maxHeight
);
520 bool needUpdate
= false;
522 m_delegate
->setMaximumSize(grid
);
524 // If we're starting with the first item
525 if (m_validRows
== 0) {
526 m_needPostLayoutPass
= false;
527 m_currentLayoutPos
= QPoint();
530 if (!m_savedPositions
.isEmpty()) {
531 m_layoutBroken
= true;
532 // Restart the delayed cache clear timer if it's running and we haven't
533 // finished laying out the icons.
534 if (m_delayedCacheClearTimer
.isActive() && m_validRows
< m_items
.size()) {
535 m_delayedCacheClearTimer
.start(5000, this);
538 m_layoutBroken
= false;
541 // Do a 20 millisecond layout pass
545 const int count
= qMin(m_validRows
+ 50, m_items
.size());
546 if (!m_savedPositions
.isEmpty()) {
548 // Layout with saved icon positions
549 // ================================================================
550 for (int i
= m_validRows
; i
< count
; i
++) {
551 const QModelIndex index
= m_model
->index(i
, 0);
552 KFileItem item
= m_model
->itemForIndex(index
);
553 const QPoint pos
= m_savedPositions
.value(item
.name(), QPoint(-1, -1));
554 if (pos
!= QPoint(-1, -1)) {
555 m_items
[i
].rect
= QRect(pos
, grid
);
556 m_items
[i
].layouted
= true;
557 m_items
[i
].needSizeAdjust
= true;
558 if (m_items
[i
].rect
.intersects(visibleRect
)) {
562 // We don't have a saved position for this file, so we'll record the
563 // size and lay it out in a second layout pass.
564 m_items
[i
].rect
= QRect(QPoint(), grid
);
565 m_items
[i
].layouted
= false;
566 m_items
[i
].needSizeAdjust
= true;
567 m_needPostLayoutPass
= true;
570 // If we've finished laying out all the icons
571 if (!m_initialListing
&& !m_needPostLayoutPass
&& count
== m_items
.size()) {
572 needUpdate
|= doLayoutSanityCheck();
577 // ================================================================
578 QPoint pos
= m_currentLayoutPos
;
579 for (int i
= m_validRows
; i
< count
; i
++) {
580 pos
= nextGridPosition(pos
, grid
, rect
);
581 m_items
[i
].rect
= QRect(pos
, grid
);
582 m_items
[i
].layouted
= true;
583 m_items
[i
].needSizeAdjust
= true;
584 if (m_items
[i
].rect
.intersects(visibleRect
)) {
588 m_currentLayoutPos
= pos
;
591 } while (m_validRows
< m_items
.size() && time
.elapsed() < 30);
594 // Second layout pass for files that didn't have a saved position
595 // ====================================================================
596 if (m_validRows
== m_items
.size() && m_needPostLayoutPass
) {
597 QPoint pos
= QPoint();
598 for (int i
= 0; i
< m_items
.size(); i
++) {
599 if (m_items
[i
].layouted
) {
602 pos
= findNextEmptyPosition(pos
, grid
, rect
);
603 m_items
[i
].rect
.moveTo(pos
);
604 if (m_items
[i
].rect
.intersects(visibleRect
)) {
608 needUpdate
|= doLayoutSanityCheck();
609 m_needPostLayoutPass
= false;
614 if (m_validRows
< m_items
.size() || m_needPostLayoutPass
) {
615 m_delayedLayoutTimer
.start(10, this);
616 } else if (!m_initialListing
) {
621 m_dirtyRegion
= QRegion(visibleRect
);
628 void IconView::alignIconsToGrid()
632 const QRect cr
= contentsRect().toRect();
633 const QSize size
= gridSize() + QSize(spacing
, spacing
);
634 int topMargin
= margin
+ cr
.top();
635 int leftMargin
= margin
+ cr
.left();
636 int vOffset
= topMargin
+ size
.height() / 2;
637 int hOffset
= leftMargin
+ size
.width() / 2;
638 bool layoutChanged
= false;
640 for (int i
= 0; i
< m_items
.size(); i
++) {
641 const QPoint center
= m_items
[i
].rect
.center();
642 const int col
= qRound((center
.x() - hOffset
) / qreal(size
.width()));
643 const int row
= qRound((center
.y() - vOffset
) / qreal(size
.height()));
645 const QPoint
pos(leftMargin
+ col
* size
.width() + (size
.width() - m_items
[i
].rect
.width() - spacing
) / 2,
646 topMargin
+ row
* size
.height());
648 if (pos
!= m_items
[i
].rect
.topLeft()) {
649 m_items
[i
].rect
.moveTo(pos
);
650 layoutChanged
= true;
655 doLayoutSanityCheck();
656 markAreaDirty(visibleArea());
657 m_layoutBroken
= true;
658 m_savedPositions
.clear();
659 m_regionCache
.clear();
663 QRect
IconView::itemsBoundingRect() const
666 for (int i
= 0; i
< m_validRows
; i
++) {
667 if (m_items
[i
].layouted
) {
668 boundingRect
|= m_items
[i
].rect
;
675 bool IconView::doLayoutSanityCheck()
677 // Find the bounding rect of the items
678 QRect boundingRect
= itemsBoundingRect();
681 boundingRect
.adjust(-10, -10, 10, 10);
683 const QRect cr
= contentsRect().toRect();
684 int scrollValue
= m_scrollBar
->value();
687 // Make sure no items have negative coordinates
688 if (boundingRect
.y() < cr
.top() || boundingRect
.x() < cr
.left()) {
689 delta
.rx() = qMax(0, cr
.left() - boundingRect
.x());
690 delta
.ry() = qMax(0, cr
.top() - boundingRect
.y());
693 // Remove any empty space above the visible area
694 if (delta
.y() == 0 && scrollValue
> 0) {
695 delta
.ry() = -qBound(0, boundingRect
.top() - cr
.top(), scrollValue
);
698 if (!delta
.isNull()) {
700 for (int i
= 0; i
< m_validRows
; i
++) {
701 if (m_items
[i
].layouted
) {
702 m_items
[i
].rect
.translate(delta
);
706 // Adjust the bounding rect and the scrollbar value and range
707 boundingRect
= boundingRect
.translated(delta
) | cr
;
708 scrollValue
+= delta
.y();
710 m_scrollBar
->setRange(0, qMax(boundingRect
.height() - cr
.height(), scrollValue
));
711 m_scrollBar
->setValue(scrollValue
);
713 if (m_scrollBar
->minimum() != m_scrollBar
->maximum()) {
719 m_regionCache
.clear();
724 m_scrollBar
->setRange(0, qMax(boundingRect
.height() - cr
.height(), scrollValue
));
725 m_scrollBar
->setValue(scrollValue
);
727 if (m_scrollBar
->minimum() != m_scrollBar
->maximum()) {
736 void IconView::updateScrollBar()
738 const QRect cr
= contentsRect().toRect();
739 QRect boundingRect
= itemsBoundingRect();
741 if (boundingRect
.isValid()) {
743 boundingRect
.adjust(-10, -10, 10, 10);
746 m_scrollBar
->setRange(0, boundingRect
.height() - cr
.height());
747 m_scrollBar
->setPageStep(cr
.height());
748 m_scrollBar
->setSingleStep(gridSize().height());
751 m_scrollBar
->setRange(0, 0);
754 // Update the scrollbar visibility
755 if (m_scrollBar
->minimum() != m_scrollBar
->maximum()) {
762 void IconView::finishedScrolling()
764 // Find the bounding rect of the items
765 QRect boundingRect
= itemsBoundingRect();
767 if (boundingRect
.isValid()) {
769 boundingRect
.adjust(-10, -10, 10, 10);
771 const QRect cr
= contentsRect().toRect();
773 // Remove any empty space above the visible area by shifting all the items
774 // and adjusting the scrollbar range.
775 int deltaY
= qBound(0, boundingRect
.top() - cr
.top(), m_scrollBar
->value());
777 for (int i
= 0; i
< m_validRows
; i
++) {
778 if (m_items
[i
].layouted
) {
779 m_items
[i
].rect
.translate(0, -deltaY
);
782 m_scrollBar
->setValue(m_scrollBar
->value() - deltaY
);
783 m_scrollBar
->setRange(0, m_scrollBar
->maximum() - deltaY
);
784 markAreaDirty(visibleArea());
785 boundingRect
.translate(0, -deltaY
);
786 m_regionCache
.clear();
789 // Remove any empty space below the visible area by adjusting the
790 // maximum value of the scrollbar.
792 int max
= qMax(m_scrollBar
->value(), boundingRect
.height() - cr
.height());
793 if (m_scrollBar
->maximum() > max
) {
794 m_scrollBar
->setRange(0, max
);
798 m_scrollBar
->setRange(0, 0);
801 // Update the scrollbar visibility
802 if (m_scrollBar
->minimum() != m_scrollBar
->maximum()) {
809 void IconView::paintErrorMessage(QPainter
*painter
, const QRect
&rect
, const QString
&message
) const
811 QIcon icon
= KIconLoader::global()->loadIcon("dialog-error", KIconLoader::NoGroup
, KIconLoader::SizeHuge
,
812 KIconLoader::DefaultState
, QStringList(), 0, true);
813 const QSize iconSize
= icon
.isNull() ? QSize() :
814 icon
.actualSize(QSize(KIconLoader::SizeHuge
, KIconLoader::SizeHuge
));
815 const int flags
= Qt::AlignCenter
| Qt::TextWordWrap
;
816 const int blur
= qCeil(m_delegate
->shadowBlur());
818 QFontMetrics fm
= painter
->fontMetrics();
819 QRect r
= fm
.boundingRect(rect
.adjusted(0, 0, -iconSize
.width() - 4, 0), flags
, message
);
820 QPixmap
pm(r
.size());
821 pm
.fill(Qt::transparent
);
823 p
.setFont(painter
->font());
824 p
.setPen(palette().color(QPalette::Text
));
825 p
.drawText(pm
.rect(), flags
, message
);
829 if (m_delegate
->shadowColor().alpha() > 0) {
830 shadow
= QImage(pm
.size() + QSize(blur
* 2, blur
* 2), QImage::Format_ARGB32_Premultiplied
);
832 p
.setCompositionMode(QPainter::CompositionMode_Source
);
833 p
.fillRect(shadow
.rect(), Qt::transparent
);
834 p
.drawPixmap(blur
, blur
, pm
);
837 Plasma::PaintUtils::shadowBlur(shadow
, blur
, m_delegate
->shadowColor());
840 const QSize
size(pm
.width() + iconSize
.width() + 4, qMax(iconSize
.height(), pm
.height()));
841 const QPoint iconPos
= rect
.topLeft() + QPoint((rect
.width() - size
.width()) / 2,
842 (rect
.height() - size
.height()) / 2);
843 const QPoint textPos
= iconPos
+ QPoint(iconSize
.width() + 4, (iconSize
.height() - pm
.height()) / 2);
845 if (!icon
.isNull()) {
846 icon
.paint(painter
, QRect(iconPos
, iconSize
));
849 if (!shadow
.isNull()) {
850 painter
->drawImage(textPos
- QPoint(blur
, blur
) + m_delegate
->shadowOffset().toPoint(), shadow
);
852 painter
->drawPixmap(textPos
, pm
);
855 void IconView::paint(QPainter
*painter
, const QStyleOptionGraphicsItem
*option
, QWidget
*widget
)
859 int offset
= m_scrollBar
->value();
860 const QRect cr
= contentsRect().toRect();
865 QRect clipRect
= cr
& option
->exposedRect
.toAlignedRect();
866 if (clipRect
.isEmpty()) {
872 painter
->setClipRect(clipRect
);
874 // Update the dirty region in the backbuffer
875 // =========================================
876 if (!m_dirtyRegion
.isEmpty()) {
877 QStyleOptionViewItemV4 opt
= viewOptions();
879 QPainter
p(&m_pixmap
);
880 p
.translate(-cr
.topLeft() - QPoint(0, offset
));
881 p
.setClipRegion(m_dirtyRegion
);
883 // Clear the dirty region
884 p
.setCompositionMode(QPainter::CompositionMode_Source
);
885 p
.fillRect(mapToViewport(cr
).toAlignedRect(), Qt::transparent
);
886 p
.setCompositionMode(QPainter::CompositionMode_SourceOver
);
888 for (int i
= 0; i
< m_validRows
; i
++) {
889 opt
.rect
= m_items
[i
].rect
;
891 if (!m_items
[i
].layouted
|| !m_dirtyRegion
.intersects(opt
.rect
)) {
895 const QModelIndex index
= m_model
->index(i
, 0);
896 opt
.state
&= ~(QStyle::State_HasFocus
| QStyle::State_MouseOver
| QStyle::State_Selected
);
898 if (index
== m_hoveredIndex
) {
899 opt
.state
|= QStyle::State_MouseOver
;
902 if (m_selectionModel
->isSelected(index
)) {
903 if (m_dragInProgress
) {
906 updateTextShadows(palette().color(QPalette::HighlightedText
));
907 opt
.state
|= QStyle::State_Selected
;
909 updateTextShadows(palette().color(QPalette::Text
));
912 if (hasFocus() && index
== m_selectionModel
->currentIndex()) {
913 opt
.state
|= QStyle::State_HasFocus
;
916 if (m_items
[i
].needSizeAdjust
) {
917 const QSize size
= m_delegate
->sizeHint(opt
, index
);
918 m_items
[i
].rect
.setHeight(size
.height());
919 m_items
[i
].needSizeAdjust
= false;
920 opt
.rect
= m_items
[i
].rect
;
923 m_delegate
->paint(&p
, opt
, index
);
926 if (m_rubberBand
.isValid())
928 QStyleOptionRubberBand opt
;
929 initStyleOption(&opt
);
930 opt
.rect
= m_rubberBand
;
931 opt
.shape
= QRubberBand::Rectangle
;
934 style()->drawControl(QStyle::CE_RubberBand
, &opt
, &p
);
937 m_dirtyRegion
= QRegion();
940 syncBackBuffer(painter
, clipRect
);
942 if (!m_errorMessage
.isEmpty()) {
943 paintErrorMessage(painter
, cr
, m_errorMessage
);
947 void IconView::updateTextShadows(const QColor
&textColor
)
949 if (!m_drawShadows
) {
950 m_delegate
->setShadowColor(Qt::transparent
);
956 // Use black shadows with bright text, and white shadows with dark text.
957 if (qGray(textColor
.rgb()) > 192) {
958 shadowColor
= Qt::black
;
960 shadowColor
= Qt::white
;
963 if (m_delegate
->shadowColor() != shadowColor
) {
964 m_delegate
->setShadowColor(shadowColor
);
966 // Center white shadows to create a halo effect, and offset dark shadows slightly.
967 if (shadowColor
== Qt::white
) {
968 m_delegate
->setShadowOffset(QPoint(0, 0));
970 m_delegate
->setShadowOffset(QPoint(layoutDirection() == Qt::RightToLeft
? -1 : 1, 1));
975 bool IconView::indexIntersectsRect(const QModelIndex
&index
, const QRect
&rect
) const
977 if (!index
.isValid() || index
.row() >= m_items
.count()) {
981 QRect r
= m_items
[index
.row()].rect
;
982 if (!r
.intersects(rect
)) {
986 // If the item is fully contained in the rect
987 if (r
.left() > rect
.left() && r
.right() < rect
.right() &&
988 r
.top() > rect
.top() && r
.bottom() < rect
.bottom()) {
992 // If the item is partially inside the rect
993 return visualRegion(index
).intersects(rect
);
996 QModelIndex
IconView::indexAt(const QPointF
&point
) const
998 if (!mapToViewport(contentsRect()).contains(point
))
999 return QModelIndex();
1001 const QPoint pt
= point
.toPoint();
1003 // If we have a hovered index, check it before walking the list
1004 if (m_hoveredIndex
.isValid()) {
1005 if (m_items
[m_hoveredIndex
.row()].rect
.contains(pt
) &&
1006 visualRegion(m_hoveredIndex
).contains(pt
))
1008 return m_hoveredIndex
;
1012 for (int i
= 0; i
< m_validRows
; i
++) {
1013 if (!m_items
[i
].layouted
|| !m_items
[i
].rect
.contains(pt
)) {
1017 const QModelIndex index
= m_model
->index(i
, 0);
1018 if (visualRegion(index
).contains(pt
)) {
1024 return QModelIndex();
1027 QRect
IconView::visualRect(const QModelIndex
&index
) const
1029 if (!index
.isValid() || index
.row() < 0 || index
.row() >= m_validRows
||
1030 !m_items
[index
.row()].layouted
) {
1034 return m_items
[index
.row()].rect
;
1037 QRegion
IconView::visualRegion(const QModelIndex
&index
) const
1039 QStyleOptionViewItemV4 option
= viewOptions();
1040 option
.rect
= m_items
[index
.row()].rect
;
1041 if (m_selectionModel
->isSelected(index
)) {
1042 option
.state
|= QStyle::State_Selected
;
1044 if (index
== m_hoveredIndex
) {
1045 option
.state
|= QStyle::State_MouseOver
;
1048 quint64 key
= quint64(option
.state
) << 32 | index
.row();
1049 if (QRegion
*region
= m_regionCache
.object(key
)) {
1056 // Make this a virtual function in KDE 5
1057 QMetaObject::invokeMethod(m_delegate
, "shape", Q_RETURN_ARG(QRegion
, region
),
1058 Q_ARG(QStyleOptionViewItem
, option
),
1059 Q_ARG(QModelIndex
, index
));
1061 m_regionCache
.insert(key
, new QRegion(region
));
1066 void IconView::updateScrollBarGeometry()
1068 QRectF cr
= contentsRect();
1070 QRectF r
= QRectF(cr
.right() - m_scrollBar
->geometry().width(), cr
.top(),
1071 m_scrollBar
->geometry().width(), cr
.height());
1072 if (m_scrollBar
->geometry() != r
) {
1073 m_scrollBar
->setGeometry(r
);
1077 void IconView::renameSelectedIcon()
1079 QModelIndex index
= m_selectionModel
->currentIndex();
1080 if (!index
.isValid())
1083 // Don't allow renaming of files the aren't visible in the view
1084 const QRect rect
= visualRect(index
);
1085 if (!mapToViewport(contentsRect()).contains(rect
)) {
1089 QStyleOptionViewItemV4 option
= viewOptions();
1092 QWidget
*editor
= m_delegate
->createEditor(0, option
, index
);
1093 editor
->setAttribute(Qt::WA_NoSystemBackground
);
1094 editor
->installEventFilter(m_delegate
);
1096 QGraphicsProxyWidget
*proxy
= new QGraphicsProxyWidget(this);
1097 proxy
->setWidget(editor
);
1099 m_delegate
->updateEditorGeometry(editor
, option
, index
);
1100 m_delegate
->setEditorData(editor
, index
);
1105 m_editorIndex
= index
;
1108 bool IconView::renameInProgress() const
1110 return m_editorIndex
.isValid();
1113 void IconView::commitData(QWidget
*editor
)
1115 m_delegate
->setModelData(editor
, m_model
, m_editorIndex
);
1118 void IconView::closeEditor(QWidget
*editor
, QAbstractItemDelegate::EndEditHint hint
)
1122 editor
->removeEventFilter(m_delegate
);
1123 if (editor
->hasFocus()) {
1128 editor
->deleteLater();
1129 m_editorIndex
= QModelIndex();
1131 markAreaDirty(visibleArea());
1134 void IconView::resizeEvent(QGraphicsSceneResizeEvent
*)
1136 updateScrollBarGeometry();
1138 int maxWidth
= contentsRect().width() - m_scrollBar
->geometry().width();
1139 int maxHeight
= contentsRect().height();
1141 if (m_validRows
> 0)
1143 if ((m_flow
== QListView::LeftToRight
&& columnsForWidth(maxWidth
) != m_columns
) ||
1144 (m_flow
== QListView::TopToBottom
&& rowsForHeight(maxHeight
) != m_rows
))
1146 // The scrollbar range will be updated after the re-layout
1147 if (m_validRows
> 0) {
1148 m_delayedRelayoutTimer
.start(500, this);
1150 m_delayedLayoutTimer
.start(10, this);
1155 markAreaDirty(visibleArea());
1160 void IconView::focusInEvent(QFocusEvent
*event
)
1163 markAreaDirty(visibleArea());
1166 void IconView::focusOutEvent(QFocusEvent
*event
)
1169 markAreaDirty(visibleArea());
1172 void IconView::hoverEnterEvent(QGraphicsSceneHoverEvent
*event
)
1174 const QModelIndex index
= indexAt(mapToViewport(event
->pos()));
1175 if (index
.isValid()) {
1176 m_hoveredIndex
= index
;
1177 markAreaDirty(visualRect(index
));
1181 void IconView::hoverLeaveEvent(QGraphicsSceneHoverEvent
*event
)
1185 if (m_hoveredIndex
.isValid()) {
1186 markAreaDirty(visualRect(m_hoveredIndex
));
1187 m_hoveredIndex
= QModelIndex();
1191 void IconView::hoverMoveEvent(QGraphicsSceneHoverEvent
*event
)
1193 const QModelIndex index
= indexAt(mapToViewport(event
->pos()));
1194 if (index
!= m_hoveredIndex
) {
1195 markAreaDirty(visualRect(index
));
1196 markAreaDirty(visualRect(m_hoveredIndex
));
1197 m_hoveredIndex
= index
;
1201 void IconView::mousePressEvent(QGraphicsSceneMouseEvent
*event
)
1203 if (!contentsRect().contains(event
->pos()) || !m_errorMessage
.isEmpty()) {
1208 const QPointF pos
= mapToViewport(event
->pos());
1209 setFocus(Qt::MouseFocusReason
);
1211 if (event
->button() == Qt::RightButton
) {
1212 const QModelIndex index
= indexAt(pos
);
1213 if (index
.isValid()) {
1214 if (!m_selectionModel
->isSelected(index
)) {
1215 m_selectionModel
->select(index
, QItemSelectionModel::ClearAndSelect
);
1216 m_selectionModel
->setCurrentIndex(index
, QItemSelectionModel::NoUpdate
);
1217 markAreaDirty(visibleArea());
1219 event
->ignore(); // Causes contextMenuEvent() to get called
1220 } else if (m_selectionModel
->hasSelection()) {
1221 m_selectionModel
->clearSelection();
1222 markAreaDirty(visibleArea());
1227 if (event
->button() == Qt::LeftButton
) {
1228 const QModelIndex index
= indexAt(pos
);
1230 // If an icon was pressed
1231 if (index
.isValid())
1233 if (event
->modifiers() & Qt::ControlModifier
) {
1234 m_selectionModel
->select(index
, QItemSelectionModel::Toggle
);
1235 m_selectionModel
->setCurrentIndex(index
, QItemSelectionModel::NoUpdate
);
1236 markAreaDirty(visualRect(index
));
1237 } else if (!m_selectionModel
->isSelected(index
)) {
1238 m_selectionModel
->select(index
, QItemSelectionModel::ClearAndSelect
);
1239 m_selectionModel
->setCurrentIndex(index
, QItemSelectionModel::NoUpdate
);
1240 markAreaDirty(visibleArea());
1242 m_pressedIndex
= index
;
1243 m_buttonDownPos
= pos
;
1247 // If empty space was pressed
1248 m_pressedIndex
= QModelIndex();
1249 m_buttonDownPos
= pos
;
1251 if (event
->modifiers() & Qt::ControlModifier
) {
1252 // Make the current selection persistent
1253 m_selectionModel
->select(m_selectionModel
->selection(), QItemSelectionModel::Select
);
1254 } else if (static_cast<Plasma::Containment
*>(parentWidget())->isContainment() &&
1255 event
->widget()->window()->inherits("DashboardView")) {
1256 // Let the event propagate to the parent widget, which will emit releaseVisualFocus().
1257 // We prefer hiding the Dashboard to allowing rubber band selections in the containment
1258 // when the icon view is being shown on the Dashboard.
1263 if (m_selectionModel
->hasSelection()) {
1264 if (!(event
->modifiers() & (Qt::ShiftModifier
| Qt::ControlModifier
))) {
1265 m_selectionModel
->clearSelection();
1266 markAreaDirty(visibleArea());
1272 void IconView::mouseReleaseEvent(QGraphicsSceneMouseEvent
*event
)
1274 if (event
->button() == Qt::LeftButton
)
1276 if (m_rubberBand
.isValid()) {
1277 markAreaDirty(m_rubberBand
);
1278 m_rubberBand
= QRect();
1282 const QPointF pos
= mapToViewport(event
->pos());
1283 const QModelIndex index
= indexAt(pos
);
1285 if (index
.isValid() && index
== m_pressedIndex
&& !(event
->modifiers() & Qt::ControlModifier
)) {
1286 if (!m_doubleClick
&& KGlobalSettings::singleClick()) {
1287 emit
activated(index
);
1288 m_selectionModel
->clearSelection();
1289 markAreaDirty(visibleArea());
1291 // We don't clear and update the selection and current index in
1292 // mousePressEvent() if the item is already selected when it's pressed,
1293 // so we need to do that here.
1294 if (m_selectionModel
->currentIndex() != index
||
1295 m_selectionModel
->selectedIndexes().count() > 1) {
1296 m_selectionModel
->select(index
, QItemSelectionModel::ClearAndSelect
);
1297 m_selectionModel
->setCurrentIndex(index
, QItemSelectionModel::NoUpdate
);
1298 markAreaDirty(visibleArea());
1300 m_doubleClick
= false;
1305 m_doubleClick
= false;
1306 m_pressedIndex
= QModelIndex();
1309 void IconView::mouseDoubleClickEvent(QGraphicsSceneMouseEvent
*event
)
1311 if (event
->button() != Qt::LeftButton
) {
1315 // So we don't activate the item again on the release event
1316 m_doubleClick
= true;
1318 // We don't want to invoke the default implementation in this case, since it
1319 // calls mousePressEvent().
1320 if (KGlobalSettings::singleClick()) {
1324 const QModelIndex index
= indexAt(mapToViewport(event
->pos()));
1325 if (!index
.isValid()) {
1329 // Activate the item
1330 emit
activated(index
);
1332 m_selectionModel
->clearSelection();
1333 markAreaDirty(visibleArea());
1336 void IconView::mouseMoveEvent(QGraphicsSceneMouseEvent
*event
)
1338 if (!(event
->buttons() & Qt::LeftButton
)) {
1342 // If an item is pressed
1343 if (m_pressedIndex
.isValid()) {
1344 const QPointF point
= event
->pos() - event
->buttonDownPos(Qt::LeftButton
);
1345 if (point
.toPoint().manhattanLength() >= QApplication::startDragDistance()) {
1346 startDrag(m_buttonDownPos
, event
->widget());
1351 const int scrollBarOffset
= m_scrollBar
->isVisible() ? m_scrollBar
->geometry().width() : 0;
1352 const QRect viewportRect
= visibleArea().adjusted(0, 0, int(-scrollBarOffset
), 0);
1353 const QPointF pos
= mapToViewport(event
->pos());
1354 const QRectF rubberBand
= QRectF(m_buttonDownPos
, pos
).normalized();
1355 const QRect r
= QRectF(rubberBand
& viewportRect
).toAlignedRect();
1357 if (r
!= m_rubberBand
) {
1358 const QPoint pt
= pos
.toPoint();
1359 QRectF dirtyRect
= m_rubberBand
| r
;
1362 dirtyRect
|= visualRect(m_hoveredIndex
);
1363 m_hoveredIndex
= QModelIndex();
1365 foreach (const QModelIndex
&index
, m_selectionModel
->selectedIndexes()) {
1366 dirtyRect
|= visualRect(index
);
1369 // Select the indexes inside the rubber band
1370 QItemSelection selection
;
1371 for (int i
= 0; i
< m_items
.size(); i
++) {
1372 QModelIndex index
= m_model
->index(i
, 0);
1373 if (!indexIntersectsRect(index
, m_rubberBand
))
1379 dirtyRect
|= m_items
[i
].rect
;
1380 if (m_items
[i
].rect
.contains(pt
) && visualRegion(index
).contains(pt
)) {
1381 m_hoveredIndex
= index
;
1383 index
= m_model
->index(++i
, 0);
1384 } while (i
< m_items
.size() && indexIntersectsRect(index
, m_rubberBand
));
1386 selection
.select(m_model
->index(start
, 0), m_model
->index(i
- 1, 0));
1388 m_selectionModel
->select(selection
, QItemSelectionModel::ToggleCurrent
);
1390 // Update the current index
1391 if (m_hoveredIndex
.isValid()) {
1392 if (m_hoveredIndex
!= m_selectionModel
->currentIndex()) {
1393 dirtyRect
|= visualRect(m_selectionModel
->currentIndex());
1395 m_selectionModel
->setCurrentIndex(m_hoveredIndex
, QItemSelectionModel::NoUpdate
);
1397 markAreaDirty(dirtyRect
);
1401 void IconView::wheelEvent(QGraphicsSceneWheelEvent
*event
)
1403 if ((event
->modifiers() & Qt::CTRL
) || (event
->orientation() == Qt::Horizontal
)) {
1404 // Let the event propagate to the parent widget
1409 int pixels
= 64 * event
->delta() / 120;
1410 smoothScroll(0, -pixels
);
1413 void IconView::contextMenuEvent(QGraphicsSceneContextMenuEvent
*event
)
1415 const QPointF pos
= mapToViewport(event
->pos());
1416 const QModelIndex index
= indexAt(pos
);
1418 if (index
.isValid()) {
1419 emit
contextMenuRequest(event
->widget(), event
->screenPos());
1421 // Let the event propagate to the parent widget
1426 void IconView::dragEnterEvent(QGraphicsSceneDragDropEvent
*event
)
1428 event
->setAccepted(KUrl::List::canDecode(event
->mimeData()));
1431 void IconView::dragMoveEvent(QGraphicsSceneDragDropEvent
*event
)
1433 const QModelIndex index
= indexAt(mapToViewport(event
->pos()));
1434 if (index
== m_hoveredIndex
) {
1438 const QString appletMimeType
= static_cast<Plasma::Corona
*>(scene())->appletMimeType();
1439 QRectF dirtyRect
= visualRect(m_hoveredIndex
);
1440 m_hoveredIndex
= QModelIndex();
1442 if (index
.isValid() && (m_model
->flags(index
) & Qt::ItemIsDropEnabled
) &&
1443 !event
->mimeData()->hasFormat(appletMimeType
))
1445 dirtyRect
|= visualRect(index
);
1446 bool onOurself
= false;
1448 foreach (const QModelIndex
&selected
, m_selectionModel
->selectedIndexes()) {
1449 if (selected
== index
) {
1456 m_hoveredIndex
= index
;
1457 dirtyRect
|= visualRect(index
);
1461 markAreaDirty(dirtyRect
);
1462 event
->setAccepted(!event
->mimeData()->hasFormat(appletMimeType
));
1465 void IconView::dropEvent(QGraphicsSceneDragDropEvent
*event
)
1467 // If the dropped item is an applet, let the parent widget handle it
1468 const QString appletMimeType
= static_cast<Plasma::Corona
*>(scene())->appletMimeType();
1469 if (event
->mimeData()->hasFormat(appletMimeType
)) {
1476 // Check if the drop event originated from this applet.
1477 // Normally we'd do this by checking if the source widget matches the target widget
1478 // in the drag and drop operation, but since two QGraphicsItems can be part of the
1479 // same widget, we can't use that method here.
1481 if ((!m_dragInProgress
&& !m_hoveredIndex
.isValid()) ||
1482 ((!m_dragInProgress
|| m_hoveredIndex
.isValid()) &&
1483 m_model
->flags(m_hoveredIndex
) & Qt::ItemIsDropEnabled
))
1485 item
= m_model
->itemForIndex(m_hoveredIndex
);
1488 if (!item
.isNull()) {
1489 QDropEvent
ev(event
->screenPos(), event
->dropAction(), event
->mimeData(),
1490 event
->buttons(), event
->modifiers());
1491 //kDebug() << "dropping to" << m_url << "with" << view() << event->modifiers();
1492 KonqOperations::doDrop(item
, m_dirModel
->dirLister()->url(), &ev
, event
->widget());
1493 //kDebug() << "all done!";
1497 // If we get to this point, the drag was started from within the applet,
1498 // so instead of moving/copying/linking the dropped URL's to the folder,
1499 // we'll move the items in the view.
1500 QPoint delta
= (mapToViewport(event
->pos()) - m_buttonDownPos
).toPoint();
1501 if (delta
.isNull() || m_iconsLocked
) {
1505 // If this option is set, we'll assume the dragged icons were aligned
1506 // to the grid before the drag started, and just adjust the delta we use
1507 // to move all of them.
1508 if (m_alignToGrid
) {
1509 const QSize size
= gridSize() + QSize(10, 10);
1510 if ((qAbs(delta
.x()) < size
.width() / 2) && (qAbs(delta
.y()) < size
.height() / 2)) {
1514 delta
.rx() = qRound(delta
.x() / qreal(size
.width())) * size
.width();
1515 delta
.ry() = qRound(delta
.y() / qreal(size
.height())) * size
.height();
1518 QModelIndexList indexes
;
1520 foreach (const KUrl
&url
, KUrl::List::fromMimeData(event
->mimeData())) {
1521 const QModelIndex index
= m_model
->indexForUrl(url
);
1522 if (index
.isValid()) {
1523 boundingRect
|= m_items
[index
.row()].rect
;
1524 indexes
.append(index
);
1528 const QRect cr
= contentsRect().toRect();
1529 boundingRect
.adjust(-10, -10, 10, 10);
1530 boundingRect
.translate(delta
);
1532 // Don't allow the user to move icons outside the scrollable area of the view.
1533 // Note: This code will need to be changed if support for a horizontal scrollbar is added.
1534 if (m_flow
== QListView::LeftToRight
|| m_flow
== QListView::TopToBottom
) {
1535 if (boundingRect
.left() < cr
.left()) {
1536 delta
.rx() += cr
.left() - boundingRect
.left();
1538 else if (boundingRect
.right() > cr
.right()) {
1539 delta
.rx() -= boundingRect
.right() - cr
.right();
1542 if (m_flow
== QListView::TopToBottom
) {
1543 if (boundingRect
.top() < cr
.top()) {
1544 delta
.ry() += cr
.top() - boundingRect
.top();
1546 else if (boundingRect
.bottom() > cr
.bottom()) {
1547 delta
.ry() -= boundingRect
.bottom() - cr
.bottom();
1552 foreach (const QModelIndex
&index
, indexes
) {
1553 m_items
[index
.row()].rect
.translate(delta
);
1556 // Make sure no icons have negative coordinates etc.
1557 doLayoutSanityCheck();
1558 markAreaDirty(visibleArea());
1559 m_regionCache
.clear();
1561 m_layoutBroken
= true;
1562 emit
indexesMoved(indexes
);
1565 void IconView::changeEvent(QEvent
*event
)
1567 QGraphicsWidget::changeEvent(event
);
1569 switch (event
->type())
1571 case QEvent::ContentsRectChange
:
1573 qreal left
, top
, right
, bottom
;
1574 getContentsMargins(&left
, &top
, &right
, &bottom
);
1576 if (m_validRows
== 0) {
1577 m_margins
[Plasma::LeftMargin
] = left
;
1578 m_margins
[Plasma::TopMargin
] = top
;
1579 m_margins
[Plasma::RightMargin
] = right
;
1580 m_margins
[Plasma::BottomMargin
] = bottom
;
1584 // Check if the margins have changed, but the contents rect still has the same size.
1585 // This mainly happens when the applet is used as a containment, and the user moves
1586 // a panel to the opposite edge, or when the user enables/disables panel autohide.
1587 bool widthChanged
= int(m_margins
[Plasma::LeftMargin
] + m_margins
[Plasma::RightMargin
]) != int(left
+ right
);
1588 bool heightChanged
= int(m_margins
[Plasma::TopMargin
] + m_margins
[Plasma::BottomMargin
]) != int(top
+ bottom
);
1589 bool needRelayout
= false;
1591 if ((m_flow
== QListView::LeftToRight
&& widthChanged
) ||
1592 (m_flow
== QListView::TopToBottom
&& heightChanged
))
1594 needRelayout
= true;
1597 // Don't throw the layout away if all items will fit in the new contents rect
1599 const QRect cr
= contentsRect().toRect();
1600 QRect boundingRect
= itemsBoundingRect();
1601 boundingRect
.adjust(-10, -10, 10, 10);
1602 if (boundingRect
.width() < cr
.width() && boundingRect
.height() < cr
.height()) {
1603 needRelayout
= false;
1610 m_delayedLayoutTimer
.start(10, this);
1614 delta
.rx() = int(left
- m_margins
[Plasma::LeftMargin
]);
1615 delta
.ry() = int(top
- m_margins
[Plasma::TopMargin
]);
1617 if (!delta
.isNull()) {
1618 for (int i
= 0; i
< m_validRows
; i
++) {
1619 if (m_items
[i
].layouted
) {
1620 m_items
[i
].rect
.translate(delta
);
1623 markAreaDirty(visibleArea());
1628 m_margins
[Plasma::LeftMargin
] = left
;
1629 m_margins
[Plasma::TopMargin
] = top
;
1630 m_margins
[Plasma::RightMargin
] = right
;
1631 m_margins
[Plasma::BottomMargin
] = bottom
;
1635 case QEvent::FontChange
:
1636 case QEvent::PaletteChange
:
1637 case QEvent::StyleChange
:
1638 markAreaDirty(visibleArea());
1647 // pos is the position where the mouse was clicked in the applet.
1648 // widget is the widget that sent the mouse event that triggered the drag.
1649 void IconView::startDrag(const QPointF
&pos
, QWidget
*widget
)
1651 QModelIndexList indexes
= m_selectionModel
->selectedIndexes();
1653 foreach (const QModelIndex
&index
, indexes
) {
1654 boundingRect
|= visualRect(index
);
1657 QPixmap
pixmap(boundingRect
.size());
1658 pixmap
.fill(Qt::transparent
);
1660 QStyleOptionViewItemV4 option
= viewOptions();
1661 // ### We can't draw the items as selected or hovered since Qt doesn't
1662 // use an ARGB window for the drag pixmap.
1663 //option.state |= QStyle::State_Selected;
1664 option
.state
&= ~(QStyle::State_Selected
| QStyle::State_MouseOver
);
1666 updateTextShadows(palette().color(QPalette::HighlightedText
));
1668 QPainter
p(&pixmap
);
1669 foreach (const QModelIndex
&index
, indexes
)
1671 option
.rect
= visualRect(index
).translated(-boundingRect
.topLeft());
1673 // ### Reenable this code when Qt uses an ARGB window for the drag pixmap
1674 if (index
== m_hoveredIndex
)
1675 option
.state
|= QStyle::State_MouseOver
;
1677 option
.state
&= ~QStyle::State_MouseOver
;
1679 m_delegate
->paint(&p
, option
, index
);
1683 // Mark the area containing the about-to-be-dragged items as dirty, so they
1684 // will be erased from the view on the next repaint. We have to do this
1685 // before calling QDrag::exec(), since it's a blocking call.
1686 markAreaDirty(boundingRect
);
1688 // Unset the hovered index so dropEvent won't think the icons are being
1689 // dropped on a dragged folder.
1690 m_hoveredIndex
= QModelIndex();
1691 m_dragInProgress
= true;
1693 QDrag
*drag
= new QDrag(widget
);
1694 drag
->setMimeData(m_model
->mimeData(indexes
));
1695 drag
->setPixmap(pixmap
);
1696 drag
->setHotSpot((pos
- boundingRect
.topLeft()).toPoint());
1697 drag
->exec(m_model
->supportedDragActions());
1699 m_dragInProgress
= false;
1701 // Repaint the dragged icons in case the drag did not remove the file
1702 markAreaDirty(boundingRect
);
1705 QStyleOptionViewItemV4
IconView::viewOptions() const
1707 QStyleOptionViewItemV4 option
;
1708 initStyleOption(&option
);
1710 option
.font
= font();
1711 option
.decorationAlignment
= Qt::AlignTop
| Qt::AlignHCenter
;
1712 option
.decorationPosition
= QStyleOptionViewItem::Top
;
1713 option
.decorationSize
= iconSize();
1714 option
.displayAlignment
= Qt::AlignHCenter
;
1715 option
.textElideMode
= Qt::ElideRight
;
1716 option
.locale
= QLocale::system();
1717 option
.widget
= m_styleWidget
;
1718 option
.viewItemPosition
= QStyleOptionViewItemV4::OnlyOne
;
1721 option
.features
= QStyleOptionViewItemV2::WrapText
;
1727 void IconView::timerEvent(QTimerEvent
*event
)
1729 AbstractItemView::timerEvent(event
);
1731 if (event
->timerId() == m_delayedCacheClearTimer
.timerId()) {
1732 m_delayedCacheClearTimer
.stop();
1733 m_savedPositions
.clear();
1734 } else if (event
->timerId() == m_delayedLayoutTimer
.timerId()) {
1735 m_delayedLayoutTimer
.stop();
1738 else if (event
->timerId() == m_delayedRelayoutTimer
.timerId()) {
1740 m_delayedRelayoutTimer
.stop();
1742 // This is to give the busy animation a chance to appear.
1743 m_delayedLayoutTimer
.start(10, this);
1748 #include "iconview.moc"