Fix compiler warnings.
[shopper.git] / src / ui / QFingerScrollArea.cc
blob53c3c9d0588cffdd010ee619184ab7695b4fb01c
1 /* Shopper
2 * Copyright (C) 2008 David Greaves
4 * This software is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public License
6 * as published by the Free Software Foundation; either version 2.1 of
7 * the License, or (at your option) any later version.
9 * This software is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this software; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 * 02110-1301 USA
20 //#define DEBUG_SHOPPER 1
21 #include "shopper.h" // automake, i8n, gettext
22 #include "QFingerScrollArea.h"
23 #include <QtGui>
25 #define SCROLL_SENSITIVITY 20 // pixels before we scroll
26 #define SCROLL_SCALE 0.1 // sensitivity to movement
27 #define KINETIC_REFRESH 100 // ms
28 #define VSCALE 100 // velocity scaling to ensure not lost in int rounding
29 #define DECEL 0.75 // velocity reduction factor/kinetic refresh
30 #define DECEL_DURATION 1000 // ms
32 #define DEBOUNCE 200 // after a mouseRelease we disallow MousePress for this long
34 // QFingerScrollArea a QScrollArea that responds to fingers
35 QFingerScrollArea::QFingerScrollArea(QWidget * parent) :
36 QScrollArea(parent),
37 allowHoriz(false),
38 allowVert(true),
39 scrollPaper(true),
40 direction(1),
41 scrolling(false),
42 recently_scrolling(false),
43 scrolling_setup(false),
44 scroll_start_x(0),
45 scroll_start_y(0),
46 start_x(0),
47 start_y(0),
48 curr_x(0),
49 curr_y(0),
50 scroll_range_x(0),
51 scroll_range_y(0),
52 scale_x(0),
53 scale_y(0),
54 kinetic(true),
55 last_x(0),
56 last_y(0),
57 vel_x(0),
58 vel_y(0),
59 vel_x1(0),
60 vel_y1(0),
61 last_ev_time(0),
62 curr_time(0),
63 kineticTimer(0),
64 debounceTimer(0),
65 kinetic_cycles(0)
67 max_x= qApp->desktop()->screenGeometry().width();
68 max_y= qApp->desktop()->screenGeometry().height();
71 ////////////////////////////////////////////////////////////////
72 // Interface
73 void QFingerScrollArea::setScrollConstraints(bool AllowHoriz, bool AllowVert)
75 allowVert = AllowVert;
76 allowHoriz = AllowHoriz;
77 if (!(allowHoriz or allowVert)) allowVert = true; // Don't
79 void QFingerScrollArea::setScrollPaper(bool _scrollPaper)
81 scrollPaper = _scrollPaper;
83 void QFingerScrollArea::setKinetic(bool _kinetic)
85 kinetic = _kinetic;
87 ////////////////////////////////////////////////////////////////
88 // Finger scroll implementation - movement
89 void QFingerScrollArea::mousePressEvent ( QMouseEvent * event )
91 if (event->button() == Qt::LeftButton){
92 setupEvent ( event );
94 QScrollArea::mousePressEvent(event);
96 void QFingerScrollArea::setupEvent ( QMouseEvent * event )
98 start_y = event->globalY();
99 scroll_start_y = verticalScrollBar()->value();
100 scroll_range_y = verticalScrollBar()->maximum() - verticalScrollBar()->minimum();
102 start_x = event->globalX();
103 scroll_start_x = horizontalScrollBar()->value();
104 scroll_range_x = horizontalScrollBar()->maximum() - horizontalScrollBar()->minimum();
106 if (scrollPaper) {
107 scale_x = widget()->width()-width();
108 scale_y = widget()->height()-height();
109 direction = 1;
110 } else {
111 scale_x = width();
112 scale_y = height();
113 direction = -1;
115 event_time.start();
116 last_ev_time=0;
117 scrolling_setup = true;
118 DEBUG("scroll setup " <<start_x<<":"<<start_y);
120 void QFingerScrollArea::mouseMoveEvent ( QMouseEvent * event )
122 // We should get a mousePressedEvent - but we don't always...
123 if (!scrolling_setup) {
124 setupEvent(event);
127 if (abs(start_x - event->globalX()) > SCROLL_SENSITIVITY or
128 abs(start_y - event->globalY()) > SCROLL_SENSITIVITY) {
129 if ((allowVert and scroll_range_y) or
130 (allowHoriz and scroll_range_x))
131 scrolling = true;
133 if (!scrolling) {
134 QScrollArea::mouseMoveEvent(event);
135 return;
137 curr_y = event->globalY();
138 curr_x = event->globalX();
140 if (allowVert and scroll_range_y) {
141 // (curr_y - start_y) = delta_pixels
142 // scroll_start / scroll_range = fraction_value_of_scrollbar
143 // fraction_value_of_scrollbar * scale_y = pixels above widget base
144 // pixels above widget base + delta_pixels = new_fraction_value_of_scrollbar * scale
145 // new_fraction_value_of_scrollbar = new_value_of_scrollbar / scroll_range
146 // v2 = ((scroll_start / scroll_range) * scale + (curr - start))/scale)*scroll_range
148 // if scale is the scrollarea height then moving will match the scrollbar
150 // if scale is the child widget height then moving will scroll pixel by pixel
153 // DEBUG(scroll_start_y<<" + (("<<start_y<<" - "<<curr_y<<") * "<<scroll_range_y<<")/"<<scale_y);
154 int v_y = scroll_start_y + direction * ((start_y - curr_y) * scroll_range_y)/scale_y;
155 verticalScrollBar()->setValue(v_y);
157 if (allowHoriz and scroll_range_x) {
158 int v_x = scroll_start_x + direction * ((start_x - curr_x) * scroll_range_x)/scale_x;
159 verticalScrollBar()->setValue(v_x);
161 curr_time = event_time.elapsed();
162 DEBUG("scroll move " <<curr_x<<":"<<curr_y<<" time:"<<curr_time);
164 if (curr_time > last_ev_time){
165 if (last_ev_time){ // first time round we set v to zero
166 vel_x1 = vel_x;
167 vel_x= (last_x - curr_x)*VSCALE / (curr_time - last_ev_time);
168 vel_y1 = vel_y;
169 vel_y= (last_y - curr_y)*VSCALE / (curr_time - last_ev_time);
170 DEBUG("Velocity last_y:"<<last_y<< " curr_y:"<<curr_y <<" curr_time:"<<curr_time <<" last_ev_time:"<<last_ev_time << " Velocity: "<<vel_y);
171 } else {
172 vel_x = vel_y = 0;
173 vel_x1 = vel_y1 = 0;
175 // Store the last values
176 last_ev_time = curr_time;
177 last_x = curr_x;
178 last_y = curr_y;
180 event->accept();
182 void QFingerScrollArea::mouseReleaseEvent ( QMouseEvent * event )
184 // _ENTER;
185 if (scrolling and event->button() == Qt::LeftButton) {
186 event->accept();
187 if (kinetic and last_ev_time) { // Only do velocity if we had a last_ev_time
188 curr_time = event_time.elapsed();
189 #ifdef DEBUG_SHOPPER
190 int y = event->globalY();
191 int x = event->globalX();
192 #endif
193 DEBUG("scroll stop " <<x<<":"<<y<<" time:"<<curr_time);
195 kinetic_cycles = 0;
196 kineticTimer=startTimer(KINETIC_REFRESH);
198 scrolling = false;
199 scrolling_setup = false;
200 recently_scrolling = true;
201 debounceTimer = startTimer(DEBOUNCE);
202 return;
204 QScrollArea::mouseReleaseEvent(event);
206 ////////////////////////////////////////////////////////////////
207 // Kinetics
208 void QFingerScrollArea::timerEvent(QTimerEvent *event)
210 if (event->timerId() == debounceTimer) {
211 recently_scrolling = false;
212 killTimer(debounceTimer);
213 debounceTimer = 0;
214 return;
217 if (event->timerId() == kineticTimer) {
218 if (allowVert) {
219 int v_y = verticalScrollBar()->value() +
220 direction * vel_y1 * KINETIC_REFRESH/VSCALE * scroll_range_y / scale_y;
221 verticalScrollBar()->setValue(v_y);
222 vel_y1 *= DECEL;
225 if (allowHoriz) {
226 int v_x = horizontalScrollBar()->value() +
227 direction * vel_x1 * KINETIC_REFRESH/VSCALE * scroll_range_x / scale_x;
228 horizontalScrollBar()->setValue(v_x);
229 vel_x1 *= DECEL;
231 show();
232 kinetic_cycles++;
233 if (scrolling or kinetic_cycles > DECEL_DURATION/KINETIC_REFRESH or
234 (vel_x1 == 0 and vel_y1 ==0)) {
235 killTimer(kineticTimer);
240 ////////////////////////////////////////////////////////////////
241 // Child management - event filtering
242 // Handle events down the hierarchy : FIX: as children are added, register them
243 void QFingerScrollArea::childEvent ( QChildEvent * event )
245 DEBUG("childEvent is a " << event->type());
246 if (event->type() == QEvent::ChildAdded or
247 event->type() == 70) { // FIX is this a Qt bug? 70 is ChildInserted which is deprecated in 4.4
248 registerChildrenForFingerScrolling(event->child());
250 if (event->removed()) {
251 deregisterChildrenForFingerScrolling(event->child());
255 void QFingerScrollArea::registerChildrenForFingerScrolling(QObject *top)
257 top->installEventFilter(this);
258 DEBUG("registered a " << top->metaObject()->className());
259 QObject *obj;
260 QList<QObject *> children = top->findChildren<QObject *>();
261 foreach (obj, children){
262 DEBUG("registered a " << obj->metaObject()->className());
263 obj->installEventFilter(this);
266 void QFingerScrollArea::deregisterChildrenForFingerScrolling(QObject *top)
268 top->removeEventFilter(this);
269 DEBUG("deregistered a " << top->metaObject()->className());
270 QObject *obj;
271 QList<QObject *> children = top->findChildren<QObject *>();
272 foreach (obj, children){
273 obj->removeEventFilter(this);
274 DEBUG("deregistered a " << obj->metaObject()->className());
278 bool QFingerScrollArea::eventFilter(QObject *obj, QEvent *event)
280 switch (event->type()) {
281 case QEvent::MouseButtonPress :
282 if (recently_scrolling) {
283 return true;
285 if (static_cast<QMouseEvent *>(event)->button() == Qt::LeftButton){
286 setupEvent ( static_cast<QMouseEvent *>(event));
288 break;
289 case QEvent::MouseButtonRelease :
290 if (scrolling) {
291 mouseReleaseEvent(static_cast<QMouseEvent *>(event));
292 return true;
294 if (recently_scrolling) {
295 return true;
297 break;
298 case QEvent::MouseMove :
299 if (scrolling) {
300 mouseMoveEvent(static_cast<QMouseEvent *>(event));
301 return true;
303 break;
304 case QEvent::ChildAdded :
305 registerChildrenForFingerScrolling(static_cast<QChildEvent *>(event)->child());
306 break;
307 case QEvent::ChildRemoved :
308 deregisterChildrenForFingerScrolling(static_cast<QChildEvent *>(event)->child());
309 break;
310 default:
311 break;
313 // standard event processing
314 return QScrollArea::eventFilter(obj, event);