~ubuntu-branches/ubuntu/warty/kdebase/warty

« back to all changes in this revision

Viewing changes to khotkeys/shared/gestures.cpp

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2004-09-16 04:51:45 UTC
  • Revision ID: james.westby@ubuntu.com-20040916045145-9vr63kith3k1cpza
Tags: upstream-3.2.2
ImportĀ upstreamĀ versionĀ 3.2.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
 
 
3
 KHotKeys
 
4
 
 
5
 Copyright (C) 1999-2002 Lubos Lunak <l.lunak@kde.org>
 
6
 
 
7
 Distributed under the terms of the GNU General Public License version 2.
 
8
 
 
9
 Based on LibStroke :
 
10
  ( libstroke - an X11 stroke interface library
 
11
  Copyright (c) 1996,1997,1998,1999  Mark F. Willey, ETLA Technical
 
12
  There is a reference application available on the LibStroke Home Page:
 
13
  http://www.etla.net/~willey/projects/libstroke/ )
 
14
 
 
15
 $Id: gestures.cpp,v 1.6.2.2 2004/03/15 13:38:21 lunakl Exp $
 
16
 
 
17
****************************************************************************/
 
18
 
 
19
#define _GESTURES_CPP_
 
20
 
 
21
#ifdef HAVE_CONFIG_H
 
22
#include <config.h>
 
23
#endif
 
24
 
 
25
#include "gestures.h"
 
26
 
 
27
#include <stdlib.h>
 
28
#include <math.h>
 
29
#include <assert.h>
 
30
 
 
31
#include <X11/Xlib.h>
 
32
 
 
33
#include <kapplication.h>
 
34
#include <kdebug.h>
 
35
#include <kxerrorhandler.h>
 
36
#include <kkeynative.h>
 
37
 
 
38
#include "input.h"
 
39
 
 
40
namespace KHotKeys
 
41
{
 
42
 
 
43
Gesture* gesture_handler;
 
44
 
 
45
Gesture::Gesture( bool /*enabled_P*/, QObject* parent_P )
 
46
    : _enabled( false ), recording( false ), button( 0 )
 
47
    {
 
48
    (void) new DeleteObject( this, parent_P );
 
49
    assert( gesture_handler == NULL );
 
50
    gesture_handler = this;
 
51
    connect( &nostroke_timer, SIGNAL( timeout()), SLOT( stroke_timeout()));
 
52
    }
 
53
      
 
54
Gesture::~Gesture()
 
55
    {
 
56
    enable( false );
 
57
    gesture_handler = NULL;
 
58
    }
 
59
 
 
60
void Gesture::enable( bool enabled_P )
 
61
    {
 
62
    if( _enabled == enabled_P )
 
63
        return;
 
64
    _enabled = enabled_P;
 
65
    assert( button != 0 );
 
66
    update_grab();
 
67
    }
 
68
    
 
69
void Gesture::update_grab()
 
70
    {
 
71
    if( _enabled && handlers.count() > 0 )
 
72
        {
 
73
        kapp->installX11EventFilter( this );
 
74
        // CHECKME at se grabuje jen kdyz je alespon jedno gesto?
 
75
        // + seznam oken, pro ktere nedelat grab?
 
76
        grab_mouse( true );
 
77
        }
 
78
    else
 
79
        {
 
80
        grab_mouse( false );
 
81
        kapp->removeX11EventFilter( this );
 
82
        }
 
83
    }
 
84
 
 
85
void Gesture::register_handler( QObject* receiver_P, const char* slot_P )
 
86
    {
 
87
    if( handlers.contains( receiver_P ))
 
88
        return;
 
89
    handlers[ receiver_P ] = true;
 
90
    connect( this, SIGNAL( handle_gesture( const QString& )),
 
91
        receiver_P, slot_P );
 
92
    if( handlers.count() == 1 )
 
93
        update_grab();
 
94
    }
 
95
 
 
96
void Gesture::unregister_handler( QObject* receiver_P, const char* slot_P )
 
97
    {
 
98
    if( !handlers.contains( receiver_P ))
 
99
        return;
 
100
    handlers.remove( receiver_P );
 
101
    disconnect( this, SIGNAL( handle_gesture( const QString& )),
 
102
        receiver_P, slot_P );
 
103
    if( handlers.count() == 0 )
 
104
        update_grab();
 
105
    }
 
106
 
 
107
bool Gesture::x11Event( XEvent* ev_P )
 
108
    {
 
109
    if( ev_P->type == ButtonPress && ev_P->xbutton.button == button )
 
110
        {
 
111
        kdDebug( 1217 ) << "GESTURE: mouse press" << endl;
 
112
        stroke.reset();
 
113
        stroke.record( ev_P->xbutton.x, ev_P->xbutton.y );
 
114
        nostroke_timer.start( timeout, true );
 
115
        recording = true;
 
116
        start_x = ev_P->xbutton.x_root;
 
117
        start_y = ev_P->xbutton.y_root;
 
118
        return true;
 
119
        }
 
120
    else if( ev_P->type == ButtonRelease && ev_P->xbutton.button == button
 
121
        && recording )
 
122
        {
 
123
        recording = false;
 
124
        nostroke_timer.stop();
 
125
        stroke.record( ev_P->xbutton.x, ev_P->xbutton.y );
 
126
        QString gesture( stroke.translate());
 
127
        if( gesture.isEmpty())
 
128
            {
 
129
            kdDebug( 1217 ) << "GESTURE: replay" << endl;
 
130
            XAllowEvents( qt_xdisplay(), AsyncPointer, CurrentTime );
 
131
            XUngrabPointer( qt_xdisplay(), CurrentTime );
 
132
            mouse_replay( true );
 
133
            return true;
 
134
            }
 
135
        kdDebug( 1217 ) << "GESTURE: got: " << gesture << endl;
 
136
        emit handle_gesture( gesture );
 
137
        return true;
 
138
        }
 
139
    else if( ev_P->type == MotionNotify && recording )
 
140
        { // ignore small initial movement
 
141
        if( nostroke_timer.isActive()
 
142
            && abs( start_x - ev_P->xmotion.x_root ) < 10
 
143
            && abs( start_y - ev_P->xmotion.y_root ) < 10 )
 
144
            return true;
 
145
        nostroke_timer.stop();
 
146
        stroke.record( ev_P->xmotion.x, ev_P->xmotion.y );
 
147
        }
 
148
    return false;
 
149
    }
 
150
 
 
151
void Gesture::stroke_timeout()
 
152
    {
 
153
    kdDebug( 1217 ) << "GESTURE: timeout" << endl;
 
154
    XAllowEvents( qt_xdisplay(), AsyncPointer, CurrentTime );
 
155
    XUngrabPointer( qt_xdisplay(), CurrentTime );
 
156
    mouse_replay( false );
 
157
    recording = false;
 
158
    }
 
159
 
 
160
void Gesture::mouse_replay( bool release_P )
 
161
    {
 
162
    bool was_enabled = _enabled;
 
163
    enable( false );
 
164
    Mouse::send_mouse_button( button, release_P );
 
165
    enable( was_enabled );
 
166
    }
 
167
 
 
168
void Gesture::grab_mouse( bool grab_P )
 
169
    {
 
170
    if( grab_P )
 
171
        {
 
172
        KXErrorHandler handler;
 
173
        static int mask[] = { 0, Button1MotionMask, Button2MotionMask, Button3MotionMask,
 
174
            Button4MotionMask, Button5MotionMask, ButtonMotionMask, ButtonMotionMask,
 
175
            ButtonMotionMask, ButtonMotionMask };
 
176
#define XCapL KKeyNative::modXLock()
 
177
#define XNumL KKeyNative::modXNumLock()
 
178
#define XScrL KKeyNative::modXScrollLock()
 
179
        unsigned int mods[ 8 ] = 
 
180
            {
 
181
            0, XCapL, XNumL, XNumL | XCapL,
 
182
            XScrL, XScrL | XCapL,
 
183
            XScrL | XNumL, XScrL | XNumL | XCapL
 
184
            };
 
185
#undef XCapL
 
186
#undef XNumL
 
187
#undef XScrL
 
188
        for( int i = 0;
 
189
             i < 8;
 
190
             ++i )
 
191
            XGrabButton( qt_xdisplay(), button, mods[ i ], qt_xrootwin(), False,
 
192
                ButtonPressMask | ButtonReleaseMask | mask[ button ], GrabModeAsync, GrabModeAsync,
 
193
                None, None );
 
194
        bool err = handler.error( true );
 
195
        kdDebug( 1217 ) << "Gesture grab:" << err << endl;
 
196
        }
 
197
    else
 
198
        {
 
199
        kdDebug( 1217 ) << "Gesture ungrab" << endl;
 
200
        XUngrabButton( qt_xdisplay(), button, AnyModifier, qt_xrootwin());
 
201
        }
 
202
    }
 
203
 
 
204
void Gesture::set_mouse_button( unsigned int button_P )
 
205
    {
 
206
    if( button == button_P )
 
207
        return;
 
208
    if( !_enabled )
 
209
        {
 
210
        button = button_P;
 
211
        return;
 
212
        }
 
213
    grab_mouse( false );
 
214
    button = button_P;
 
215
    grab_mouse( true );
 
216
    }
 
217
 
 
218
void Gesture::set_timeout( int timeout_P )
 
219
    {
 
220
    timeout = timeout_P;
 
221
    }
 
222
    
 
223
Stroke::Stroke()
 
224
    {
 
225
    reset();
 
226
    points = new point[ MAX_POINTS ]; // CHECKME
 
227
    }
 
228
    
 
229
Stroke::~Stroke()
 
230
    {
 
231
    delete[] points;
 
232
    }
 
233
 
 
234
void Stroke::reset()
 
235
    {
 
236
    min_x = 10000;
 
237
    min_y = 10000;
 
238
    max_x = -1;
 
239
    max_y = -1;
 
240
    point_count = -1;
 
241
    }
 
242
        
 
243
bool Stroke::record( int x, int y )
 
244
    {
 
245
    if( point_count >= MAX_POINTS )
 
246
        return false;
 
247
    if( point_count == -1 )
 
248
        {
 
249
        ++point_count;
 
250
        points[ point_count ].x = x;
 
251
        points[ point_count ].y = y;
 
252
        min_x = max_x = x;
 
253
        min_y = max_y = y;
 
254
        }
 
255
    else
 
256
        {
 
257
      // interpolate between last and current point
 
258
        int delx = x - points[ point_count ].x;
 
259
        int dely = y - points[ point_count ].y;
 
260
        if( abs( delx ) > abs( dely )) // step by the greatest delta direction
 
261
            { 
 
262
            float iy = points[ point_count ].y;
 
263
             // go from the last point to the current, whatever direction it may be
 
264
            for( int ix = points[ point_count ].x;
 
265
                 ( delx > 0 ) ? ( ix < x ) : ( ix > x );
 
266
                 ( delx > 0 ) ? ++ix : --ix )
 
267
                {
 
268
                // step the other axis by the correct increment
 
269
                if( dely < 0 )
 
270
                    iy -= fabs( dely / ( float ) delx );
 
271
                else
 
272
                    iy += fabs( dely / ( float ) delx );
 
273
                // add the interpolated point
 
274
                ++point_count;
 
275
                if( point_count >= MAX_POINTS )
 
276
                    return false;
 
277
                points[ point_count ].x = ix;
 
278
                points[ point_count ].y = ( int )iy;
 
279
                }
 
280
            // add the last point
 
281
            ++point_count;
 
282
            if( point_count >= MAX_POINTS )
 
283
                return false;
 
284
            points[ point_count ].x = x;
 
285
            points[ point_count ].y = y;
 
286
            // update metrics, it's ok to do it only for the last point
 
287
            if( x < min_x )
 
288
                min_x = x;
 
289
            if( x > max_x )
 
290
                max_x = x;
 
291
            if( y < min_y )
 
292
                min_y = y;
 
293
            if( y > max_y )
 
294
                max_y = y;
 
295
            }
 
296
        else
 
297
            { // same thing, but for dely larger than delx case...
 
298
            float ix = points[ point_count ].x;
 
299
             // go from the last point to the current, whatever direction it may be
 
300
            for( int iy = points[ point_count ].y;
 
301
                 ( dely > 0 ) ? ( iy < y ) : ( iy > y );
 
302
                 ( dely > 0 ) ? ++iy : --iy )
 
303
                {
 
304
                // step the other axis by the correct increment
 
305
                if( delx < 0 )
 
306
                    ix -= fabs( delx / ( float ) dely );
 
307
                else
 
308
                    ix += fabs( delx / ( float ) dely );
 
309
                // add the interpolated point
 
310
                ++point_count;
 
311
                if( point_count >= MAX_POINTS )
 
312
                    return false;
 
313
                points[ point_count ].x = ( int )ix;
 
314
                points[ point_count ].y = iy;
 
315
                }
 
316
            // add the last point
 
317
            ++point_count;
 
318
            if( point_count >= MAX_POINTS )
 
319
                return false;
 
320
            points[ point_count ].x = x;
 
321
            points[ point_count ].y = y;
 
322
            // update metrics, ts's ok to do it only for the last point
 
323
            if( x < min_x )
 
324
                min_x = x;
 
325
            if( x > max_x )
 
326
                max_x = x;
 
327
            if( y < min_y )
 
328
                min_y = y;
 
329
            if( y > max_y )
 
330
                max_y = y;
 
331
            }
 
332
        }
 
333
    return true;
 
334
    }
 
335
    
 
336
char* Stroke::translate( int min_bin_points_percentage_P, int scale_ratio_P, int min_points_P )
 
337
    {
 
338
    if( point_count < min_points_P )
 
339
        return NULL;
 
340
    // determine size of grid
 
341
    delta_x = max_x - min_x;
 
342
    delta_y = max_y - min_y;
 
343
    if( delta_x > scale_ratio_P * delta_y )
 
344
        {
 
345
        int avg_y = ( max_y + min_y ) / 2;
 
346
        min_y = avg_y - delta_x / 2;
 
347
        max_y = avg_y + delta_x / 2;
 
348
        delta_y = max_y - min_y;
 
349
        }
 
350
    else if( delta_y > scale_ratio_P * delta_x )
 
351
        {
 
352
        int avg_x = ( max_x + min_x ) / 2;
 
353
        min_x = avg_x - delta_y / 2;
 
354
        max_x = avg_x + delta_y / 2;
 
355
        delta_x = max_x - min_x;
 
356
        }
 
357
    // calculate bin boundary positions
 
358
    bound_x_1 = min_x + delta_x / 3;
 
359
    bound_x_2 = min_x + 2 * delta_x / 3;
 
360
    bound_y_1 = min_y + delta_y / 3;
 
361
    bound_y_2 = min_y + 2 * delta_y / 3;
 
362
 
 
363
    int sequence_count = 0;
 
364
    // points-->sequence translation scratch variables
 
365
    int prev_bin = 0;
 
366
    int current_bin = 0;
 
367
    int bin_count = 0;
 
368
// build string by placing points in bins, collapsing bins and discarding
 
369
// those with too few points...
 
370
    for( int pos = 0;
 
371
         pos <= point_count;
 
372
         ++pos )
 
373
        {
 
374
        // figure out which bin the point falls in
 
375
        current_bin = bin( points[ pos ].x, points[ pos ].y );
 
376
        // if this is the first point, consider it the previous bin, too.
 
377
        if( prev_bin == 0 )
 
378
            prev_bin = current_bin;
 
379
        if( prev_bin == current_bin )
 
380
            bin_count++;
 
381
        else
 
382
            {  // we are moving to a new bin -- consider adding to the sequence
 
383
                                                // CHECKME tohle taky konfigurovatelne ?
 
384
            if( bin_count >= ( min_bin_points_percentage_P * point_count / 100 )
 
385
                || sequence_count == 0 )
 
386
                {
 
387
                if( sequence_count >= MAX_SEQUENCE )
 
388
                    return NULL;
 
389
                ret_val[ sequence_count++ ] = prev_bin + '0';
 
390
                }
 
391
            // restart counting points in the new bin
 
392
            bin_count=0;
 
393
            prev_bin = current_bin;
 
394
            }
 
395
        }
 
396
 
 
397
    // add the last run of points to the sequence
 
398
    if( sequence_count >= MAX_SEQUENCE - 1 )
 
399
        return NULL;
 
400
    ret_val[ sequence_count++ ] = current_bin + '0';
 
401
    ret_val[ sequence_count ] = 0; // endmark
 
402
    return ret_val;
 
403
    }
 
404
 
 
405
/* figure out which bin the point falls in */
 
406
int Stroke::bin( int x, int y )
 
407
    {
 
408
    int bin_num = 1;
 
409
    if( x > bound_x_1 )
 
410
        ++bin_num;
 
411
    if( x > bound_x_2 )
 
412
        ++bin_num;
 
413
    if( y < bound_y_1 )
 
414
        bin_num += 3;
 
415
    if( y < bound_y_2 )
 
416
        bin_num += 3;
 
417
    return bin_num;
 
418
    }
 
419
 
 
420
} // namespace KHotKeys
 
421
 
 
422
#include "gestures.moc"