2 * Copyright © 2008 Fredrik Höglund <fredrik@kde.org>
4 * The smooth scrolling code is based on the code in KHTMLView,
5 * Copyright © 2006-2008 Germain Garand <germain@ebooksfrance.org>
6 * Copyright © 2008 Allan Sandfeld Jensen <kde@carewolf.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
24 #include "abstractitemview.h"
25 #include "proxymodel.h"
28 #include <QItemSelectionModel>
29 #include <QPaintEngine>
31 #include <KFileItemDelegate>
35 # include <X11/Xlib.h>
39 static const int sSmoothScrollTime
= 140;
40 static const int sSmoothScrollTick
= 14;
43 AbstractItemView::AbstractItemView(QGraphicsWidget
*parent
)
44 : QGraphicsWidget(parent
),
47 m_viewScrolled(false),
56 m_smoothScrolling(false)
58 m_scrollBar
= new Plasma::ScrollBar(this);
59 connect(m_scrollBar
, SIGNAL(valueChanged(int)), SLOT(scrollBarValueChanged(int)));
60 connect(m_scrollBar
->nativeWidget(), SIGNAL(actionTriggered(int)), SLOT(scrollBarActionTriggered(int)));
61 connect(m_scrollBar
->nativeWidget(), SIGNAL(sliderReleased()), SLOT(scrollBarSliderReleased()));
63 // This is a dummy widget that's never shown - it's just passed to
64 // KFileItemDelegate in the style options, so it will use the widget's
65 // style to draw the view item backgrounds.
66 m_styleWidget
= new QWidget
;
67 m_style
= new FolderViewStyle
;
68 m_styleWidget
->setStyle(m_style
);
70 int size
= style()->pixelMetric(QStyle::PM_LargeIconSize
);
71 m_iconSize
= QSize(size
, size
);
74 AbstractItemView::~AbstractItemView()
80 void AbstractItemView::setModel(QAbstractItemModel
*model
)
82 m_model
= static_cast<ProxyModel
*>(model
);
83 m_dirModel
= static_cast<KDirModel
*>(m_model
->sourceModel());
85 connect(m_model
, SIGNAL(rowsInserted(QModelIndex
,int,int)), SLOT(rowsInserted(QModelIndex
,int,int)));
86 connect(m_model
, SIGNAL(rowsRemoved(QModelIndex
,int,int)), SLOT(rowsRemoved(QModelIndex
,int,int)));
87 connect(m_model
, SIGNAL(modelReset()), SLOT(modelReset()));
88 connect(m_model
, SIGNAL(layoutChanged()), SLOT(layoutChanged()));
89 connect(m_model
, SIGNAL(dataChanged(QModelIndex
,QModelIndex
)), SLOT(dataChanged(QModelIndex
,QModelIndex
)));
92 QAbstractItemModel
*AbstractItemView::model() const
97 void AbstractItemView::setSelectionModel(QItemSelectionModel
*model
)
99 m_selectionModel
= model
;
102 QItemSelectionModel
*AbstractItemView::selectionModel() const
104 return m_selectionModel
;
107 void AbstractItemView::setItemDelegate(KFileItemDelegate
*delegate
)
109 m_delegate
= static_cast<KFileItemDelegate
*>(delegate
);
111 connect(m_delegate
, SIGNAL(closeEditor(QWidget
*,QAbstractItemDelegate::EndEditHint
)),
112 SLOT(closeEditor(QWidget
*,QAbstractItemDelegate::EndEditHint
)));
113 connect(m_delegate
, SIGNAL(commitData(QWidget
*)), SLOT(commitData(QWidget
*)));
116 KFileItemDelegate
*AbstractItemView::itemDelegate() const
121 void AbstractItemView::setIconSize(const QSize
&iconSize
)
123 m_iconSize
= iconSize
;
126 QSize
AbstractItemView::iconSize() const
131 QScrollBar
*AbstractItemView::verticalScrollBar() const
133 return m_scrollBar
->nativeWidget();
136 QRect
AbstractItemView::visibleArea() const
138 return mapToViewport(contentsRect()).toAlignedRect();
141 // Marks the given rect in viewport coordinates, as dirty and schedules a repaint.
142 void AbstractItemView::markAreaDirty(const QRect
&rect
)
144 if (!rect
.isEmpty() && rect
.intersects(visibleArea())) {
145 m_dirtyRegion
+= rect
;
146 update(mapFromViewport(rect
));
150 // This function scrolls the contents of the backbuffer the distance the scrollbar
151 // has moved since the last time this function was called.
152 QRect
AbstractItemView::scrollBackBuffer()
154 int value
= m_scrollBar
->value();
155 int delta
= m_lastScrollValue
- value
;
156 m_lastScrollValue
= value
;
158 if (qAbs(delta
) >= m_pixmap
.height()) {
159 return visibleArea();
167 h
= m_pixmap
.height() - sy
;
168 dirty
= QRect(0, m_pixmap
.height() - sy
, m_pixmap
.width(), sy
);
172 h
= m_pixmap
.height() - dy
;
173 dirty
= QRect(0, 0, m_pixmap
.width(), dy
);
176 #if defined(Q_WS_X11)
177 const QPaintEngine::Type type
= m_pixmap
.paintEngine()->type();
178 if (type
== QPaintEngine::X11
) {
179 Display
*dpy
= QX11Info::display();
180 GC gc
= XCreateGC(dpy
, m_pixmap
.handle(), 0, 0);
181 XCopyArea(dpy
, m_pixmap
.handle(), m_pixmap
.handle(), gc
, 0, sy
, m_pixmap
.width(), h
, 0, dy
);
183 } else if (type
== QPaintEngine::Raster
) {
184 // Hack to prevent the image from detaching
185 const QImage image
= m_pixmap
.toImage();
186 const uchar
*src
= image
.scanLine(sy
);
187 uchar
*dst
= const_cast<uchar
*>(image
.scanLine(dy
));
188 memmove((void*)dst
, (const void*)src
, h
* image
.bytesPerLine());
192 dirty
= m_pixmap
.rect();
195 return mapToViewport(dirty
.translated(contentsRect().topLeft().toPoint())).toAlignedRect();
198 void AbstractItemView::prepareBackBuffer()
200 const QRect cr
= contentsRect().toRect();
202 // Make sure the backbuffer pixmap has the same size as the content rect
203 if (m_pixmap
.size() != cr
.size()) {
204 QPixmap
pixmap(cr
.size());
205 pixmap
.fill(Qt::transparent
);
206 if (!m_pixmap
.isNull()) {
207 // Static content optimization
209 if (m_pixmap
.paintEngine()->type() == QPaintEngine::X11
) {
210 GC gc
= XCreateGC(QX11Info::display(), pixmap
.handle(), 0, NULL
);
211 XCopyArea(QX11Info::display(), m_pixmap
.handle(), pixmap
.handle(), gc
, 0, 0,
212 m_pixmap
.width(), m_pixmap
.height(), 0, 0);
213 XFreeGC(QX11Info::display(), gc
);
218 p
.setCompositionMode(QPainter::CompositionMode_Source
);
219 p
.drawPixmap(0, 0, m_pixmap
);
221 QRegion
region(pixmap
.rect());
222 region
-= m_pixmap
.rect();
223 region
.translate(0, m_scrollBar
->value());
224 m_dirtyRegion
|= region
;
226 m_dirtyRegion
= QRegion(visibleArea());
231 if (m_viewScrolled
) {
232 m_dirtyRegion
+= scrollBackBuffer();
233 m_viewScrolled
= false;
237 // This function draws the backbuffer pixmap on the widget, and fades out the top
238 // and bottom if as needed.
239 void AbstractItemView::syncBackBuffer(QPainter
*painter
, const QRect
&clipRect
)
241 const QRect cr
= contentsRect().toRect();
243 const int fadeHeight
= 16;
244 const QRect
topFadeRect(cr
.x(), cr
.y(), cr
.width(), fadeHeight
);
245 const QRect
bottomFadeRect(cr
.bottomLeft() - QPoint(0, fadeHeight
), QSize(cr
.width(), fadeHeight
));
246 int scrollValue
= m_scrollBar
->value();
247 int maxValue
= m_scrollBar
->maximum();
249 // Draw the backbuffer on the widget
250 // =================================
251 if ((scrollValue
> 0 && topFadeRect
.intersects(clipRect
)) ||
252 (scrollValue
< maxValue
&& bottomFadeRect
.intersects(clipRect
)))
254 QPixmap pixmap
= m_pixmap
;
256 p
.setCompositionMode(QPainter::CompositionMode_DestinationIn
);
258 // Fade out the top section of the pixmap if the scrollbar slider isn't at the top
259 if (scrollValue
> 0 && topFadeRect
.intersects(clipRect
))
261 if (m_topFadeTile
.isNull())
263 m_topFadeTile
= QPixmap(256, fadeHeight
);
264 m_topFadeTile
.fill(Qt::transparent
);
265 QLinearGradient
g(0, 0, 0, fadeHeight
);
266 g
.setColorAt(0, Qt::transparent
);
267 g
.setColorAt(1, Qt::black
);
268 QPainter
p(&m_topFadeTile
);
269 p
.setCompositionMode(QPainter::CompositionMode_Source
);
270 p
.fillRect(0, 0, 256, fadeHeight
, g
);
273 p
.drawTiledPixmap(0, 0, m_pixmap
.width(), fadeHeight
, m_topFadeTile
);
276 // Fade out the bottom part of the pixmap if the scrollbar slider isn't at the bottom
277 if (scrollValue
< maxValue
&& bottomFadeRect
.intersects(clipRect
))
279 if (m_topFadeTile
.isNull())
281 m_bottomFadeTile
= QPixmap(256, fadeHeight
);
282 m_bottomFadeTile
.fill(Qt::transparent
);
283 QLinearGradient
g(0, 0, 0, fadeHeight
);
284 g
.setColorAt(0, Qt::black
);
285 g
.setColorAt(1, Qt::transparent
);
286 QPainter
p(&m_bottomFadeTile
);
287 p
.setCompositionMode(QPainter::CompositionMode_Source
);
288 p
.fillRect(0, 0, 256, fadeHeight
, g
);
291 p
.drawTiledPixmap(0, m_pixmap
.height() - fadeHeight
, m_pixmap
.width(), fadeHeight
, m_bottomFadeTile
);
295 painter
->drawPixmap(cr
.topLeft(), pixmap
);
299 painter
->drawPixmap(cr
.topLeft(), m_pixmap
);
303 void AbstractItemView::rowsInserted(const QModelIndex
&parent
, int first
, int last
)
310 void AbstractItemView::rowsRemoved(const QModelIndex
&parent
, int first
, int last
)
317 void AbstractItemView::modelReset()
321 void AbstractItemView::layoutChanged()
325 void AbstractItemView::dataChanged(const QModelIndex
&topLeft
, const QModelIndex
&bottomRight
)
328 Q_UNUSED(bottomRight
)
331 void AbstractItemView::commitData(QWidget
*editor
)
336 void AbstractItemView::closeEditor(QWidget
*editor
, QAbstractItemDelegate::EndEditHint hint
)
343 void AbstractItemView::scrollBarValueChanged(int value
)
347 m_viewScrolled
= true;
351 void AbstractItemView::scrollBarActionTriggered(int action
)
355 case QAbstractSlider::SliderSingleStepAdd
:
356 case QAbstractSlider::SliderSingleStepSub
:
357 case QAbstractSlider::SliderPageStepAdd
:
358 case QAbstractSlider::SliderPageStepSub
:
359 smoothScroll(0, m_scrollBar
->nativeWidget()->sliderPosition() - m_scrollBar
->value());
362 case QAbstractSlider::SliderToMinimum
:
363 case QAbstractSlider::SliderToMaximum
:
364 // Use a delayed call since the value won't propagate until after this function returns
365 QMetaObject::invokeMethod(this, "finishedScrolling", Qt::QueuedConnection
);
370 void AbstractItemView::scrollBarSliderReleased()
375 void AbstractItemView::finishedScrolling()
379 void AbstractItemView::timerEvent(QTimerEvent
*event
)
381 if (event
->timerId() == m_smoothScrollTimer
.timerId()) {
386 void AbstractItemView::startScrolling()
388 m_smoothScrolling
= true;
389 m_smoothScrollTimer
.start(sSmoothScrollTick
, this);
392 void AbstractItemView::stopScrolling()
394 m_smoothScrollTimer
.stop();
399 m_smoothScrolling
= false;
403 void AbstractItemView::smoothScroll(int dx
, int dy
)
405 // full scroll is remaining scroll plus new scroll
409 if (m_dx
== 0 && m_dy
== 0) return;
411 int steps
= sSmoothScrollTime
/sSmoothScrollTick
;
413 // average step size (stored in 1/16 px/step)
414 m_ddx
= (m_dx
*16)/(steps
+1);
415 m_ddy
= (m_dy
*16)/(steps
+1);
417 if (qAbs(m_ddx
) < 64 && qAbs(m_ddy
) < 64) {
418 // Don't move slower than average 4px/step in minimum one direction
419 if (m_ddx
> 0) m_ddx
= qMax(m_ddx
, 64);
420 if (m_ddy
> 0) m_ddy
= qMax(m_ddy
, 64);
421 if (m_ddx
< 0) m_ddx
= qMin(m_ddx
, -64);
422 if (m_ddy
< 0) m_ddy
= qMin(m_ddy
, -64);
423 // This means fewer than normal steps
424 steps
= qMax(m_ddx
? (m_dx
*16)/m_ddx
: 0, m_ddy
? (m_dy
*16)/m_ddy
: 0);
425 if (steps
< 1) steps
= 1;
426 m_ddx
= (m_dx
*16)/(steps
+1);
427 m_ddy
= (m_dy
*16)/(steps
+1);
430 // step size starts at double average speed and ends at 0
434 // deacceleration speed
435 m_dddx
= (m_ddx
+1)/steps
;
436 m_dddy
= (m_ddy
+1)/steps
;
438 if (!m_smoothScrolling
) {
444 void AbstractItemView::scrollTick() {
445 if (m_dx
== 0 && m_dy
== 0) {
450 // step size + remaining partial step
451 int tddx
= m_ddx
+ m_rdx
;
452 int tddy
= m_ddy
+ m_rdy
;
454 // don't go under 1px/step
455 if (tddx
> 0 && tddx
< 16) tddx
= 16;
456 if (tddy
> 0 && tddy
< 16) tddy
= 16;
457 if (tddx
< 0 && tddx
> -16) tddx
= -16;
458 if (tddy
< 0 && tddy
> -16) tddy
= -16;
460 // full pixel steps to scroll in this step
463 // remaining partial step (this is especially needed for 1.x sized steps)
467 // limit step to requested scrolling distance
468 if (qAbs(ddx
) > qAbs(m_dx
)) ddx
= m_dx
;
469 if (qAbs(ddy
) > qAbs(m_dy
)) ddy
= m_dy
;
471 // Don't stop if deaccelerated too fast
472 if (!ddx
) ddx
= m_dx
;
473 if (!ddy
) ddy
= m_dy
;
475 // update remaining scroll
479 m_scrollBar
->setValue(m_scrollBar
->value() + ddy
);
481 // update scrolling speed
484 // don't change direction
485 if (abs(dddx
) > abs(m_ddx
)) dddx
= m_ddx
;
486 if (abs(dddy
) > abs(m_ddy
)) dddy
= m_ddy
;
492 #include "abstractitemview.moc"