]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Provide basic rubberband functionality
authorPeter Penz <peter.penz19@gmail.com>
Thu, 18 Aug 2011 21:11:16 +0000 (23:11 +0200)
committerPeter Penz <peter.penz19@gmail.com>
Thu, 18 Aug 2011 21:14:30 +0000 (23:14 +0200)
This is just a rough draft: The rubberband gets visible and an
automatic scrolling is done if the autoscroll-margins have been
reached. However currently no items get selected yet. Currently
the autoscrolling has a severe bug if the scrollbars are manually
changed before or after a rubberband selection.

src/CMakeLists.txt
src/kitemviews/kitemlistcontainer.cpp
src/kitemviews/kitemlistcontainer.h
src/kitemviews/kitemlistcontroller.cpp
src/kitemviews/kitemlistcontroller.h
src/kitemviews/kitemlistrubberband.cpp [new file with mode: 0644]
src/kitemviews/kitemlistrubberband_p.h [new file with mode: 0644]
src/kitemviews/kitemlistview.cpp
src/kitemviews/kitemlistview.h

index 31f5fa49046e4068f00998462f387a606c4953cf..31d3f8928a49a68c27c896dd3eaee87bd93a8ed9 100644 (file)
@@ -25,6 +25,7 @@ set(dolphinprivate_LIB_SRCS
     kitemviews/kitemlistcontainer.cpp
     kitemviews/kitemlistcontroller.cpp
     kitemviews/kitemlistgroupheader.cpp
+    kitemviews/kitemlistrubberband.cpp
     kitemviews/kitemlistselectionmanager.cpp
     kitemviews/kitemlistsizehintresolver.cpp
     kitemviews/kitemliststyleoption.cpp
index b8d05dc340956f9457f9dc08d9dcefb39faffc44..b01751e46cfd44067d09921b52c1419de164d5d0 100644 (file)
@@ -210,18 +210,32 @@ void KItemListContainer::slotViewChanged(KItemListView* current, KItemListView*
     QGraphicsScene* scene = static_cast<QGraphicsView*>(viewport())->scene();
     if (previous) {
         scene->removeItem(previous);
-        disconnect(previous, SIGNAL(offsetChanged(int,int)), this, SLOT(updateScrollBars()));
-        disconnect(previous, SIGNAL(maximumOffsetChanged(int,int)), this, SLOT(updateScrollBars()));
+        disconnect(previous, SIGNAL(offsetChanged(qreal,qreal)), this, SLOT(updateScrollBars()));
+        disconnect(previous, SIGNAL(maximumOffsetChanged(qreal,qreal)), this, SLOT(updateScrollBars()));
+        disconnect(previous, SIGNAL(scrollTo(qreal)), this, SLOT(scrollTo(qreal)));
         m_viewOffsetAnimation->setTargetObject(0);
     }
     if (current) {
         scene->addItem(current);
-        connect(current, SIGNAL(offsetChanged(int,int)), this, SLOT(updateScrollBars()));
-        connect(current, SIGNAL(maximumOffsetChanged(int,int)), this, SLOT(updateScrollBars()));
+        connect(current, SIGNAL(offsetChanged(qreal,qreal)), this, SLOT(updateScrollBars()));
+        connect(current, SIGNAL(maximumOffsetChanged(qreal,qreal)), this, SLOT(updateScrollBars()));
+        connect(current, SIGNAL(scrollTo(qreal)), this, SLOT(scrollTo(qreal)));
         m_viewOffsetAnimation->setTargetObject(current);
     }
 }
 
+void KItemListContainer::scrollTo(qreal offset)
+{
+    const KItemListView* view = m_controller->view();
+    if (!view) {
+        return;
+    }
+
+    QScrollBar* scrollBar = (view->scrollOrientation() == Qt::Vertical)
+                            ? verticalScrollBar() : horizontalScrollBar();
+    scrollBar->setValue(offset);
+}
+
 void KItemListContainer::updateScrollBars()
 {
     const KItemListView* view = m_controller->view();
@@ -267,11 +281,6 @@ void KItemListContainer::updateScrollBars()
     scrollBar->setMaximum(maximum);
     scrollBar->setValue(value);
 
-    disconnect(view, SIGNAL(scrollTo(int)),
-               otherScrollBar, SLOT(setValue(int)));
-    connect(view, SIGNAL(scrollTo(int)),
-            scrollBar, SLOT(setValue(int)));
-
     // Make sure that the other scroll bar is hidden
     otherScrollBar->setMaximum(0);
     otherScrollBar->setValue(0);
@@ -316,7 +325,7 @@ void KItemListContainer::initialize()
     setViewport(graphicsView);
 
     m_viewOffsetAnimation = new QPropertyAnimation(this, "offset");
-    m_viewOffsetAnimation->setDuration(500);
+    m_viewOffsetAnimation->setDuration(300);
 
     horizontalScrollBar()->installEventFilter(this);
     verticalScrollBar()->installEventFilter(this);
index e71a7b083baeabdbf6df3727e7759bc6d776b071..0cda436d011eece92264baee914e6a2f51a1eafd 100644 (file)
@@ -59,6 +59,7 @@ protected:
 private slots:
     void slotModelChanged(KItemModelBase* current, KItemModelBase* previous);
     void slotViewChanged(KItemListView* current, KItemListView* previous);
+    void scrollTo(qreal offset);
     void updateScrollBars();
 
 private:
index ef449f496193617c78e13e8c9ca9170700107370..7331131a94f8a638566806d107ac030cb195f0cc 100644 (file)
 #include "kitemlistcontroller.h"
 
 #include "kitemlistview.h"
+#include "kitemlistrubberband_p.h"
 #include "kitemlistselectionmanager.h"
 
 #include <QEvent>
 #include <QGraphicsSceneEvent>
-#include <QTransform>
 
 #include <KDebug>
 
@@ -80,11 +80,16 @@ void KItemListController::setView(KItemListView* view)
     }
 
     KItemListView* oldView = m_view;
+    if (oldView) {
+        disconnect(oldView, SIGNAL(offsetChanged(qreal,qreal)), this, SLOT(slotViewOffsetChanged(qreal,qreal)));
+    }
+
     m_view = view;
 
     if (m_view) {
         m_view->setController(this);
         m_view->setModel(m_model);
+        connect(m_view, SIGNAL(offsetChanged(qreal,qreal)), this, SLOT(slotViewOffsetChanged(qreal,qreal)));
     }
 
     emit viewChanged(m_view, oldView);
@@ -223,6 +228,10 @@ bool KItemListController::inputMethodEvent(QInputMethodEvent* event)
 
 bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
 {
+    if (!m_view) {
+        return false;
+    }
+
     const QPointF pos = transform.map(event->pos());
     m_pressedIndex = m_view->itemAt(pos);
 
@@ -248,29 +257,40 @@ bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const
 
         switch (m_selectionBehavior) {
         case NoSelection:
-            return true;
+            break;
+
         case SingleSelection:
             m_selectionManager->setSelected(m_pressedIndex);
-            return true;
+            break;
+
         case MultiSelection:
             if (controlPressed) {
                 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
                 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
-            }
-            else {
-                if (shiftPressed && m_selectionManager->isAnchoredSelectionActive()) {
-                    // The anchored selection is continued automatically by calling
-                    // m_selectionManager->setCurrentItem(m_pressedIndex), see above -> nothing more to do here
-                    return true;
-                }
-
+            } else if (!shiftPressed || !m_selectionManager->isAnchoredSelectionActive()) {
                 // Select the pressed item and start a new anchored selection
                 m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select);
                 m_selectionManager->beginAnchoredSelection(m_pressedIndex);
             }
+            break;
+
+        default:
+            Q_ASSERT(false);
+            break;
         }
 
         return true;
+    } else {
+        KItemListRubberBand* rubberBand = m_view->rubberBand();
+        QPointF startPos = pos;
+        if (m_view->scrollOrientation() == Qt::Vertical) {
+            startPos.ry() += m_view->offset();
+        } else {
+            startPos.rx() += m_view->offset();
+        }
+        rubberBand->setStartPosition(startPos);
+        rubberBand->setEndPosition(startPos);
+        rubberBand->setActive(true);
     }
 
     return false;
