~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to kstyles/oxygen/demo/oxygensimulator.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// krazy:excludeall=qclasses
 
2
 
 
3
//////////////////////////////////////////////////////////////////////////////
 
4
// oxygensimulator.cpp
 
5
// simulates event chain passed to the application
 
6
// -------------------
 
7
//
 
8
// Copyright (c) 2010 Hugo Pereira Da Costa <hugo@oxygen-icons.org>
 
9
//
 
10
// Permission is hereby granted, free of charge, to any person obtaining a copy
 
11
// of this software and associated documentation files (the "Software"), to
 
12
// deal in the Software without restriction, including without limitation the
 
13
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 
14
// sell copies of the Software, and to permit persons to whom the Software is
 
15
// furnished to do so, subject to the following conditions:
 
16
//
 
17
// The above copyright notice and this permission notice shall be included in
 
18
// all copies or substantial portions of the Software.
 
19
//
 
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
21
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
22
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
23
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
24
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 
25
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 
26
// IN THE SOFTWARE.
 
27
//////////////////////////////////////////////////////////////////////////////
 
28
 
 
29
#include "oxygensimulator.h"
 
30
#include "oxygensimulator.moc"
 
31
 
 
32
 
 
33
#include <QtGui/QAbstractItemView>
 
34
#include <QtGui/QApplication>
 
35
#include <QtGui/QCheckBox>
 
36
#include <QtGui/QComboBox>
 
37
#include <QtGui/QCursor>
 
38
#include <QtGui/QFocusEvent>
 
39
#include <QtGui/QHoverEvent>
 
40
#include <QtGui/QMdiSubWindow>
 
41
#include <QtGui/QMenu>
 
42
#include <QtGui/QMouseEvent>
 
43
#include <QtGui/QPushButton>
 
44
#include <QtGui/QRadioButton>
 
45
#include <QtGui/QScrollBar>
 
46
#include <QtGui/QSlider>
 
47
#include <QtGui/QStyle>
 
48
#include <QtGui/QStyleOptionComboBox>
 
49
#include <QtGui/QStyleOptionSlider>
 
50
#include <QtGui/QToolButton>
 
51
 
 
52
#ifdef Q_OS_WIN
 
53
/* need windows.h include for Sleep function*/
 
54
#include <windows.h>
 
55
#endif
 
56
 
 
57
#ifdef Q_OS_UNIX
 
58
#include <ctime>
 
59
#endif
 
60
 
 
61
namespace Oxygen
 
