File indexing completed on 2024-05-19 05:35:24
0001 // krazy:excludeall=qclasses 0002 0003 ////////////////////////////////////////////////////////////////////////////// 0004 // oxygensimulator.cpp 0005 // simulates event chain passed to the application 0006 // ------------------- 0007 // 0008 // SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr> 0009 // 0010 // SPDX-License-Identifier: MIT 0011 ////////////////////////////////////////////////////////////////////////////// 0012 0013 #include "oxygensimulator.h" 0014 0015 #include <QAbstractItemView> 0016 #include <QApplication> 0017 #include <QCheckBox> 0018 #include <QComboBox> 0019 #include <QCursor> 0020 #include <QFocusEvent> 0021 #include <QHoverEvent> 0022 #include <QMdiSubWindow> 0023 #include <QMenu> 0024 #include <QMouseEvent> 0025 #include <QPushButton> 0026 #include <QRadioButton> 0027 #include <QScrollBar> 0028 #include <QSlider> 0029 #include <QStyle> 0030 #include <QStyleOptionComboBox> 0031 #include <QStyleOptionSlider> 0032 #include <QToolButton> 0033 0034 #ifdef Q_OS_WIN 0035 /* need windows.h include for Sleep function*/ 0036 #include <windows.h> 0037 #endif 0038 0039 #ifdef Q_OS_UNIX 0040 #include <ctime> 0041 #endif 0042 0043 namespace Oxygen 0044 { 0045 //_______________________________________________________________________ 0046 bool Simulator::_grabMouse = true; 0047 int Simulator::_defaultDelay = 250; 0048 0049 //_______________________________________________________________________ 0050 void Simulator::wait(int delay) 0051 { 0052 _events.append(Event(Event::Wait, nullptr, delay)); 0053 } 0054 0055 //_______________________________________________________________________ 0056 void Simulator::click(QWidget *receiver, int delay) 0057 { 0058 QPoint position; 0059 if (QCheckBox *checkbox = qobject_cast<QCheckBox *>(receiver)) { 0060 QStyleOptionButton option; 0061 option.initFrom(checkbox); 0062 position = checkbox->style()->subElementRect(QStyle::SE_CheckBoxIndicator, &option, checkbox).center(); 0063 0064 } else if (QRadioButton *radiobutton = qobject_cast<QRadioButton *>(receiver)) { 0065 QStyleOptionButton option; 0066 option.initFrom(radiobutton); 0067 position = radiobutton->style()->subElementRect(QStyle::SE_RadioButtonIndicator, &option, radiobutton).center(); 0068 0069 } else if (const QMdiSubWindow *window = qobject_cast<QMdiSubWindow *>(receiver)) { 0070 QStyleOptionTitleBar option; 0071 option.initFrom(window); 0072 int titleBarHeight(window->style()->pixelMetric(QStyle::PM_TitleBarHeight, &option, window)); 0073 QRect titleBarRect(QPoint(0, 0), QSize(window->width(), titleBarHeight)); 0074 if (!titleBarRect.isValid()) 0075 return; 0076 position = titleBarRect.center(); 0077 0078 } else { 0079 position = receiver->rect().center(); 0080 } 0081 0082 click(receiver, position, delay); 0083 } 0084 0085 //_______________________________________________________________________ 0086 void Simulator::click(QWidget *receiver, const QPoint &position, int delay) 0087 { 0088 Event event(Event::Click, receiver, delay); 0089 event._position = position; 0090 _events.append(event); 0091 } 0092 0093 //_______________________________________________________________________ 0094 void Simulator::slide(QWidget *receiver, const QPoint &position, int delay) 0095 { 0096 Event event(Event::Slide, receiver, delay); 0097 event._position = position; 0098 _events.append(event); 0099 } 0100 0101 //_______________________________________________________________________ 0102 void Simulator::selectItem(QWidget *receiver, int row, int column, int delay) 0103 { 0104 Event event(Event::SelectItem, receiver, delay); 0105 event._position = QPoint(column, row); 0106 _events.append(event); 0107 } 0108 0109 //_______________________________________________________________________ 0110 void Simulator::selectComboBoxItem(QWidget *receiver, int index, int delay) 0111 { 0112 Event event(Event::SelectComboBoxItem, receiver, delay); 0113 event._position.setX(index); 0114 _events.append(event); 0115 } 0116 0117 //_______________________________________________________________________ 0118 void Simulator::selectMenuItem(QWidget *receiver, int index, int delay) 0119 { 0120 Event event(Event::SelectMenuItem, receiver, delay); 0121 event._position.setX(index); 0122 _events.append(event); 0123 } 0124 0125 //_______________________________________________________________________ 0126 void Simulator::selectTab(QTabWidget *tabwidget, int index, int delay) 0127 { 0128 const auto children = tabwidget->children(); 0129 for (QObject *child : children) { 0130 if (QTabBar *tabbar = qobject_cast<QTabBar *>(child)) { 0131 selectTab(tabbar, index, delay); 0132 break; 0133 } 0134 } 0135 } 0136 0137 //_______________________________________________________________________ 0138 void Simulator::selectTab(QTabBar *receiver, int index, int delay) 0139 { 0140 Event event(Event::SelectTab, receiver, delay); 0141 event._position.setX(index); 0142 _events.append(event); 0143 } 0144 0145 //_______________________________________________________________________ 0146 void Simulator::writeText(QWidget *receiver, QString text, int delay) 0147 { 0148 Event event(Event::WriteText, receiver, delay); 0149 event._text = text; 0150 _events.append(event); 0151 } 0152 0153 //_______________________________________________________________________ 0154 void Simulator::clearText(QWidget *receiver, int delay) 0155 { 0156 _events.append(Event(Event::ClearText, receiver, delay)); 0157 } 0158 0159 //_______________________________________________________________________ 0160 void Simulator::run(void) 0161 { 0162 if (_events.isEmpty()) 0163 return; 0164 0165 // clear abort state 0166 _aborted = false; 0167 0168 emit stateChanged(true); 0169 0170 for (const Event &event : std::as_const(_events)) { 0171 if (_aborted) { 0172 _events.clear(); 0173 return; 0174 } 0175 0176 processEvent(event); 0177 } 0178 0179 // add last event to reset previousWidget and previousPosition 0180 if (_previousWidget) { 0181 postEvent(_previousWidget.data(), QEvent::Leave); 0182 if (_previousWidget.data()->testAttribute(Qt::WA_Hover)) { 0183 const QPoint oldPosition(_previousWidget.data()->mapFromGlobal(_previousPosition)); 0184 const QPoint newPosition(_previousWidget.data()->mapFromGlobal(QPoint(-1, -1))); 0185 postHoverEvent(_previousWidget.data(), QEvent::HoverLeave, newPosition, oldPosition); 0186 } 0187 0188 _previousWidget.clear(); 0189 _previousPosition = QPoint(-1, -1); 0190 } 0191 0192 _events.clear(); 0193 emit stateChanged(false); 0194 0195 return; 0196 } 0197 0198 //_______________________________________________________________________ 0199 void Simulator::abort(void) 0200 { 0201 _aborted = true; 0202 emit stateChanged(true); 0203 } 0204 0205 //_______________________________________________________________________ 0206 void Simulator::timerEvent(QTimerEvent *event) 0207 { 0208 if (event->timerId() == _timer.timerId()) { 0209 _timer.stop(); 0210 0211 } else if (event->timerId() == _pendingEventsTimer.timerId()) { 0212 if (_aborted) { 0213 qDeleteAll(_pendingEvents); 0214 0215 _pendingEvents.clear(); 0216 _pendingWidget.clear(); 0217 0218 } else if (_pendingWidget && _pendingWidget.data()->isVisible()) { 0219 _pendingEventsTimer.stop(); 0220 for (QEvent *event : std::as_const(_pendingEvents)) { 0221 if (event->type() == QEvent::MouseMove) { 0222 QPoint position(static_cast<QMouseEvent *>(event)->pos()); 0223 moveCursor(_pendingWidget.data()->mapToGlobal(position)); 0224 } 0225 0226 postQEvent(_pendingWidget.data(), event); 0227 postDelay(150); 0228 } 0229 0230 _pendingEvents.clear(); 0231 _pendingWidget.clear(); 0232 } 0233 0234 } else 0235 return QObject::timerEvent(event); 0236 } 0237 0238 //_______________________________________________________________________ 0239 void Simulator::processEvent(const Event &event) 0240 { 0241 if (_aborted) 0242 return; 0243 if (!event._receiver) { 0244 if (event._type == Event::Wait) { 0245 postDelay(event._delay); 0246 } 0247 0248 return; 0249 } 0250 0251 QWidget *receiver(event._receiver.data()); 0252 switch (event._type) { 0253 // click event 0254 case Event::Click: { 0255 // enter widget or move cursor to relevant position 0256 if (!enter(receiver, event._position, event._delay)) { 0257 moveCursor(receiver->mapToGlobal(event._position)); 0258 postMouseEvent(receiver, QEvent::MouseMove, Qt::NoButton, event._position); 0259 } 0260 0261 postMouseClickEvent(receiver, Qt::LeftButton, event._position); 0262 break; 0263 } 0264 0265 // slide 0266 case Event::Slide: { 0267 const QPoint &delta(event._position); 0268 0269 // calculate begin position depending on widget type 0270 QPoint begin; 0271 if (const QSlider *slider = qobject_cast<QSlider *>(receiver)) { 0272 // this is copied from QSlider::initStyleOption 0273 QStyleOptionSlider option; 0274 option.initFrom(slider); 0275 option.orientation = slider->orientation(); 0276 option.sliderPosition = slider->sliderPosition(); 0277 option.minimum = slider->minimum(); 0278 option.maximum = slider->maximum(); 0279 option.upsideDown = (slider->orientation() == Qt::Horizontal) ? (slider->invertedAppearance() != (option.direction == Qt::RightToLeft)) 0280 : (!slider->invertedAppearance()); 0281 0282 QRect handleRect(slider->style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, slider)); 0283 0284 if (!handleRect.isValid()) 0285 break; 0286 begin = handleRect.center(); 0287 0288 } else if (const QScrollBar *scrollbar = qobject_cast<QScrollBar *>(receiver)) { 0289 // this is copied from QSlider::initStyleOption 0290 QStyleOptionSlider option; 0291 option.initFrom(scrollbar); 0292 option.orientation = scrollbar->orientation(); 0293 option.sliderPosition = scrollbar->sliderPosition(); 0294 option.minimum = scrollbar->minimum(); 0295 option.maximum = scrollbar->maximum(); 0296 option.upsideDown = scrollbar->invertedAppearance(); 0297 if (scrollbar->orientation() == Qt::Horizontal) { 0298 option.state |= QStyle::State_Horizontal; 0299 } 0300 0301 QRect handleRect(scrollbar->style()->subControlRect(QStyle::CC_ScrollBar, &option, QStyle::SC_ScrollBarSlider, scrollbar)); 0302 0303 if (!handleRect.isValid()) 0304 break; 0305 begin = handleRect.center(); 0306 0307 } else if (const QMdiSubWindow *window = qobject_cast<QMdiSubWindow *>(receiver)) { 0308 QStyleOptionTitleBar option; 0309 option.initFrom(window); 0310 int titleBarHeight(window->style()->pixelMetric(QStyle::PM_TitleBarHeight, &option, window)); 0311 QRect titleBarRect(QPoint(0, 0), QSize(window->width(), titleBarHeight)); 0312 if (!titleBarRect.isValid()) 0313 break; 0314 begin = titleBarRect.center(); 0315 0316 } else { 0317 begin = receiver->rect().center(); 0318 } 0319 0320 // enter widget or move cursor to relevant position 0321 if (!enter(receiver, begin, event._delay)) { 0322 moveCursor(receiver->mapToGlobal(begin)); 0323 postMouseEvent(receiver, QEvent::MouseMove, Qt::NoButton, begin); 0324 } 0325 0326 const QPoint end(begin + delta); 0327 postMouseEvent(receiver, QEvent::MouseButtonPress, Qt::LeftButton, begin, Qt::LeftButton); 0328 setFocus(receiver); 0329 postDelay(50); 0330 const int steps = 10; 0331 for (int i = 0; i < steps; ++i) { 0332 QPoint current(begin.x() + qreal((i + 1) * (end.x() - begin.x())) / steps, begin.y() + qreal((i + 1) * (end.y() - begin.y())) / steps); 0333 moveCursor(receiver->mapToGlobal(current), 1); 0334 postMouseEvent(receiver, QEvent::MouseMove, Qt::NoButton, current, Qt::LeftButton, Qt::NoModifier); 0335 postDelay(20); 0336 } 0337 0338 postMouseEvent(receiver, QEvent::MouseButtonRelease, Qt::LeftButton, end); 0339 break; 0340 } 0341 0342 case Event::SelectItem: { 0343 const QAbstractItemView *view = qobject_cast<QAbstractItemView *>(receiver); 0344 if (!(view && view->model())) 0345 break; 0346 0347 const int column(event._position.x()); 0348 const int row(event._position.y()); 0349 0350 // find index 0351 const QModelIndex modelIndex(view->model()->index(row, column)); 0352 if (!modelIndex.isValid()) 0353 break; 0354 0355 // get rect 0356 QRect r(view->visualRect(modelIndex)); 0357 if (!r.isValid()) 0358 break; 0359 0360 // enter widget or move cursor to relevant position 0361 const QPoint position(r.center()); 0362 if (!enter(view->viewport(), position, event._delay)) { 0363 moveCursor(view->viewport()->mapToGlobal(position)); 0364 postMouseEvent(view->viewport(), QEvent::MouseMove, Qt::NoButton, position); 0365 postDelay(event._delay); 0366 } 0367 0368 postMouseClickEvent(view->viewport(), Qt::LeftButton, position); 0369 break; 0370 } 0371 0372 case Event::SelectComboBoxItem: { 0373 QComboBox *combobox = qobject_cast<QComboBox *>(receiver); 0374 if (!combobox) 0375 break; 0376 0377 // get arrow rect 0378 QStyleOptionComboBox option; 0379 option.initFrom(combobox); 0380 QRect arrowRect(combobox->style()->subControlRect(QStyle::CC_ComboBox, &option, QStyle::SC_ComboBoxArrow, combobox)); 0381 0382 // enter widget or move cursor to relevant position 0383 QPoint position(arrowRect.center()); 0384 if (!enter(combobox, position, event._delay)) { 0385 moveCursor(combobox->mapToGlobal(position)); 0386 postMouseEvent(combobox, QEvent::MouseMove, Qt::NoButton, position); 0387 postDelay(event._delay); 0388 } 0389 0390 postMouseClickEvent(combobox, Qt::LeftButton, position); 0391 0392 // select item in view 0393 QAbstractItemView *view = combobox->view(); 0394 const int row(event._position.x()); 0395 const int column(0); 0396 0397 // find index 0398 const QModelIndex modelIndex(view->model()->index(row, column)); 0399 if (!modelIndex.isValid()) 0400 break; 0401 0402 // get rect 0403 QRect r(view->visualRect(modelIndex)); 0404 if (!r.isValid()) 0405 break; 0406 0407 // send event 0408 position = QPoint(r.center()); 0409 moveCursor(view->viewport()->mapToGlobal(position)); 0410 postMouseEvent(view->viewport(), QEvent::MouseMove, Qt::NoButton, position, Qt::NoButton, Qt::NoModifier); 0411 postDelay(100); 0412 postMouseClickEvent(view->viewport(), Qt::LeftButton, position); 0413 break; 0414 } 0415 0416 case Event::SelectMenuItem: { 0417 // retrieve menu 0418 QMenu *menu(nullptr); 0419 if (const QToolButton *button = qobject_cast<QToolButton *>(receiver)) 0420 menu = button->menu(); 0421 else if (const QPushButton *button = qobject_cast<QPushButton *>(receiver)) 0422 menu = button->menu(); 0423 0424 // abort if not found 0425 if (!menu) 0426 break; 0427 0428 // get action and geometry 0429 const int row(event._position.x()); 0430 QList<QAction *> actions(menu->actions()); 0431 if (row >= actions.size()) 0432 break; 0433 0434 menu->sizeHint(); 0435 QRect r(menu->actionGeometry(actions[row])); 0436 if (!r.isValid()) 0437 break; 0438 0439 /*! 0440 HACK: As soon as leftMouseButton is pressed on a button with menu, 0441 the menu is shown and code is interrupted until an action is selected in the menu. 0442 As a consequence, one must first generate the events, execute them with a delay, and then 0443 click on the button (before delay is expired). This way, the menu events will be executed 0444 even if the menu is visible (and blocking further code execution). 0445 */ 0446 QPoint position(r.center()); 0447 _pendingWidget = menu; 0448 _pendingEvents.append(new QMouseEvent(QEvent::MouseMove, position, Qt::NoButton, Qt::NoButton, Qt::NoModifier)); 0449 0450 _pendingEvents.append(new QMouseEvent(QEvent::MouseButtonPress, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); 0451 0452 _pendingEvents.append(new QMouseEvent(QEvent::MouseButtonRelease, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); 0453 0454 _pendingEventsTimer.start(10, this); 0455 0456 // enter widget or move cursor to relevant position 0457 position = receiver->rect().center(); 0458 if (!enter(receiver, position, event._delay)) { 0459 moveCursor(receiver->mapToGlobal(position)); 0460 postMouseEvent(receiver, QEvent::MouseMove, Qt::NoButton, position); 0461 postDelay(event._delay); 0462 } 0463 0464 // click 0465 postMouseEvent(receiver, QEvent::MouseButtonPress, Qt::LeftButton, position, Qt::NoButton, Qt::NoModifier); 0466 break; 0467 } 0468 0469 case Event::SelectTab: { 0470 const QTabBar *tabbar = qobject_cast<QTabBar *>(receiver); 0471 if (!tabbar) 0472 break; 0473 0474 const int index(event._position.x()); 0475 0476 const QRect r(tabbar->tabRect(index)); 0477 if (!r.isValid()) 0478 break; 0479 0480 // enter widget or move cursor to relevant position 0481 const QPoint position(r.center()); 0482 if (!enter(receiver, position, event._delay)) { 0483 moveCursor(receiver->mapToGlobal(position)); 0484 postMouseEvent(receiver, QEvent::MouseMove, Qt::NoButton, position); 0485 postDelay(event._delay); 0486 } 0487 0488 postMouseClickEvent(receiver, Qt::LeftButton, position); 0489 break; 0490 } 0491 0492 case Event::WriteText: { 0493 enter(receiver, receiver->rect().center(), event._delay); 0494 setFocus(receiver); 0495 const QString &text(event._text); 0496 for (int i = 0; i < text.length(); ++i) { 0497 const Qt::Key key(toKey(text.at(i))); 0498 const QString local(text.at(i)); 0499 postKeyEvent(receiver, QEvent::KeyPress, key, local, Qt::NoModifier); 0500 postKeyEvent(receiver, QEvent::KeyRelease, key, local, Qt::NoModifier); 0501 postDelay(20); 0502 } 0503 break; 0504 } 0505 0506 case Event::ClearText: { 0507 enter(receiver, receiver->rect().center(), event._delay); 0508 setFocus(receiver); 0509 postKeyEvent(receiver, QEvent::KeyPress, Qt::Key_A, QStringLiteral("a"), Qt::ControlModifier); 0510 postKeyEvent(receiver, QEvent::KeyRelease, Qt::Key_A, QStringLiteral("a"), Qt::ControlModifier); 0511 postDelay(20); 0512 postKeyClickEvent(receiver, Qt::Key_Backspace, QString()); 0513 } 0514 0515 default: 0516 break; 0517 } 0518 0519 // delay 0520 postDelay(event._delay); 0521 0522 return; 0523 } 0524 0525 //_______________________________________________________________________ 0526 void Simulator::postEvent(QWidget *receiver, QEvent::Type type) const 0527 { 0528 postQEvent(receiver, new QEvent(type)); 0529 } 0530 0531 //_______________________________________________________________________ 0532 void Simulator::postHoverEvent(QWidget *receiver, QEvent::Type type, const QPoint &newPosition, const QPoint &oldPosition) const 0533 { 0534 postQEvent(receiver, new QHoverEvent(type, newPosition, oldPosition)); 0535 } 0536 0537 //_______________________________________________________________________ 0538 bool Simulator::enter(QWidget *receiver, const QPoint &position, int delay) 0539 { 0540 if (receiver == _previousWidget.data()) 0541 return false; 0542 0543 // store position 0544 moveCursor(receiver->mapToGlobal(position)); 0545 0546 // leave previous widget 0547 if (_previousWidget) { 0548 postEvent(_previousWidget.data(), QEvent::Leave); 0549 if (_previousWidget.data()->testAttribute(Qt::WA_Hover)) { 0550 const QPoint oldPosition(_previousWidget.data()->mapFromGlobal(_previousPosition)); 0551 const QPoint newPosition(_previousWidget.data()->mapFromGlobal(receiver->mapToGlobal(position))); 0552 postHoverEvent(_previousWidget.data(), QEvent::HoverLeave, newPosition, oldPosition); 0553 } 0554 } 0555 0556 // enter or move in current widget 0557 if (!receiver->rect().contains(receiver->mapFromGlobal(_previousPosition))) { 0558 // enter current widget if needed 0559 postEvent(receiver, QEvent::Enter); 0560 if (receiver->testAttribute(Qt::WA_Hover)) { 0561 const QPoint oldPosition(receiver->mapFromGlobal(_previousPosition)); 0562 const QPoint newPosition(position); 0563 postHoverEvent(receiver, QEvent::HoverEnter, newPosition, oldPosition); 0564 } 0565 0566 } else if (receiver->mapFromGlobal(_previousPosition) != position) { 0567 // move mouse if needed 0568 postMouseEvent(receiver, QEvent::MouseMove, Qt::NoButton, position); 0569 if (receiver->testAttribute(Qt::WA_Hover)) { 0570 const QPoint oldPosition(receiver->mapFromGlobal(_previousPosition)); 0571 const QPoint newPosition(position); 0572 postHoverEvent(receiver, QEvent::HoverMove, newPosition, oldPosition); 0573 } 0574 } 0575 0576 // update previous widget and position 0577 _previousWidget = receiver; 0578 _previousPosition = receiver->mapToGlobal(position); 0579 postDelay(delay); 0580 0581 return true; 0582 } 0583 0584 //_______________________________________________________________________ 0585 void Simulator::postMouseClickEvent(QWidget *receiver, Qt::MouseButton button, const QPoint &position) 0586 { 0587 // button press and button release 0588 postMouseEvent(receiver, QEvent::MouseButtonPress, button, position, button); 0589 setFocus(receiver); 0590 postDelay(50); 0591 postMouseEvent(receiver, QEvent::MouseButtonRelease, button, position, button); 0592 } 0593 0594 //_______________________________________________________________________ 0595 void Simulator::postMouseEvent(QWidget *receiver, 0596 QEvent::Type type, 0597 Qt::MouseButton button, 0598 const QPoint &position, 0599 Qt::MouseButtons buttons, 0600 Qt::KeyboardModifiers modifiers) const 0601 { 0602 postQEvent(receiver, new QMouseEvent(type, position, receiver->mapToGlobal(position), button, buttons, modifiers)); 0603 } 0604 0605 //_______________________________________________________________________ 0606 void Simulator::postKeyClickEvent(QWidget *receiver, Qt::Key key, QString text, Qt::KeyboardModifiers modifiers) const 0607 { 0608 postKeyModifiersEvent(receiver, QEvent::KeyPress, modifiers); 0609 postKeyEvent(receiver, QEvent::KeyPress, key, text, modifiers); 0610 postKeyEvent(receiver, QEvent::KeyRelease, key, text, modifiers); 0611 postKeyModifiersEvent(receiver, QEvent::KeyRelease, modifiers); 0612 } 0613 0614 //_______________________________________________________________________ 0615 void Simulator::postKeyModifiersEvent(QWidget *receiver, QEvent::Type type, Qt::KeyboardModifiers modifiers) const 0616 { 0617 if (modifiers == Qt::NoModifier) 0618 return; 0619 0620 switch (type) { 0621 case QEvent::KeyPress: { 0622 if (modifiers & Qt::ShiftModifier) { 0623 postKeyEvent(receiver, QEvent::KeyPress, Qt::Key_Shift, QString()); 0624 } 0625 0626 if (modifiers & Qt::ControlModifier) { 0627 postKeyEvent(receiver, QEvent::KeyPress, Qt::Key_Control, QString(), modifiers & Qt::ShiftModifier); 0628 } 0629 0630 if (modifiers & Qt::AltModifier) { 0631 postKeyEvent(receiver, QEvent::KeyPress, Qt::Key_Alt, QString(), modifiers & (Qt::ShiftModifier | Qt::ControlModifier)); 0632 } 0633 0634 if (modifiers & Qt::MetaModifier) { 0635 postKeyEvent(receiver, QEvent::KeyPress, Qt::Key_Meta, QString(), modifiers & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier)); 0636 } 0637 0638 break; 0639 } 0640 0641 case QEvent::KeyRelease: { 0642 if (modifiers & Qt::MetaModifier) { 0643 postKeyEvent(receiver, QEvent::KeyRelease, Qt::Key_Meta, QString()); 0644 } 0645 0646 if (modifiers & Qt::AltModifier) { 0647 postKeyEvent(receiver, QEvent::KeyRelease, Qt::Key_Alt, QString(), modifiers & Qt::MetaModifier); 0648 } 0649 0650 if (modifiers & Qt::ControlModifier) { 0651 postKeyEvent(receiver, QEvent::KeyRelease, Qt::Key_Control, QString(), modifiers & (Qt::MetaModifier | Qt::AltModifier)); 0652 } 0653 0654 if (modifiers & Qt::ShiftModifier) { 0655 postKeyEvent(receiver, QEvent::KeyRelease, Qt::Key_Shift, QString(), modifiers & (Qt::MetaModifier | Qt::AltModifier | Qt::ControlModifier)); 0656 } 0657 } 0658 0659 default: 0660 break; 0661 } 0662 } 0663 0664 //_______________________________________________________________________ 0665 void Simulator::postKeyEvent(QWidget *receiver, QEvent::Type type, Qt::Key key, QString text, Qt::KeyboardModifiers modifiers) const 0666 { 0667 postQEvent(receiver, new QKeyEvent(type, key, modifiers, text)); 0668 } 0669 0670 //_______________________________________________________________________ 0671 void Simulator::postDelay(int delay) 0672 { 0673 // check value 0674 if (delay == -1) 0675 delay = _defaultDelay; 0676 if (delay <= 0) 0677 return; 0678 0679 // this is largely inspired from qtestlib's qsleep implementation 0680 _timer.start(delay, this); 0681 while (_timer.isActive()) { 0682 // flush events in loop 0683 QCoreApplication::processEvents(QEventLoop::AllEvents, delay); 0684 int ms(10); 0685 0686 // sleep 0687 #ifdef Q_OS_WIN 0688 Sleep(uint(ms)); 0689 #else 0690 struct timespec ts = {ms / 1000, (ms % 1000) * 1000 * 1000}; 0691 nanosleep(&ts, nullptr); 0692 #endif 0693 } 0694 } 0695 0696 //_______________________________________________________________________ 0697 void Simulator::moveCursor(const QPoint &position, int steps) 0698 { 0699 // do nothing if mouse grab is disabled 0700 if (!_grabMouse) 0701 return; 0702 if (_aborted) 0703 return; 0704 const QPoint begin(QCursor::pos()); 0705 const QPoint end(position); 0706 if (begin == end) 0707 return; 0708 0709 if (steps > 1) { 0710 for (int i = 0; i < steps; ++i) { 0711 const QPoint current(begin.x() + qreal((i + 1) * (end.x() - begin.x())) / steps, begin.y() + qreal((i + 1) * (end.y() - begin.y())) / steps); 0712 QCursor::setPos(current); 0713 postDelay(10); 0714 } 0715 0716 } else { 0717 QCursor::setPos(end); 0718 } 0719 } 0720 0721 //_______________________________________________________________________ 0722 void Simulator::setFocus(QWidget *receiver) 0723 { 0724 if (receiver->focusPolicy() != Qt::NoFocus) { 0725 receiver->setFocus(); 0726 } 0727 } 0728 0729 //_______________________________________________________________________ 0730 Qt::Key Simulator::toKey(QChar a) const 0731 { 0732 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0733 return (Qt::Key)QKeySequence(a)[0]; 0734 #else 0735 return QKeySequence(a)[0].key(); 0736 #endif 0737 } 0738 0739 //_______________________________________________________________________ 0740 void Simulator::postQEvent(QWidget *receiver, QEvent *event) const 0741 { 0742 if (_aborted) 0743 delete event; 0744 else 0745 qApp->postEvent(receiver, event); 0746 } 0747 }