@@ -278,38 +298,55 @@ bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const
 
 bool KItemListController::mouseMoveEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
 {
-    Q_UNUSED(event);
-    Q_UNUSED(transform);
+    if (!m_view) {
+        return false;
+    }
+
+    KItemListRubberBand* rubberBand = m_view->rubberBand();
+    if (rubberBand->isActive()) {
+        QPointF endPos = transform.map(event->pos());
+        if (m_view->scrollOrientation() == Qt::Vertical) {
+            endPos.ry() += m_view->offset();
+        } else {
+            endPos.rx() += m_view->offset();
+        }
+        rubberBand->setEndPosition(endPos);
+    }
+
     return false;
 }
 
 bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
 {
-    if (m_view) {
-        const QPointF pos = transform.map(event->pos());
-        const int index = m_view->itemAt(pos);
-        const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier || event->modifiers() & Qt::ControlModifier;
-
-        if (index >= 0 && index == m_pressedIndex) {
-            // The release event is done above the same item as the press event
-            bool emitItemClicked = true;
-            if (event->button() & Qt::LeftButton) {
-                if (m_view->isAboveExpansionToggle(index, pos)) {
-                    emit itemExpansionToggleClicked(index);
-                    emitItemClicked = false;
-                }
-                else if (shiftOrControlPressed) {
-                    // The mouse click should only update the selection, not trigger the item
-                    emitItemClicked = false;
-                }
-            }
+    if (!m_view) {
+        return false;
+    }
+
+    m_view->rubberBand()->setActive(false);
 
-            if (emitItemClicked) {
-                emit itemClicked(index, event->button());
+    const QPointF pos = transform.map(event->pos());
+    const int index = m_view->itemAt(pos);
+    const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier || event->modifiers() & Qt::ControlModifier;
+
+    if (index >= 0 && index == m_pressedIndex) {
+        // The release event is done above the same item as the press event
+        bool emitItemClicked = true;
+        if (event->button() & Qt::LeftButton) {
+            if (m_view->isAboveExpansionToggle(index, pos)) {
+                emit itemExpansionToggleClicked(index);
+                emitItemClicked = false;
             }
-        } else if (!shiftOrControlPressed) {
-            m_selectionManager->clearSelection();
+            else if (shiftOrControlPressed) {
+                // The mouse click should only update the selection, not trigger the item
+                emitItemClicked = false;
+            }
+        }
+
+        if (emitItemClicked) {
+            emit itemClicked(index, event->button());
         }
+    } else if (!shiftOrControlPressed) {
+        m_selectionManager->clearSelection();
     }
 
     m_pressedIndex = -1;
@@ -480,4 +517,28 @@ bool KItemListController::processEvent(QEvent* event, const QTransform& transfor
     return false;
 }
 
+void KItemListController::slotViewOffsetChanged(qreal current, qreal previous)
+{
+    if (!m_view) {
+        return;
+    }
+
+    KItemListRubberBand* rubberBand = m_view->rubberBand();
+    if (rubberBand->isActive()) {
+        const qreal diff = current - previous;
+        // TODO: Ideally just QCursor::pos() should be used as
+        // new end-position but it seems there is no easy way
+        // to have something like QWidget::mapFromGlobal() for QGraphicsWidget
+        // (... or I just missed an easy way to do the mapping)
+        QPointF endPos = rubberBand->endPosition();
+        if (m_view->scrollOrientation() == Qt::Vertical) {
+            endPos.ry() += diff;
+        } else {
+            endPos.rx() += diff;
+        }
+
+        rubberBand->setEndPosition(endPos);
+    }
+}
+
 #include "kitemlistcontroller.moc"
index 092a7bc6210b196c80e13640ab1b65bfcc3e6850..c4406a972c7da829c7fa6679a573c2b6e7a67ad0 100644 (file)
@@ -28,6 +28,7 @@
 #include <QObject>
 
 class KItemModelBase;
+class KItemListRubberBandManager;
 class KItemListSelectionManager;
 class KItemListView;
 class QGraphicsSceneHoverEvent;
@@ -119,6 +120,9 @@ signals:
     void modelChanged(KItemModelBase* current, KItemModelBase* previous);
     void viewChanged(KItemListView* current, KItemListView* previous);
 
+private slots:
+    void slotViewOffsetChanged(qreal current, qreal previous);
+
 private:
     SelectionBehavior m_selectionBehavior;
     KItemModelBase* m_model;
diff --git a/src/kitemviews/kitemlistrubberband.cpp b/src/kitemviews/kitemlistrubberband.cpp
new file mode 100644 (file)
index 0000000..b8605a7
--- /dev/null
@@ -0,0 +1,80 @@
+/***************************************************************************
+ *   Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com>             *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
+ ***************************************************************************/
+
+#include "kitemlistrubberband_p.h"
+
+KItemListRubberBand::KItemListRubberBand(QObject* parent) :
+    QObject(parent),
+    m_active(false),
+    m_startPos(),
+    m_endPos()
+{
+}
+
+KItemListRubberBand::~KItemListRubberBand()
+{
+}
+
+void KItemListRubberBand::setStartPosition(const QPointF& pos)
+{
+    if (m_startPos != pos) {
+        const QPointF previous = m_startPos;
+        m_startPos = pos;
+        emit startPositionChanged(m_startPos, previous);
+    }
+}
+
+QPointF KItemListRubberBand::startPosition() const
+{
+    return m_startPos;
+}
+
+void KItemListRubberBand::setEndPosition(const QPointF& pos)
+{
+    if (m_endPos != pos) {
+        const QPointF previous = m_endPos;
+        m_endPos = pos;
+        emit endPositionChanged(m_endPos, previous);
+    }
+}
+
+QPointF KItemListRubberBand::endPosition() const
+{
+    return m_endPos;
+}
+
+void KItemListRubberBand::setActive(bool active)
+{
+    if (m_active != active) {
+        m_active = active;
+        emit activationChanged(active);
+
+        if (!active) {
+            m_startPos = QPointF();
+            m_endPos = QPointF();
+        }
+    }
+}
+
+bool KItemListRubberBand::isActive() const
+{
+    return m_active;
+}
+
+#include "kitemlistrubberband_p.moc"
diff --git a/src/kitemviews/kitemlistrubberband_p.h b/src/kitemviews/kitemlistrubberband_p.h
new file mode 100644 (file)
index 0000000..aea58af
--- /dev/null
@@ -0,0 +1,60 @@
+/***************************************************************************
+ *   Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com>             *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
+ ***************************************************************************/
+
+#ifndef KITEMLISTRUBBERBAND_H
+#define KITEMLISTRUBBERBAND_H
+
+#include <libdolphin_export.h>
+#include <QObject>
+#include <QPointF>
+
+/**
+ * @brief Manages the rubberband when selecting items.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KItemListRubberBand : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit KItemListRubberBand(QObject* parent = 0);
+    virtual ~KItemListRubberBand();
+
+    void setStartPosition(const QPointF& pos);
+    QPointF startPosition() const;
+
+    void setEndPosition(const QPointF& pos);
+    QPointF endPosition() const;
+
+    void setActive(bool active);
+    bool isActive() const;
+
+signals:
+    void activationChanged(bool active);
+    void startPositionChanged(const QPointF& current, const QPointF& previous);
+    void endPositionChanged(const QPointF& current, const QPointF& previous);
+
+private:
+    bool m_active;
+    QPointF m_startPos;
+    QPointF m_endPos;
+};
+
+#endif
+
+
index a88116800f8ea034178a44f73eae9708678b7695..3b6b4e2e96e903e0327e929b3d86484de599c9be 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "kitemlistcontroller.h"
 #include "kitemlistgroupheader.h"
+#include "kitemlistrubberband_p.h"
 #include "kitemlistselectionmanager.h"
 #include "kitemlistsizehintresolver_p.h"
 #include "kitemlistviewlayouter_p.h"
 
 #include <KDebug>
 
+#include <QCursor>
 #include <QGraphicsSceneMouseEvent>
+#include <QPainter>
 #include <QPropertyAnimation>
 #include <QStyle>
+#include <QStyleOptionRubberBand>
 #include <QTimer>
 
 KItemListView::KItemListView(QGraphicsWidget* parent) :
     QGraphicsWidget(parent),
+    m_autoScrollMarginEnabled(false),
     m_grouped(false),
     m_activeTransactions(0),
     m_itemSize(),
@@ -56,7 +61,9 @@ KItemListView::KItemListView(QGraphicsWidget* parent) :
     m_animation(0),
     m_layoutTimer(0),
     m_oldOffset(0),
-    m_oldMaximumOffset(0)
+    m_oldMaximumOffset(0),
+    m_rubberBand(0),
+    m_mousePos()
 {
     setAcceptHoverEvents(true);
 
@@ -73,6 +80,9 @@ KItemListView::KItemListView(QGraphicsWidget* parent) :
     m_layoutTimer->setInterval(300);
     m_layoutTimer->setSingleShot(true);
     connect(m_layoutTimer, SIGNAL(timeout()), this, SLOT(slotLayoutTimerFinished()));
+
+    m_rubberBand = new KItemListRubberBand(this);
+    connect(m_rubberBand, SIGNAL(activationChanged(bool)), this, SLOT(slotRubberBandActivationChanged(bool)));
 }
 
 KItemListView::~KItemListView()
@@ -348,6 +358,30 @@ bool KItemListView::isTransactionActive() const
     return m_activeTransactions > 0;
 }
 
+void KItemListView::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
+{
+    QGraphicsWidget::paint(painter, option, widget);
+
+    if (m_rubberBand->isActive()) {
+        QRectF rubberBandRect = QRectF(m_rubberBand->startPosition(),
+                                       m_rubberBand->endPosition()).normalized();
+
+        const QPointF topLeft = rubberBandRect.topLeft();
+        if (scrollOrientation() == Qt::Vertical) {
+            rubberBandRect.moveTo(topLeft.x(), topLeft.y() - offset());
+        } else {
+            rubberBandRect.moveTo(topLeft.x() - offset(), topLeft.y());
+        }
+
+        QStyleOptionRubberBand opt;
+        opt.initFrom(widget);
+        opt.shape = QRubberBand::Rectangle;
+        opt.opaque = false;
+        opt.rect = rubberBandRect.toRect();
+        style()->drawControl(QStyle::CE_RubberBand, &opt, painter);
+    }
+}
+
 void KItemListView::initializeItemListWidget(KItemListWidget* item)
 {
     Q_UNUSED(item);
@@ -415,9 +449,16 @@ bool KItemListView::event(QEvent* event)
 
 void KItemListView::mousePressEvent(QGraphicsSceneMouseEvent* event)
 {
+    m_mousePos = transform().map(event->pos());
     event->accept();
 }
 
+void KItemListView::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
+{
+    m_mousePos = transform().map(event->pos());
+    QGraphicsWidget::mouseMoveEvent(event);
+}
+
 QList<KItemListWidget*> KItemListView::visibleItemListWidgets() const
 {
     return m_visibleItems.values();
@@ -436,7 +477,7 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges)
     foreach (const KItemRange& range, itemRanges) {
         // range.index is related to the model before anything has been inserted.
         // As in each loop the current item-range gets inserted the index must
-        // be increased by the already previoulsy inserted items.
+        // be increased by the already previously inserted items.
         const int index = range.index + previouslyInsertedCount;
         const int count = range.count;
         if (index < 0 || count <= 0) {
@@ -701,6 +742,30 @@ void KItemListView::slotLayoutTimerFinished()
     doLayout(Animation, 0, 0);
 }
 
+void KItemListView::slotRubberBandStartPosChanged()
+{
+    update();
+}
+
+void KItemListView::slotRubberBandEndPosChanged()
+{
+    triggerAutoScrolling();
+    update();
+}
+
+void KItemListView::slotRubberBandActivationChanged(bool active)
+{
+    if (active) {
+        connect(m_rubberBand, SIGNAL(startPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandStartPosChanged()));
+        connect(m_rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandEndPosChanged()));
+    } else {
+        disconnect(m_rubberBand, SIGNAL(startPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandStartPosChanged()));
+        disconnect(m_rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandEndPosChanged()));
+    }
+
+    update();
+}
+
 void KItemListView::setController(KItemListController* controller)
 {
     if (m_controller != controller) {
@@ -756,6 +821,11 @@ void KItemListView::setModel(KItemModelBase* model)
     onModelChanged(model, previous);
 }
 
+KItemListRubberBand* KItemListView::rubberBand() const
+{
+    return m_rubberBand;
+}
+
 void KItemListView::updateLayout()
 {
     doLayout(Animation, 0, 0);
@@ -903,13 +973,13 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha
 
 void KItemListView::emitOffsetChanges()
 {
-    const int newOffset = m_layouter->offset();
+    const qreal newOffset = m_layouter->offset();
     if (m_oldOffset != newOffset) {
         emit offsetChanged(newOffset, m_oldOffset);
         m_oldOffset = newOffset;
     }
 
-    const int newMaximumOffset = m_layouter->maximumOffset();
+    const qreal newMaximumOffset = m_layouter->maximumOffset();
     if (m_oldMaximumOffset != newMaximumOffset) {
         emit maximumOffsetChanged(newMaximumOffset, m_oldMaximumOffset);
         m_oldMaximumOffset = newMaximumOffset;
@@ -1102,6 +1172,40 @@ void KItemListView::updateWidgetProperties(KItemListWidget* widget, int index)
     widget->setData(m_model->data(index));
 }
 
+void KItemListView::triggerAutoScrolling()
+{
+    const int pos = (scrollOrientation() == Qt::Vertical) ? m_mousePos.y() : m_mousePos.x();
+    const int inc = calculateAutoScrollingIncrement(pos, size().height());
+    if (inc != 0) {
+        emit scrollTo(offset() + inc);
+    }
+}
+
+int KItemListView::calculateAutoScrollingIncrement(int pos, int size)
+{
+    int inc = 0;
+
+    const int minSpeed = 4;
+    const int maxSpeed = 768;
+    const int speedLimiter = 48;
+    const int autoScrollBorder = 64;
+
+    if (pos < autoScrollBorder) {
+        inc = -minSpeed + qAbs(pos - autoScrollBorder) * (pos - autoScrollBorder) / speedLimiter;
+        if (inc < -maxSpeed) {
+            inc = -maxSpeed;
+        }
+    } else if (pos > size - autoScrollBorder) {
+        inc = minSpeed + qAbs(pos - size + autoScrollBorder) * (pos - size + autoScrollBorder) / speedLimiter;
+        if (inc > maxSpeed) {
+            inc = maxSpeed;
+        }
+    }
+
+    return inc;
+}
+
+
 KItemListCreatorBase::~KItemListCreatorBase()
 {
     qDeleteAll(m_recycleableWidgets);
index 974a9baaf537665e645650e0233dbd116ae278aa..e874957de98e7c4703ea16617cb24c6dc65ae8b6 100644 (file)
@@ -37,6 +37,7 @@ class KItemListWidgetCreatorBase;
 class KItemListGroupHeader;
 class KItemListGroupHeaderCreatorBase;
 class KItemListSizeHintResolver;
+class KItemListRubberBand;
 class KItemListViewAnimation;
 class KItemListViewLayouter;
 class KItemListWidget;
@@ -142,10 +143,15 @@ public:
     void endTransaction();
     bool isTransactionActive() const;
 
+    /**
+     * @reimp
+     */
+   virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
+
 signals:
-    void offsetChanged(int current, int previous);
-    void maximumOffsetChanged(int current, int previous);
-    void scrollTo(int newOffset);
+    void offsetChanged(qreal current, qreal previous);
+    void maximumOffsetChanged(qreal current, qreal previous);
+    void scrollTo(qreal newOffset);
 
 protected:
     virtual void initializeItemListWidget(KItemListWidget* item);
@@ -164,6 +170,7 @@ protected:
 
     virtual bool event(QEvent* event);
     virtual void mousePressEvent(QGraphicsSceneMouseEvent* event);
+    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
 
     QList<KItemListWidget*> visibleItemListWidgets() const;
 
@@ -180,6 +187,10 @@ private slots:
                                KItemListViewAnimation::AnimationType type);
     void slotLayoutTimerFinished();
 
+    void slotRubberBandStartPosChanged();
+    void slotRubberBandEndPosChanged();
+    void slotRubberBandActivationChanged(bool active);
+
 private:
     enum LayoutAnimationHint
     {
@@ -196,6 +207,8 @@ private:
     void setController(KItemListController* controller);
     void setModel(KItemModelBase* model);
 
+    KItemListRubberBand* rubberBand() const;
+
     void updateLayout();
     void doLayout(LayoutAnimationHint hint, int changedIndex, int changedCount);
     void doGroupHeadersLayout(LayoutAnimationHint hint, int changedIndex, int changedCount);
@@ -241,7 +254,22 @@ private:
      */
     void updateWidgetProperties(KItemListWidget* widget, int index);
 
+    /**
+     * Emits the signal scrollTo() with the corresponding target offset if the current
+     * mouse position is above the autoscroll-margin.
+     */
+    void triggerAutoScrolling();
+
+    /**
+     * Helper function for triggerAutoScrolling(). Returns the scroll increment
+     * that should be added to the offset() based on the available size \a size
+     * and the current mouse position \a pos. As soon as \a pos is inside
+     * the autoscroll-margin a value != 0 will be returned.
+     */
+    static int calculateAutoScrollingIncrement(int pos, int size);
+
 private:
+    bool m_autoScrollMarginEnabled;
     bool m_grouped;
     int m_activeTransactions; // Counter for beginTransaction()/endTransaction()
 
@@ -263,8 +291,12 @@ private:
     KItemListViewAnimation* m_animation;
 
     QTimer* m_layoutTimer; // Triggers an asynchronous doLayout() call.
-    int m_oldOffset;
-    int m_oldMaximumOffset;
+    qreal m_oldOffset;
+    qreal m_oldMaximumOffset;
+
+    KItemListRubberBand* m_rubberBand;
+
+    QPointF m_mousePos;
 
     friend class KItemListController;
 };