62
{
 
63
 
 
64
    //_______________________________________________________________________
 
65
    bool Simulator::_grabMouse = true;
 
66
    int Simulator::_defaultDelay = 250;
 
67
 
 
68
    //_______________________________________________________________________
 
69
    Simulator::~Simulator( void )
 
70
    {}
 
71
 
 
72
    //_______________________________________________________________________
 
73
    void Simulator::wait( int delay )
 
74
    { _events.push_back( Event( Event::Wait, 0, delay ) ); }
 
75
 
 
76
    //_______________________________________________________________________
 
77
    void Simulator::click( QWidget* receiver, int delay  )
 
78
    {
 
79
 
 
80
        QPoint position;
 
81
        if( QCheckBox* checkbox = qobject_cast<QCheckBox*>( receiver ) )
 
82
        {
 
83
 
 
84
            QStyleOptionButton option;
 
85
            option.initFrom( checkbox );
 
86
            position = checkbox->style()->subElementRect(
 
87
                QStyle::SE_CheckBoxIndicator,
 
88
                &option,
 
89
                checkbox).center();
 
90
 
 
91
        } else if( QRadioButton* radiobutton = qobject_cast<QRadioButton*>( receiver ) ) {
 
92
 
 
93
            QStyleOptionButton option;
 
94
            option.initFrom( radiobutton );
 
95
            position = radiobutton->style()->subElementRect(
 
96
                QStyle::SE_RadioButtonIndicator,
 
97
                &option,
 
98
                radiobutton).center();
 
99
 
 
100
        } else if( const QMdiSubWindow* window = qobject_cast<QMdiSubWindow*>( receiver ) ) {
 
101
 
 
102
            QStyleOptionTitleBar option;
 
103
            option.initFrom( window );
 
104
            int titleBarHeight( window->style()->pixelMetric( QStyle::PM_TitleBarHeight, &option, window ) );
 
105
            QRect titleBarRect( QPoint(0,0), QSize( window->width(), titleBarHeight ) );
 
106
            if( !titleBarRect.isValid() ) return;
 
107
            position = titleBarRect.center();
 
108
 
 
109
        } else {
 
110
 
 
111
            position = receiver->rect().center();
 
112
 
 
113
        }
 
114
 
 
115
        click( receiver, position, delay );
 
116
 
 
117
    }
 
118
 
 
119
    //_______________________________________________________________________
 
120
    void Simulator::click( QWidget* receiver, const QPoint& position, int delay )
 
121
    {
 
122
        Event event( Event::Click, receiver, delay );
 
123
        event._position = position;
 
124
        _events.push_back( event );
 
125
    }
 
126
 
 
127
    //_______________________________________________________________________
 
128
    void Simulator::slide( QWidget* receiver, const QPoint& position, int delay )
 
129
    {
 
130
        Event event( Event::Slide, receiver, delay );
 
131
        event._position = position;
 
132
        _events.push_back( event );
 
133
 
 
134
    }
 
135
 
 
136
    //_______________________________________________________________________
 
137
    void Simulator::selectItem( QWidget* receiver, int row, int column, int delay )
 
138
    {
 
139
        Event event( Event::SelectItem, receiver, delay );
 
140
        event._position = QPoint( column, row );
 
141
        _events.push_back( event );
 
142
    }
 
143
 
 
144
    //_______________________________________________________________________
 
145
    void Simulator::selectComboBoxItem( QWidget* receiver, int index, int delay )
 
146
    {
 
147
        Event event( Event::SelectComboBoxItem, receiver, delay );
 
148
        event._position.setX( index );
 
149
        _events.push_back( event );
 
150
    }
 
151
 
 
152
    //_______________________________________________________________________
 
153
    void Simulator::selectMenuItem( QWidget* receiver, int index, int delay )
 
154
    {
 
155
        Event event( Event::SelectMenuItem, receiver, delay );
 
156
        event._position.setX( index );
 
157
        _events.push_back( event );
 
158
    }
 
159
 
 
160
    //_______________________________________________________________________
 
161
    void Simulator::selectTab( QTabWidget* tabwidget, int index, int delay )
 
162
    {
 
163
        foreach( QObject* child, tabwidget->children() )
 
164
        {
 
165
            if( QTabBar* tabbar = qobject_cast<QTabBar*>( child ) )
 
166
            {
 
167
                selectTab( tabbar, index, delay );
 
168
                break;
 
169
            }
 
170
        }
 
171
    }
 
172
 
 
173
    //_______________________________________________________________________
 
174
    void Simulator::selectTab( QTabBar* receiver, int index, int delay )
 
175
    {
 
176
        Event event( Event::SelectTab, receiver, delay );
 
177
        event._position.setX( index );
 
178
        _events.push_back( event );
 
179
    }
 
180
 
 
181
    //_______________________________________________________________________
 
182
    void Simulator::writeText( QWidget* receiver, QString text, int delay )
 
183
    {
 
184
        Event event( Event::WriteText, receiver, delay );
 
185
        event._text = text;
 
186
        _events.push_back( event );
 
187
    }
 
188
 
 
189
    //_______________________________________________________________________
 
190
    void Simulator::clearText( QWidget* receiver, int delay )
 
191
    { _events.push_back( Event( Event::ClearText, receiver, delay ) ); }
 
192
 
 
193
    //_______________________________________________________________________
 
194
    void Simulator::run( void )
 
195
    {
 
196
 
 
197
        if( _events.isEmpty() ) return;
 
198
 
 
199
        // clear abort state
 
200
        _aborted = false;
 
201
 
 
202
        emit stateChanged( true );
 
203
        foreach( const Event& event, _events )
 
204
        {
 
205
            if( _aborted )
 
206
            {
 
207
                _events.clear();
 
208
                return;
 
209
            }
 
210
 
 
211
            processEvent( event );
 
212
        }
 
213
 
 
214
        // add last event to reset previousWidget and previousPosition
 
215
        if( _previousWidget )
 
216
        {
 
217
 
 
218
            postEvent( _previousWidget.data(), QEvent::Leave );
 
219
            if( _previousWidget.data()->testAttribute( Qt::WA_Hover ) )
 
220
            {
 
221
                const QPoint oldPosition( _previousWidget.data()->mapFromGlobal( _previousPosition ) );
 
222
                const QPoint newPosition( _previousWidget.data()->mapFromGlobal( QPoint( -1, -1 ) ) );
 
223
                postHoverEvent( _previousWidget.data(), QEvent::HoverLeave, newPosition, oldPosition );
 
224
            }
 
225
 
 
226
            _previousWidget.clear();
 
227
            _previousPosition = QPoint(-1, -1 );
 
228
 
 
229
        }
 
230
 
 
231
        _events.clear();
 
232
        emit stateChanged( false );
 
233
 
 
234
        return;
 
235
    }
 
236
 
 
237
    //_______________________________________________________________________
 
238
    void Simulator::abort( void )
 
239
    {
 
240
        _aborted = true;
 
241
        emit stateChanged( true );
 
242
 
 
243
    }
 
244
 
 
245
    //_______________________________________________________________________
 
246
    void Simulator::timerEvent( QTimerEvent* event )
 
247
    {
 
248
        if( event->timerId() == _timer.timerId() )
 
249
        {
 
250
 
 
251
            _timer.stop();
 
252
 
 
253
        } else if( event->timerId() == _pendingEventsTimer.timerId() ) {
 
254
 
 
255
            if( _aborted )
 
256
            {
 
257
 
 
258
                foreach( QEvent* event, _pendingEvents )
 
259
                { delete event; }
 
260
 
 
261
                _pendingEvents.clear();
 
262
                _pendingWidget.clear();
 
263
 
 
264
            } else if( _pendingWidget && _pendingWidget.data()->isVisible() ) {
 
265
 
 
266
                _pendingEventsTimer.stop();
 
267
                foreach( QEvent* event, _pendingEvents )
 
268
                {
 
269
 
 
270
                    if( event->type() == QEvent::MouseMove )
 
271
                    {
 
272
                        QPoint position( static_cast<QMouseEvent*>( event )->pos() );
 
273
                        moveCursor( _pendingWidget.data()->mapToGlobal( position ) );
 
274
                    }
 
275
 
 
276
                    postQEvent( _pendingWidget.data(), event );
 
277
                    postDelay( 150 );
 
278
 
 
279
                }
 
280
 
 
281
                _pendingEvents.clear();
 
282
                _pendingWidget.clear();
 
283
 
 
284
            }
 
285
 
 
286
        } else return QObject::timerEvent( event );
 
287
 
 
288
    }
 
289
 
 
290
    //_______________________________________________________________________
 
291
    void Simulator::processEvent( const Event& event )
 
292
    {
 
293
 
 
294
        if( _aborted ) return;
 
295
        if( !event._receiver )
 
296
        {
 
297
 
 
298
            if( event._type == Event::Wait )
 
299
            { postDelay( event._delay ); }
 
300
 
 
301
            return;
 
302
 
 
303
        }
 
304
 
 
305
        QWidget* receiver( event._receiver.data() );
 
306
        switch( event._type )
 
307
        {
 
308
 
 
309
            // click event
 
310
            case Event::Click:
 
311
            {
 
312
 
 
313
                // enter widget or move cursor to relevant position
 
314
                if( !enter( receiver, event._position, event._delay ) )
 
315
                {
 
316
                    moveCursor( receiver->mapToGlobal( event._position ) );
 
317
                    postMouseEvent( receiver, QEvent::MouseMove, Qt::NoButton, event._position );
 
318
                }
 
319
 
 
320
                postMouseClickEvent( receiver, Qt::LeftButton, event._position );
 
321
                break;
 
322
            }
 
323
 
 
324
            // slide
 
325
            case Event::Slide:
 
326
            {
 
327
                const QPoint& delta( event._position );
 
328
 
 
329
                // calculate begin position depending on widget type
 
330
                QPoint begin;
 
331
                if( const QSlider* slider = qobject_cast<QSlider*>( receiver ) )
 
332
                {
 
333
 
 
334
                    // this is copied from QSlider::initStyleOption
 
335
                    QStyleOptionSlider option;
 
336
                    option.initFrom( slider );
 
337
                    option.orientation = slider->orientation();
 
338
                    option.sliderPosition = slider->sliderPosition();
 
339
                    option.minimum = slider->minimum();
 
340
                    option.maximum = slider->maximum();
 
341
                    option.upsideDown = (slider->orientation() == Qt::Horizontal) ?
 
342
                        ( slider->invertedAppearance() != (option.direction == Qt::RightToLeft))
 
343
                        : (!slider->invertedAppearance() );
 
344
 
 
345
                    QRect handleRect( slider->style()->subControlRect(
 
346
                        QStyle::CC_Slider, &option, QStyle::SC_SliderHandle,
 
347
                        slider ) );
 
348
 
 
349
                    if( !handleRect.isValid() ) break;
 
350
                    begin = handleRect.center();
 
351
 
 
352
                } else if( const QScrollBar* scrollbar = qobject_cast<QScrollBar*>( receiver ) ) {
 
353
 
 
354
                    // this is copied from QSlider::initStyleOption
 
355
                    QStyleOptionSlider option;
 
356
                    option.initFrom( scrollbar );
 
357
                    option.orientation = scrollbar->orientation();
 
358
                    option.sliderPosition = scrollbar->sliderPosition();
 
359
                    option.minimum = scrollbar->minimum();
 
360
                    option.maximum = scrollbar->maximum();
 
361
                    option.upsideDown = scrollbar->invertedAppearance();
 
362
                    if( scrollbar->orientation() == Qt::Horizontal )
 
363
                    { option.state |= QStyle::State_Horizontal; }
 
364
 
 
365
                    QRect handleRect( scrollbar->style()->subControlRect(
 
366
                        QStyle::CC_ScrollBar, &option, QStyle::SC_ScrollBarSlider,
 
367
                        scrollbar ) );
 
368
 
 
369
                    if( !handleRect.isValid() ) break;
 
370
                    begin = handleRect.center();
 
371
 
 
372
                } else if( const QMdiSubWindow* window = qobject_cast<QMdiSubWindow*>( receiver ) ) {
 
373
 
 
374
                    QStyleOptionTitleBar option;
 
375
                    option.initFrom( window );
 
376
                    int titleBarHeight( window->style()->pixelMetric( QStyle::PM_TitleBarHeight, &option, window ) );
 
377
                    QRect titleBarRect( QPoint(0,0), QSize( window->width(), titleBarHeight ) );
 
378
                    if( !titleBarRect.isValid() ) break;
 
379
                    begin = titleBarRect.center();
 
380
 
 
381
                } else {
 
382
 
 
383
                    begin = receiver->rect().center();
 
384
 
 
385
                }
 
386
 
 
387
                // enter widget or move cursor to relevant position
 
388
                if( !enter( receiver, begin, event._delay ) )
 
389
                {
 
390
                    moveCursor( receiver->mapToGlobal( begin ) );
 
391
                    postMouseEvent( receiver, QEvent::MouseMove, Qt::NoButton, begin );
 
392
                }
 
393
 
 
394
                const QPoint end( begin + delta );
 
395
                postMouseEvent( receiver, QEvent::MouseButtonPress, Qt::LeftButton, begin, Qt::LeftButton );
 
396
                setFocus( receiver );
 
397
                postDelay( 50 );
 
398
                const int steps = 10;
 
399
                for( int i=0; i<steps; ++i )
 
400
                {
 
401
                    QPoint current(
 
402
                        begin.x() + qreal((i+1)*( end.x()-begin.x() ))/steps,
 
403
                        begin.y() + qreal((i+1)*( end.y()-begin.y() ))/steps );
 
404
                    moveCursor( receiver->mapToGlobal( current ), 1 );
 
405
                    postMouseEvent( receiver, QEvent::MouseMove, Qt::NoButton, current, Qt::LeftButton, Qt::NoModifier );
 
406
                    postDelay( 20 );
 
407
                }
 
408
 
 
409
                postMouseEvent( receiver, QEvent::MouseButtonRelease, Qt::LeftButton, end );
 
410
                break;
 
411
            }
 
412
 
 
413
            case Event::SelectItem:
 
414
            {
 
415
 
 
416
                const QAbstractItemView* view = qobject_cast<QAbstractItemView*>( receiver );
 
417
                if( !( view && view->model() ) ) break;
 
418
 
 
419
                const int column( event._position.x() );
 
420
                const int row( event._position.y() );
 
421
 
 
422
                // find index
 
423
                const QModelIndex modelIndex( view->model()->index( row, column ) );
 
424
                if( !modelIndex.isValid() ) break;
 
425
 
 
426
                // get rect
 
427
                QRect r( view->visualRect( modelIndex ) );
 
428
                if( !r.isValid() ) break;
 
429
 
 
430
                // enter widget or move cursor to relevant position
 
431
                const QPoint position( r.center() );
 
432
                if( !enter( view->viewport(), position, event._delay ) )
 
433
                {
 
434
                    moveCursor( view->viewport()->mapToGlobal( position ) );
 
435
                    postMouseEvent( view->viewport(), QEvent::MouseMove, Qt::NoButton, position );
 
436
                    postDelay( event._delay );
 
437
                }
 
438
 
 
439
                postMouseClickEvent( view->viewport(), Qt::LeftButton, position );
 
440
                break;
 
441
 
 
442
            }
 
443
 
 
444
            case Event::SelectComboBoxItem:
 
445
            {
 
446
 
 
447
                QComboBox* combobox = qobject_cast<QComboBox*>( receiver );
 
448
                if( !combobox ) break;
 
449
 
 
450
                // get arrow rect
 
451
                QStyleOptionComboBox option;
 
452
                option.initFrom( combobox );
 
453
                QRect arrowRect( combobox->style()->subControlRect( QStyle::CC_ComboBox, &option, QStyle::SC_ComboBoxArrow, combobox ) );
 
454
 
 
455
                // enter widget or move cursor to relevant position
 
456
                QPoint position( arrowRect.center() );
 
457
                if( !enter( combobox, position, event._delay ) )
 
458
                {
 
459
                    moveCursor( combobox->mapToGlobal( position ) );
 
460
                    postMouseEvent( combobox, QEvent::MouseMove, Qt::NoButton, position );
 
461
                    postDelay( event._delay );
 
462
                }
 
463
 
 
464
                postMouseClickEvent( combobox, Qt::LeftButton, position );
 
465
 
 
466
                // select item in view
 
467
                QAbstractItemView* view = combobox->view();
 
468
                const int row( event._position.x() );
 
469
                const int column( 0 );
 
470
 
 
471
                // find index
 
472
                const QModelIndex modelIndex( view->model()->index( row, column ) );
 
473
                if( !modelIndex.isValid() ) break;
 
474
 
 
475
                // get rect
 
476
                QRect r( view->visualRect( modelIndex ) );
 
477
                if( !r.isValid() ) break;
 
478
 
 
479
                // send event
 
480
                position = QPoint( r.center() );
 
481
                moveCursor( view->viewport()->mapToGlobal( position ) );
 
482
                postMouseEvent( view->viewport(), QEvent::MouseMove, Qt::NoButton, position, Qt::NoButton, Qt::NoModifier );
 
483
                postDelay(100);
 
484
                postMouseClickEvent( view->viewport(), Qt::LeftButton, position );
 
485
                break;
 
486
 
 
487
            }
 
488
 
 
489
 
 
490
            case Event::SelectMenuItem:
 
491
            {
 
492
 
 
493
                // retrieve menu
 
494
                QMenu* menu( 0 );
 
495
                if( const QToolButton* button = qobject_cast<QToolButton*>( receiver ) ) menu = button->menu();
 
496
                else if( const QPushButton* button = qobject_cast<QPushButton*>( receiver ) ) menu = button->menu();
 
497
 
 
498
                // abort if not found
 
499
                if( !menu ) break;
 
500
 
 
501
                // get action and geometry
 
502
                const int row( event._position.x() );
 
503
                QList<QAction*> actions( menu->actions() );
 
504
                if( row >= actions.size() ) break;
 
505
 
 
506
                menu->sizeHint();
 
507
                QRect r( menu->actionGeometry( actions[row] ) );
 
508
                if( !r.isValid() ) break;
 
509
 
 
510
                /*!
 
511
                HACK: As soon as leftMouseButton is pressed on a button with menu,
 
512
                the menu is shown and code is interrupted until an action is selected in the menu.
 
513
                As a consequence, one must first generate the events, execute them with a delay, and then
 
514
                click on the button (before delay is expired). This way, the menu events will be executed
 
515
                even if the menu is visible (and blocking further code execution).
 
516
                */
 
517
                QPoint position( r.center() );
 
518
                _pendingWidget = menu;
 
519
                _pendingEvents.push_back( new QMouseEvent(
 
520
                    QEvent::MouseMove,
 
521
                    position,
 
522
                    Qt::NoButton,
 
523
                    Qt::NoButton,
 
524
                    Qt::NoModifier ) );
 
525
 
 
526
                _pendingEvents.push_back( new QMouseEvent(
 
527
                    QEvent::MouseButtonPress,
 
528
                    position,
 
529
                    Qt::LeftButton,
 
530
                    Qt::NoButton,
 
531
                    Qt::NoModifier ) );
 
532
 
 
533
                _pendingEvents.push_back( new QMouseEvent(
 
534
                    QEvent::MouseButtonRelease,
 
535
                    position,
 
536
                    Qt::LeftButton,
 
537
                    Qt::NoButton,
 
538
                    Qt::NoModifier ) );
 
539
 
 
540
                _pendingEventsTimer.start( 10, this );
 
541
 
 
542
                // enter widget or move cursor to relevant position
 
543
                position = receiver->rect().center();
 
544
                if( !enter( receiver, position, event._delay ) )
 
545
                {
 
546
                    moveCursor( receiver->mapToGlobal( position ) );
 
547
                    postMouseEvent( receiver, QEvent::MouseMove, Qt::NoButton, position );
 
548
                    postDelay( event._delay );
 
549
                }
 
550
 
 
551
                // click
 
552
                postMouseEvent( receiver, QEvent::MouseButtonPress, Qt::LeftButton, position, Qt::NoButton, Qt::NoModifier );
 
553
                break;
 
554
 
 
555
            }
 
556
 
 
557
            case Event::SelectTab:
 
558
            {
 
559
 
 
560
                const QTabBar* tabbar = qobject_cast<QTabBar*>( receiver );
 
561
                if( !tabbar ) break;
 
562
 
 
563
                const int index( event._position.x() );
 
564
 
 
565
                const QRect r( tabbar->tabRect( index ) );
 
566
                if( !r.isValid() ) break;
 
567
 
 
568
                // enter widget or move cursor to relevant position
 
569
                const QPoint position( r.center() );
 
570
                if( !enter( receiver, position, event._delay ) )
 
571
                {
 
572
                    moveCursor( receiver->mapToGlobal( position ) );
 
573
                    postMouseEvent( receiver, QEvent::MouseMove, Qt::NoButton, position );
 
574
                    postDelay( event._delay );
 
575
                }
 
576
 
 
577
                postMouseClickEvent( receiver, Qt::LeftButton, position );
 
578
                break;
 
579
 
 
580
            }
 
581
 
 
582
            case Event::WriteText:
 
583
            {
 
584
 
 
585
                enter( receiver, receiver->rect().center(), event._delay );
 
586
                setFocus( receiver );
 
587
                const QString& text( event._text );
 
588
                for( int i=0; i < text.length(); ++i )
 
589
                {
 
590
                    const Qt::Key key( toKey( text.at(i) ) );
 
591
                    const QString local( text.at(i) );
 
592
                    postKeyEvent( receiver, QEvent::KeyPress, key, local, Qt::NoModifier );
 
593
                    postKeyEvent( receiver, QEvent::KeyRelease, key, local, Qt::NoModifier );
 
594
                    postDelay( 20 );
 
595
                }
 
596
                break;
 
597
 
 
598
            }
 
599
 
 
600
            case Event::ClearText:
 
601
            {
 
602
 
 
603
                enter( receiver, receiver->rect().center(), event._delay );
 
604
                setFocus( receiver );
 
605
                postKeyEvent( receiver, QEvent::KeyPress, Qt::Key_A, "a", Qt::ControlModifier );
 
606
                postKeyEvent( receiver, QEvent::KeyRelease, Qt::Key_A, "a", Qt::ControlModifier );
 
607
                postDelay( 20 );
 
608
                postKeyClickEvent( receiver, Qt::Key_Backspace, QString() );
 
609
 
 
610
            }
 
611
 
 
612
            default: break;
 
613
 
 
614
        }
 
615
 
 
616
        // delay
 
617
        postDelay( event._delay );
 
618
 
 
619
        return;
 
620
 
 
621
    }
 
622
 
 
623
    //_______________________________________________________________________
 
624
    void Simulator::postEvent( QWidget* receiver, QEvent::Type type ) const
 
625
    { postQEvent( receiver, new QEvent( type ) ); }
 
626
 
 
627
    //_______________________________________________________________________
 
628
    void Simulator::postHoverEvent( QWidget* receiver, QEvent::Type type, const QPoint& newPosition, const QPoint& oldPosition ) const
 
629
    { postQEvent( receiver, new QHoverEvent( type, newPosition, oldPosition ) ); }
 
630
 
 
631
 
 
632
    //_______________________________________________________________________
 
633
    bool Simulator::enter( QWidget* receiver, const QPoint& position, int delay )
 
634
    {
 
635
 
 
636
        if( receiver == _previousWidget.data() ) return false;
 
637
 
 
638
        // store position
 
639
        moveCursor( receiver->mapToGlobal( position ) );
 
640
 
 
641
        // leave previous widget
 
642
        if( _previousWidget )
 
643
        {
 
644
            postEvent( _previousWidget.data(), QEvent::Leave );
 
645
            if( _previousWidget.data()->testAttribute( Qt::WA_Hover ) )
 
646
            {
 
647
                const QPoint oldPosition( _previousWidget.data()->mapFromGlobal( _previousPosition ) );
 
648
                const QPoint newPosition( _previousWidget.data()->mapFromGlobal( receiver->mapToGlobal( position ) ) );
 
649
                postHoverEvent( _previousWidget.data(), QEvent::HoverLeave, newPosition, oldPosition );
 
650
            }
 
651
        }
 
652
 
 
653
        // enter or move in current widget
 
654
        if( !receiver->rect().contains( receiver->mapFromGlobal( _previousPosition ) ) )
 
655
        {
 
656
 
 
657
            // enter current widget if needed
 
658
            postEvent( receiver, QEvent::Enter );
 
659
            if( receiver->testAttribute( Qt::WA_Hover ) )
 
660
            {
 
661
                const QPoint oldPosition( receiver->mapFromGlobal( _previousPosition ) );
 
662
                const QPoint newPosition( position );
 
663
                postHoverEvent( receiver, QEvent::HoverEnter, newPosition, oldPosition );
 
664
            }
 
665
 
 
666
        } else if( receiver->mapFromGlobal( _previousPosition ) != position ) {
 
667
 
 
668
            // move mouse if needed
 
669
            postMouseEvent( receiver, QEvent::MouseMove, Qt::NoButton, position );
 
670
            if( receiver->testAttribute( Qt::WA_Hover ) )
 
671
            {
 
672
                const QPoint oldPosition( receiver->mapFromGlobal( _previousPosition ) );
 
673
                const QPoint newPosition( position );
 
674
                postHoverEvent( receiver, QEvent::HoverMove, newPosition, oldPosition );
 
675
            }
 
676
 
 
677
        }
 
678
 
 
679
        // update previous widget and position
 
680
        _previousWidget = receiver;
 
681
        _previousPosition = receiver->mapToGlobal( position );
 
682
        postDelay( delay );
 
683
 
 
684
        return true;
 
685
 
 
686
    }
 
687
 
 
688
    //_______________________________________________________________________
 
689
    void Simulator::postMouseClickEvent( QWidget* receiver, Qt::MouseButton button, const QPoint& position  )
 
690
    {
 
691
 
 
692
        // button press and button release
 
693
        postMouseEvent( receiver, QEvent::MouseButtonPress, button, position, button  );
 
694
        setFocus( receiver );
 
695
        postDelay(50);
 
696
        postMouseEvent( receiver, QEvent::MouseButtonRelease, button, position, button );
 
697
 
 
698
    }
 
699
 
 
700
    //_______________________________________________________________________
 
701
    void Simulator::postMouseEvent(
 
702
        QWidget* receiver,
 
703
        QEvent::Type type,
 
704
        Qt::MouseButton button,
 
705
        const QPoint& position,
 
706
        Qt::MouseButtons buttons,
 
707
        Qt::KeyboardModifiers modifiers ) const
 
708
    {
 
709
        postQEvent( receiver, new QMouseEvent(
 
710
            type,
 
711
            position,
 
712
            receiver->mapToGlobal( position ),
 
713
            button,
 
714
            buttons,
 
715
            modifiers ) );
 
716
    }
 
717
 
 
718
    //_______________________________________________________________________
 
719
    void Simulator::postKeyClickEvent( QWidget* receiver, Qt::Key key, QString text, Qt::KeyboardModifiers modifiers ) const
 
720
    {
 
721
        postKeyModifiersEvent( receiver, QEvent::KeyPress, modifiers );
 
722
        postKeyEvent( receiver, QEvent::KeyPress, key, text, modifiers );
 
723
        postKeyEvent( receiver, QEvent::KeyRelease, key, text, modifiers );
 
724
        postKeyModifiersEvent( receiver, QEvent::KeyRelease, modifiers );
 
725
 
 
726
    }
 
727
 
 
728
    //_______________________________________________________________________
 
729
    void Simulator::postKeyModifiersEvent( QWidget* receiver, QEvent::Type type, Qt::KeyboardModifiers modifiers ) const
 
730
    {
 
731
 
 
732
        if( modifiers == Qt::NoModifier ) return;
 
733
 
 
734
        switch( type )
 
735
        {
 
736
 
 
737
            case QEvent::KeyPress:
 
738
            {
 
739
                if( modifiers & Qt::ShiftModifier)
 
740
                { postKeyEvent( receiver, QEvent::KeyPress, Qt::Key_Shift, QString() ); }
 
741
 
 
742
                if( modifiers & Qt::ControlModifier )
 
743
                { postKeyEvent( receiver, QEvent::KeyPress, Qt::Key_Control, QString(), modifiers & Qt::ShiftModifier ); }
 
744
 
 
745
                if( modifiers & Qt::AltModifier )
 
746
                { postKeyEvent( receiver, QEvent::KeyPress, Qt::Key_Alt, QString(), modifiers & (Qt::ShiftModifier|Qt::ControlModifier) ); }
 
747
 
 
748
                if( modifiers & Qt::MetaModifier )
 
749
                { postKeyEvent( receiver, QEvent::KeyPress, Qt::Key_Meta, QString(), modifiers & (Qt::ShiftModifier|Qt::ControlModifier|Qt::AltModifier) ); }
 
750
 
 
751
                break;
 
752
 
 
753
            }
 
754
 
 
755
            case QEvent::KeyRelease:
 
756
            {
 
757
 
 
758
                if( modifiers & Qt::MetaModifier )
 
759
                { postKeyEvent( receiver, QEvent::KeyRelease, Qt::Key_Meta, QString() ); }
 
760
 
 
761
                if( modifiers & Qt::AltModifier )
 
762
                { postKeyEvent( receiver, QEvent::KeyRelease, Qt::Key_Alt, QString(), modifiers & Qt::MetaModifier ); }
 
763
 
 
764
                if( modifiers & Qt::ControlModifier )
 
765
                { postKeyEvent( receiver, QEvent::KeyRelease, Qt::Key_Control, QString(), modifiers & (Qt::MetaModifier|Qt::AltModifier) ); }
 
766
 
 
767
                if( modifiers & Qt::ShiftModifier)
 
768
                { postKeyEvent( receiver, QEvent::KeyRelease, Qt::Key_Shift, QString(), modifiers & (Qt::MetaModifier|Qt::AltModifier|Qt::ControlModifier) ); }
 
769
 
 
770
            }
 
771
 
 
772
            default: break;
 
773
        }
 
774
 
 
775
    }
 
776
 
 
777
    //_______________________________________________________________________
 
778
    void Simulator::postKeyEvent( QWidget* receiver, QEvent::Type type, Qt::Key key, QString text, Qt::KeyboardModifiers modifiers ) const
 
779
    { postQEvent( receiver, new QKeyEvent( type, key, modifiers, text ) ); }
 
780
 
 
781
    //_______________________________________________________________________
 
782
    void Simulator::postDelay( int delay )
 
783
    {
 
784
 
 
785
        // check value
 
786
        if( delay == -1 ) delay = _defaultDelay;
 
787
        if( delay <= 0 ) return;
 
788
 
 
789
        // this is largely inspired from qtestlib's qsleep implementation
 
790
        _timer.start( delay, this );
 
791
        while( _timer.isActive() )
 
792
        {
 
793
 
 
794
            // flush events in loop
 
795
            QCoreApplication::processEvents(QEventLoop::AllEvents, delay);
 
796
            int ms( 10 );
 
797
 
 
798
            // sleep
 
799
            #ifdef Q_OS_WIN
 
800
            Sleep(uint(ms));
 
801
            #else
 
802
            struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
 
803
            nanosleep(&ts, NULL);
 
804
            #endif
 
805
 
 
806
        }
 
807
 
 
808
    }
 
809
 
 
810
    //_______________________________________________________________________
 
811
    void Simulator::moveCursor( const QPoint& position, int steps )
 
812
    {
 
813
 
 
814
        // do nothing if mouse grab is disabled
 
815
        if( !_grabMouse ) return;
 
816
        if( _aborted ) return;
 
817
        const QPoint begin( QCursor::pos() );
 
818
        const QPoint end( position );
 
819
        if( begin == end ) return;
 
820
 
 
821
        if( steps > 1 )
 
822
        {
 
823
 
 
824
            for( int i = 0; i<steps; ++i )
 
825
            {
 
826
                const QPoint current(
 
827
                    begin.x() + qreal((i+1)*( end.x()-begin.x() ))/steps,
 
828
                    begin.y() + qreal((i+1)*( end.y()-begin.y() ))/steps );
 
829
                QCursor::setPos( current );
 
830
                postDelay( 10 );
 
831
            }
 
832
 
 
833
        } else {
 
834
 
 
835
            QCursor::setPos( end );
 
836
 
 
837
        }
 
838
 
 
839
    }
 
840
 
 
841
    //_______________________________________________________________________
 
842
    void Simulator::setFocus( QWidget* receiver )
 
843
    {
 
844
        if( receiver->focusPolicy() != Qt::NoFocus )
 
845
        { receiver->setFocus(); }
 
846
    }
 
847
 
 
848
    //_______________________________________________________________________
 
849
    Qt::Key Simulator::toKey( QChar a ) const
 
850
    { return (Qt::Key) QKeySequence( a )[0]; }
 
851
 
 
852
    //_______________________________________________________________________
 
853
    void Simulator::postQEvent( QWidget* receiver, QEvent* event ) const
 
854
    {
 
855
        if( _aborted ) delete event;
 
856
        else qApp->postEvent( receiver, event );
 
857
    }
 
858
}