kde-workspace/plasma/applets/folderview/abstractitemview.cpp
Ivailo Monev efbde9e504 plasma: organize sub-directories
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2015-12-24 22:48:47 +02:00

744 lines
23 KiB
C++

/*
* Copyright © 2008, 2009 Fredrik Höglund <fredrik@kde.org>
*
* The smooth scrolling code is based on the code in KHTMLView,
* Copyright © 2006-2008 Germain Garand <germain@ebooksfrance.org>
* Copyright © 2008 Allan Sandfeld Jensen <kde@carewolf.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "abstractitemview.h"
#include "proxymodel.h"
#include "style.h"
#include <QItemSelectionModel>
#include <QPaintEngine>
#include <KDirModel>
#include <KFileItemDelegate>
#include <KGlobalSettings>
#include <Plasma/PaintUtils>
#include <limits.h>
#ifdef Q_WS_X11
# include <qx11info_x11.h>
# include <X11/Xlib.h>
#endif
static const int sSmoothScrollTime = 140;
static const int sSmoothScrollTick = 14;
AbstractItemView::AbstractItemView(QGraphicsWidget *parent)
: QGraphicsWidget(parent),
m_itemFrame(0),
m_delegate(0),
m_lastScrollValue(0),
m_viewScrolled(false),
m_dx(0),
m_ddx(0),
m_dddx(0),
m_rdx(0),
m_dy(0),
m_ddy(0),
m_dddy(0),
m_rdy(0),
m_smoothScrolling(false),
m_autoScrollSpeed(0),
m_drawShadows(true)
{
m_scrollBar = new Plasma::ScrollBar(this);
connect(m_scrollBar, SIGNAL(valueChanged(int)), SLOT(scrollBarValueChanged(int)));
connect(m_scrollBar->nativeWidget(), SIGNAL(actionTriggered(int)), SLOT(scrollBarActionTriggered(int)));
connect(m_scrollBar->nativeWidget(), SIGNAL(sliderReleased()), SLOT(scrollBarSliderReleased()));
m_itemFrame = new Plasma::FrameSvg(this);
m_itemFrame->setImagePath("widgets/viewitem");
m_itemFrame->setCacheAllRenderedFrames(true);
m_itemFrame->setElementPrefix("normal");
connect(m_itemFrame, SIGNAL(repaintNeeded()), this, SLOT(svgChanged()));
// This is a dummy widget that's never shown - it's just passed to
// KFileItemDelegate in the style options, so it will use the widget's
// style to draw the view item backgrounds.
m_styleWidget = new QWidget;
m_style = new FolderViewStyle;
m_styleWidget->setStyle(m_style);
const int size = style()->pixelMetric(QStyle::PM_LargeIconSize);
m_iconSize = QSize(size, size);
}
AbstractItemView::~AbstractItemView()
{
delete m_styleWidget;
delete m_style;
}
void AbstractItemView::setModel(QAbstractItemModel *model)
{
m_model = static_cast<ProxyModel*>(model);
m_dirModel = static_cast<KDirModel*>(m_model->sourceModel());
connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(rowsInserted(QModelIndex,int,int)));
connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(rowsRemoved(QModelIndex,int,int)));
connect(m_model, SIGNAL(modelReset()), SLOT(modelReset()));
connect(m_model, SIGNAL(layoutChanged()), SLOT(layoutChanged()));
connect(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(dataChanged(QModelIndex,QModelIndex)));
}
QAbstractItemModel *AbstractItemView::model() const
{
return m_model;
}
void AbstractItemView::setSelectionModel(QItemSelectionModel *model)
{
m_selectionModel = model;
}
QItemSelectionModel *AbstractItemView::selectionModel() const
{
return m_selectionModel;
}
void AbstractItemView::setItemDelegate(KFileItemDelegate *delegate)
{
m_delegate = static_cast<KFileItemDelegate*>(delegate);
connect(m_delegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)),
SLOT(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)));
connect(m_delegate, SIGNAL(commitData(QWidget*)), SLOT(commitData(QWidget*)));
}
KFileItemDelegate *AbstractItemView::itemDelegate() const
{
return m_delegate;
}
void AbstractItemView::setIconSize(const QSize &iconSize)
{
m_iconSize = iconSize;
}
QSize AbstractItemView::iconSize() const
{
return m_iconSize;
}
void AbstractItemView::setDrawShadows(bool on)
{
if (m_drawShadows != on) {
m_drawShadows = on;
markAreaDirty(visibleArea());
update();
}
}
bool AbstractItemView::drawShadows() const
{
return m_drawShadows;
}
QScrollBar *AbstractItemView::verticalScrollBar() const
{
return m_scrollBar->nativeWidget();
}
QRect AbstractItemView::visibleArea() const
{
return mapToViewport(contentsRect()).toAlignedRect();
}
// Marks the given rect in viewport coordinates, as dirty and schedules a repaint.
void AbstractItemView::markAreaDirty(const QRect &rect)
{
if (!rect.isEmpty() && rect.intersects(visibleArea())) {
m_dirtyRegion += rect;
update(mapFromViewport(rect));
}
}
// This function scrolls the contents of the backbuffer the distance the scrollbar
// has moved since the last time this function was called.
QRect AbstractItemView::scrollBackBuffer()
{
const int value = m_scrollBar->value();
const int delta = m_lastScrollValue - value;
m_lastScrollValue = value;
if (qAbs(delta) >= m_pixmap.height()) {
return visibleArea();
}
int sy, dy, h;
QRect dirty;
if (delta < 0) {
dy = 0;
sy = -delta;
h = m_pixmap.height() - sy;
dirty = QRect(0, m_pixmap.height() - sy, m_pixmap.width(), sy);
} else {
dy = delta;
sy = 0;
h = m_pixmap.height() - dy;
dirty = QRect(0, 0, m_pixmap.width(), dy);
}
#if defined(Q_WS_X11)
const QPaintEngine::Type type = m_pixmap.paintEngine()->type();
if (type == QPaintEngine::X11) {
Display *dpy = QX11Info::display();
GC gc = XCreateGC(dpy, m_pixmap.handle(), 0, 0);
XCopyArea(dpy, m_pixmap.handle(), m_pixmap.handle(), gc, 0, sy, m_pixmap.width(), h, 0, dy);
XFreeGC(dpy, gc);
} else if (type == QPaintEngine::Raster) {
// Hack to prevent the image from detaching
const QImage image = m_pixmap.toImage();
const uchar *src = image.scanLine(sy);
uchar *dst = const_cast<uchar*>(image.scanLine(dy));
memmove((void*)dst, (const void*)src, h * image.bytesPerLine());
} else
#endif
{
dirty = m_pixmap.rect();
}
return mapToViewport(dirty.translated(contentsRect().topLeft().toPoint())).toAlignedRect();
}
void AbstractItemView::prepareBackBuffer()
{
const QRect cr = contentsRect().toRect();
// Make sure the backbuffer pixmap has the same size as the content rect
if (m_pixmap.size() != cr.size()) {
QPixmap pixmap(cr.size());
pixmap.fill(Qt::transparent);
if (!m_pixmap.isNull()) {
// Static content optimization
#ifdef Q_WS_X11
if (m_pixmap.paintEngine()->type() == QPaintEngine::X11) {
GC gc = XCreateGC(QX11Info::display(), pixmap.handle(), 0, NULL);
XCopyArea(QX11Info::display(), m_pixmap.handle(), pixmap.handle(), gc, 0, 0,
m_pixmap.width(), m_pixmap.height(), 0, 0);
XFreeGC(QX11Info::display(), gc);
} else
#endif
{
QPainter p(&pixmap);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.drawPixmap(0, 0, m_pixmap);
}
QRegion region(pixmap.rect());
region -= m_pixmap.rect();
region.translate(0, m_scrollBar->value());
m_dirtyRegion |= region;
} else {
m_dirtyRegion = QRegion(visibleArea());
}
m_pixmap = pixmap;
}
if (m_viewScrolled) {
m_dirtyRegion += scrollBackBuffer();
m_viewScrolled = false;
}
}
// This function draws the backbuffer pixmap on the widget, and fades out the top
// and bottom if as needed.
void AbstractItemView::syncBackBuffer(QPainter *painter, const QRect &clipRect)
{
const QRect cr = contentsRect().toRect();
const int fadeHeight = 16;
const QRect topFadeRect(cr.x(), cr.y(), cr.width(), fadeHeight);
const QRect bottomFadeRect(cr.bottomLeft() - QPoint(0, fadeHeight), QSize(cr.width(), fadeHeight));
int scrollValue = m_scrollBar->value();
int maxValue = m_scrollBar->maximum();
// Draw the backbuffer on the widget
// =================================
if ((scrollValue > 0 && topFadeRect.intersects(clipRect)) ||
(scrollValue < maxValue && bottomFadeRect.intersects(clipRect)))
{
QPixmap pixmap = m_pixmap;
QPainter p(&pixmap);
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
// Fade out the top section of the pixmap if the scrollbar slider isn't at the top
if (scrollValue > 0 && topFadeRect.intersects(clipRect))
{
if (m_topFadeTile.isNull())
{
m_topFadeTile = QPixmap(256, fadeHeight);
m_topFadeTile.fill(Qt::transparent);
QLinearGradient g(0, 0, 0, fadeHeight);
g.setColorAt(0, Qt::transparent);
g.setColorAt(1, Qt::black);
QPainter p(&m_topFadeTile);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(0, 0, 256, fadeHeight, g);
p.end();
}
p.drawTiledPixmap(0, 0, m_pixmap.width(), fadeHeight, m_topFadeTile);
}
// Fade out the bottom part of the pixmap if the scrollbar slider isn't at the bottom
if (scrollValue < maxValue && bottomFadeRect.intersects(clipRect))
{
if (m_topFadeTile.isNull())
{
m_bottomFadeTile = QPixmap(256, fadeHeight);
m_bottomFadeTile.fill(Qt::transparent);
QLinearGradient g(0, 0, 0, fadeHeight);
g.setColorAt(0, Qt::black);
g.setColorAt(1, Qt::transparent);
QPainter p(&m_bottomFadeTile);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(0, 0, 256, fadeHeight, g);
p.end();
}
p.drawTiledPixmap(0, m_pixmap.height() - fadeHeight, m_pixmap.width(), fadeHeight, m_bottomFadeTile);
}
p.end();
painter->drawPixmap(cr.topLeft(), pixmap);
}
else
{
painter->drawPixmap(cr.topLeft(), m_pixmap);
}
}
QSize AbstractItemView::doTextLayout(QTextLayout &layout, const QSize &constraints, Qt::Alignment alignment,
QTextOption::WrapMode wrapMode) const
{
QTextOption to;
to.setAlignment(alignment);
to.setTextDirection(layoutDirection());
to.setWrapMode(wrapMode);
layout.setTextOption(to);
QFontMetricsF fm = QFontMetricsF(layout.font());
QTextLine line;
qreal leading = fm.leading();
qreal widthUsed = 0;
qreal height = 0;
layout.beginLayout();
while ((line = layout.createLine()).isValid()) {
// Make the last line that will fit infinitely long.
// drawTextLayout() will handle this by fading the line out
// if it won't fit inside the constraints.
if (height + 2 * fm.lineSpacing() > constraints.height()) {
line.setLineWidth(INT_MAX);
if (line.naturalTextWidth() < constraints.width()) {
line.setLineWidth(constraints.width());
widthUsed = qMax(widthUsed, line.naturalTextWidth());
} else {
widthUsed = constraints.width();
}
} else {
line.setLineWidth(constraints.width());
widthUsed = qMax(widthUsed, line.naturalTextWidth());
}
line.setPosition(QPointF(0, height));
height += line.height() + leading;
}
layout.endLayout();
return QSize(widthUsed, height);
}
void AbstractItemView::drawTextLayout(QPainter *painter, const QTextLayout &layout, const QRect &rect) const
{
// Create the alpha gradient for the fade out effect
QLinearGradient alphaGradient(0, 0, 1, 0);
alphaGradient.setCoordinateMode(QGradient::ObjectBoundingMode);
if (layout.textOption().textDirection() == Qt::LeftToRight) {
alphaGradient.setColorAt(0, QColor(0, 0, 0, 255));
alphaGradient.setColorAt(1, QColor(0, 0, 0, 0));
} else {
alphaGradient.setColorAt(0, QColor(0, 0, 0, 0));
alphaGradient.setColorAt(1, QColor(0, 0, 0, 255));
}
QFontMetrics fm(layout.font());
QList<QRect> fadeRects;
QList<QRect> haloRects;
int fadeWidth = 30;
// Compute halo and fade rects
for (int i = 0; i < layout.lineCount(); i++) {
const QTextLine line = layout.lineAt(i);
const QRectF lr = line.naturalTextRect();
// Add a fade out rect to the list if the line is too long
if (lr.width() > rect.width()) {
int x = rect.width() - fadeWidth;
int y = lr.y();
QRect r = QStyle::visualRect(layout.textOption().textDirection(), QRect(QPoint(), rect.size()),
QRect(x, y, fadeWidth, int(lr.height())));
fadeRects.append(r);
}
haloRects.append((lr & rect.translated(-rect.topLeft())).toAlignedRect());
}
// Create a pixmap for the text
QPixmap pixmap(rect.size());
pixmap.fill(Qt::transparent);
QPainter p(&pixmap);
p.setPen(painter->pen());
int y = 0;
if (layout.textOption().alignment() & Qt::AlignVCenter) {
const int h = fm.height() * layout.lineCount();
if (h < pixmap.height()) {
y = (pixmap.height() - h) / 2;
}
}
p.translate(0, y);
// Draw each line in the layout
for (int i = 0; i < layout.lineCount(); i++)
{
const QTextLine line = layout.lineAt(i);
const QRectF tr = line.naturalTextRect();
if (tr.width() > rect.width()) {
if (layoutDirection() == Qt::LeftToRight) {
line.draw(&p, QPointF(-tr.x(), 0));
} else {
line.draw(&p, QPointF(rect.width() - tr.right(), 0));
}
} else {
line.draw(&p, QPointF());
}
}
// Reduce the alpha in each fade out rect using the alpha gradient
if (!fadeRects.isEmpty()) {
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
foreach (const QRect &rect, fadeRects) {
p.fillRect(rect, alphaGradient);
}
}
p.end();
if (drawShadows()) {
const QColor color = painter->pen().color();
if (qGray(color.rgb()) < 192) {
// Draw halos
painter->translate(0, y);
foreach (const QRect &haloRect, haloRects) {
Plasma::PaintUtils::drawHalo(painter, haloRect.translated(rect.topLeft()));
}
painter->translate(0, -y);
} else {
// Draw shadow
QImage shadow = pixmap.toImage();
Plasma::PaintUtils::shadowBlur(shadow, 2, Qt::black);
// Make the shadow twice as dark
quint32 * const pixels = reinterpret_cast<quint32*>(shadow.bits());
for (int i = 0; i < shadow.width() * shadow.height(); i++) {
pixels[i] = qMin(255, qAlpha(pixels[i]) * 2) << 24;
}
painter->drawImage(rect.topLeft() + QPoint(1, 1), shadow);
}
}
// Draw the text pixmap
painter->drawPixmap(rect.topLeft(), pixmap);
}
void AbstractItemView::rowsInserted(const QModelIndex &parent, int first, int last)
{
Q_UNUSED(parent)
Q_UNUSED(first)
Q_UNUSED(last)
}
void AbstractItemView::rowsRemoved(const QModelIndex &parent, int first, int last)
{
Q_UNUSED(parent)
Q_UNUSED(first)
Q_UNUSED(last)
}
void AbstractItemView::modelReset()
{
}
void AbstractItemView::layoutChanged()
{
}
void AbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
Q_UNUSED(topLeft)
Q_UNUSED(bottomRight)
}
void AbstractItemView::commitData(QWidget *editor)
{
Q_UNUSED(editor)
}
void AbstractItemView::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
{
Q_UNUSED(editor)
Q_UNUSED(hint)
}
void AbstractItemView::svgChanged()
{
}
void AbstractItemView::scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint hint)
{
Q_UNUSED(hint)
const QRectF r = mapFromViewport(visualRect(index));
if (r.top() < 0) {
smoothScroll(0, r.top());
} else if (r.bottom() > geometry().height()) {
smoothScroll(0, r.bottom() - geometry().height());
}
}
void AbstractItemView::autoScroll(ScrollDirection direction, int pixelsPerSecond)
{
m_scrollDirection = direction;
m_autoScrollSetSpeed = direction == ScrollUp ? -pixelsPerSecond : pixelsPerSecond;
if (!m_autoScrollTimer.isActive()) {
m_autoScrollSpeed = 1;
m_autoScrollTime.restart();
m_autoScrollTimer.start(1000 / 30, this);
}
}
void AbstractItemView::stopAutoScrolling()
{
m_autoScrollSetSpeed = 0;
m_autoScrollTimer.stop();
}
void AbstractItemView::scrollBarValueChanged(int value)
{
Q_UNUSED(value)
m_viewScrolled = true;
update();
}
void AbstractItemView::scrollBarActionTriggered(int action)
{
switch (action)
{
case QAbstractSlider::SliderSingleStepAdd:
case QAbstractSlider::SliderSingleStepSub:
case QAbstractSlider::SliderPageStepAdd:
case QAbstractSlider::SliderPageStepSub:
stopAutoScrolling();
smoothScroll(0, m_scrollBar->nativeWidget()->sliderPosition() - m_scrollBar->value());
break;
case QAbstractSlider::SliderToMinimum:
case QAbstractSlider::SliderToMaximum:
stopAutoScrolling();
// Use a delayed call since the value won't propagate until after this function returns
QMetaObject::invokeMethod(this, "finishedScrolling", Qt::QueuedConnection);
break;
}
}
void AbstractItemView::scrollBarSliderReleased()
{
finishedScrolling();
}
void AbstractItemView::finishedScrolling()
{
}
void AbstractItemView::timerEvent(QTimerEvent *event)
{
if (event->timerId() == m_smoothScrollTimer.timerId()) {
scrollTick();
} else if (event->timerId() == m_autoScrollTimer.timerId()) {
int step = qRound(m_autoScrollTime.elapsed() * (m_autoScrollSpeed / 1000.));
m_autoScrollTime.restart();
if (m_scrollDirection == ScrollUp && m_scrollBar->value() > m_scrollBar->minimum()) {
m_scrollBar->setValue(qMax(m_scrollBar->minimum(), m_scrollBar->value() + step));
} else if (m_scrollDirection == ScrollDown && m_scrollBar->value() < m_scrollBar->maximum()) {
m_scrollBar->setValue(qMin(m_scrollBar->maximum(), m_scrollBar->value() + step));
} else {
m_autoScrollSetSpeed = 0;
m_autoScrollSpeed = 0;
}
if (m_autoScrollSetSpeed > m_autoScrollSpeed) {
int delta;
if (m_autoScrollSpeed >= 0) {
delta = qBound(2, m_autoScrollSpeed * 2, 30);
} else {
delta = qBound(2, qAbs(m_autoScrollSpeed) / 2, 30);
}
m_autoScrollSpeed = qMin(m_autoScrollSpeed + delta, m_autoScrollSetSpeed);
} else if (m_autoScrollSetSpeed < m_autoScrollSpeed) {
int delta;
if (m_autoScrollSpeed >= 0) {
delta = qBound(2, m_autoScrollSpeed / 2, 30);
} else {
delta = qBound(2, qAbs(m_autoScrollSpeed * 2), 30);
}
m_autoScrollSpeed = qMax(m_autoScrollSetSpeed, m_autoScrollSpeed - delta);
}
if (m_autoScrollSpeed == 0 && m_autoScrollSetSpeed == 0) {
m_autoScrollTimer.stop();
}
}
}
void AbstractItemView::startScrolling()
{
m_smoothScrolling = true;
m_smoothScrollTimer.start(sSmoothScrollTick, this);
}
void AbstractItemView::stopScrolling()
{
m_smoothScrollTimer.stop();
m_dx = m_dy = 0;
m_ddx = m_ddy = 0;
m_rdx = m_rdy = 0;
m_dddx = m_dddy = 0;
m_smoothScrolling = false;
finishedScrolling();
}
void AbstractItemView::smoothScroll(int dx, int dy)
{
// full scroll is remaining scroll plus new scroll
m_dx = m_dx + dx;
m_dy = m_dy + dy;
if (m_dx == 0 && m_dy == 0) return;
int steps = sSmoothScrollTime/sSmoothScrollTick;
// average step size (stored in 1/16 px/step)
m_ddx = (m_dx*16)/(steps+1);
m_ddy = (m_dy*16)/(steps+1);
if (qAbs(m_ddx) < 64 && qAbs(m_ddy) < 64) {
// Don't move slower than average 4px/step in minimum one direction
if (m_ddx > 0) m_ddx = qMax(m_ddx, 64);
if (m_ddy > 0) m_ddy = qMax(m_ddy, 64);
if (m_ddx < 0) m_ddx = qMin(m_ddx, -64);
if (m_ddy < 0) m_ddy = qMin(m_ddy, -64);
// This means fewer than normal steps
steps = qMax(m_ddx ? (m_dx*16)/m_ddx : 0, m_ddy ? (m_dy*16)/m_ddy : 0);
if (steps < 1) steps = 1;
m_ddx = (m_dx*16)/(steps+1);
m_ddy = (m_dy*16)/(steps+1);
}
// step size starts at double average speed and ends at 0
m_ddx *= 2;
m_ddy *= 2;
// deacceleration speed
m_dddx = (m_ddx+1)/steps;
m_dddy = (m_ddy+1)/steps;
if (!m_smoothScrolling) {
startScrolling();
scrollTick();
}
m_smoothScrollStopwatch.start();
}
void AbstractItemView::scrollTick() {
if (m_dx == 0 && m_dy == 0) {
stopScrolling();
return;
}
// step size + remaining partial step
int tddx = m_ddx + m_rdx;
int tddy = m_ddy + m_rdy;
// don't go under 1px/step
if (tddx > 0 && tddx < 16) tddx = 16;
if (tddy > 0 && tddy < 16) tddy = 16;
if (tddx < 0 && tddx > -16) tddx = -16;
if (tddy < 0 && tddy > -16) tddy = -16;
// full pixel steps to scroll in this step
int ddx = tddx / 16;
int ddy = tddy / 16;
// remaining partial step (this is especially needed for 1.x sized steps)
m_rdx = tddx % 16;
m_rdy = tddy % 16;
// limit step to requested scrolling distance
if (qAbs(ddx) > qAbs(m_dx)) ddx = m_dx;
if (qAbs(ddy) > qAbs(m_dy)) ddy = m_dy;
// Don't stop if deaccelerated too fast
if (!ddx) ddx = m_dx;
if (!ddy) ddy = m_dy;
// update remaining scroll
m_dx -= ddx;
m_dy -= ddy;
m_scrollBar->setValue(m_scrollBar->value() + ddy);
// only consider decelerating if we aren't too far behind schedule
if (m_smoothScrollStopwatch.elapsed() < 2 * sSmoothScrollTick) {
// update scrolling speed
int dddx = m_dddx;
int dddy = m_dddy;
// don't change direction
if (abs(dddx) > abs(m_ddx)) dddx = m_ddx;
if (abs(dddy) > abs(m_ddy)) dddy = m_ddy;
m_ddx -= dddx;
m_ddy -= dddy;
}
m_smoothScrollStopwatch.start();
}
#include "moc_abstractitemview.cpp"