add more spacing
[personal-kdebase.git] / apps / plasma / applets / folderview / abstractitemview.cpp
blob867bd61518d9277e5022ab0962dacfec32b29df4
1 /*
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"
26 #include "style.h"
28 #include <QItemSelectionModel>
29 #include <QPaintEngine>
30 #include <KDirModel>
31 #include <KFileItemDelegate>
33 #ifdef Q_WS_X11
34 # include <QX11Info>
35 # include <X11/Xlib.h>
36 #endif
39 static const int sSmoothScrollTime = 140;
40 static const int sSmoothScrollTick = 14;
43 AbstractItemView::AbstractItemView(QGraphicsWidget *parent)
44 : QGraphicsWidget(parent),
45 m_delegate(0),
46 m_lastScrollValue(0),
47 m_viewScrolled(false),
48 m_dx(0),
49 m_ddx(0),
50 m_dddx(0),
51 m_rdx(0),
52 m_dy(0),
53 m_ddy(0),
54 m_dddy(0),
55 m_rdy(0),
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()
76 delete m_styleWidget;
77 delete m_style;
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
94 return m_model;
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
118 return m_delegate;
121 void AbstractItemView::setIconSize(const QSize &iconSize)
123 m_iconSize = iconSize;
126 QSize AbstractItemView::iconSize() const
128 return m_iconSize;
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();
162 int sy, dy, h;
163 QRect dirty;
164 if (delta < 0) {
165 dy = 0;
166 sy = -delta;
167 h = m_pixmap.height() - sy;
168 dirty = QRect(0, m_pixmap.height() - sy, m_pixmap.width(), sy);
169 } else {
170 dy = delta;
171 sy = 0;
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);
182 XFreeGC(dpy, gc);
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());
189 } else
190 #endif
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
208 #ifdef Q_WS_X11
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);
214 } else
215 #endif
217 QPainter p(&pixmap);
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;
225 } else {
226 m_dirtyRegion = QRegion(visibleArea());
228 m_pixmap = pixmap;
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;
255 QPainter p(&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);
271 p.end();
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);
289 p.end();
291 p.drawTiledPixmap(0, m_pixmap.height() - fadeHeight, m_pixmap.width(), fadeHeight, m_bottomFadeTile);
293 p.end();
295 painter->drawPixmap(cr.topLeft(), pixmap);
297 else
299 painter->drawPixmap(cr.topLeft(), m_pixmap);
303 void AbstractItemView::rowsInserted(const QModelIndex &parent, int first, int last)
305 Q_UNUSED(parent)
306 Q_UNUSED(first)
307 Q_UNUSED(last)
310 void AbstractItemView::rowsRemoved(const QModelIndex &parent, int first, int last)
312 Q_UNUSED(parent)
313 Q_UNUSED(first)
314 Q_UNUSED(last)
317 void AbstractItemView::modelReset()
321 void AbstractItemView::layoutChanged()
325 void AbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
327 Q_UNUSED(topLeft)
328 Q_UNUSED(bottomRight)
331 void AbstractItemView::commitData(QWidget *editor)
333 Q_UNUSED(editor)
336 void AbstractItemView::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
338 Q_UNUSED(editor)
339 Q_UNUSED(hint)
343 void AbstractItemView::scrollBarValueChanged(int value)
345 Q_UNUSED(value)
347 m_viewScrolled = true;
348 update();
351 void AbstractItemView::scrollBarActionTriggered(int action)
353 switch (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());
360 break;
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);
366 break;
370 void AbstractItemView::scrollBarSliderReleased()
372 finishedScrolling();
375 void AbstractItemView::finishedScrolling()
379 void AbstractItemView::timerEvent(QTimerEvent *event)
381 if (event->timerId() == m_smoothScrollTimer.timerId()) {
382 scrollTick();
386 void AbstractItemView::startScrolling()
388 m_smoothScrolling = true;
389 m_smoothScrollTimer.start(sSmoothScrollTick, this);
392 void AbstractItemView::stopScrolling()
394 m_smoothScrollTimer.stop();
395 m_dx = m_dy = 0;
396 m_ddx = m_ddy = 0;
397 m_rdx = m_rdy = 0;
398 m_dddx = m_dddy = 0;
399 m_smoothScrolling = false;
400 finishedScrolling();
403 void AbstractItemView::smoothScroll(int dx, int dy)
405 // full scroll is remaining scroll plus new scroll
406 m_dx = m_dx + dx;
407 m_dy = m_dy + dy;
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
431 m_ddx *= 2;
432 m_ddy *= 2;
434 // deacceleration speed
435 m_dddx = (m_ddx+1)/steps;
436 m_dddy = (m_ddy+1)/steps;
438 if (!m_smoothScrolling) {
439 startScrolling();
440 scrollTick();
444 void AbstractItemView::scrollTick() {
445 if (m_dx == 0 && m_dy == 0) {
446 stopScrolling();
447 return;
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
461 int ddx = tddx / 16;
462 int ddy = tddy / 16;
463 // remaining partial step (this is especially needed for 1.x sized steps)
464 m_rdx = tddx % 16;
465 m_rdy = tddy % 16;
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
476 m_dx -= ddx;
477 m_dy -= ddy;
479 m_scrollBar->setValue(m_scrollBar->value() + ddy);
481 // update scrolling speed
482 int dddx = m_dddx;
483 int dddy = m_dddy;
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;
488 m_ddx -= dddx;
489 m_ddy -= dddy;
492 #include "abstractitemview.moc"