Several fixes for Auto-Type

* On Windows, offer explicit methods to use the virtual keyboard style of typing. This partially reverts 1150b69836 by going back to the standard unicode method by default. However, uses can either add {MODE=VIRTUAL} to their sequence or choose "Use Virtual Keyboard" / CTRL+4 from the selection dialog.

* Took this opportunity to clean up the signature of  AutoType::performAutoType and AutoType::performAutoTypeWithSequence by removing the "hideWindow" attribute.

* Show keyboard shortcuts on the selection dialog context menu

* Fix selection dialog help icon color when in dark theme
This commit is contained in:
Jonathan White 2022-03-07 23:05:24 -05:00
parent 392cab2e36
commit 8a7eb36950
14 changed files with 207 additions and 129 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

@ -60,6 +60,7 @@ image::autotype_entry_sequences.png[]
|{DELAY X} |Pause typing for X milliseconds
|{CLEARFIELD} |Clear the input field
|{PICKCHARS} |Pick specific password characters from a dialog
|{MODE=VIRTUAL} |(Experimental) Use virtual key presses on Windows, useful for virtual machines
|===
=== Performing Global Auto-Type

View File

@ -705,14 +705,6 @@
<source>Double click a row to perform Auto-Type or find an entry using the search:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;p&gt;You can use advanced search queries to find any entry in your open databases. The following shortcuts are useful:&lt;br/&gt;
Ctrl+F - Toggle database search&lt;br/&gt;
Ctrl+1 - Type username&lt;br/&gt;
Ctrl+2 - Type password&lt;br/&gt;
Ctrl+3 - Type TOTP&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Search all open databases</source>
<translation type="unfinished"></translation>
@ -753,6 +745,19 @@ Ctrl+3 - Type TOTP&lt;/p&gt;</source>
<source>Copy TOTP</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;p&gt;You can use advanced search queries to find any entry in your open databases. The following shortcuts are useful:&lt;br/&gt;
Ctrl+F - Toggle database search&lt;br/&gt;
Ctrl+1 - Type username&lt;br/&gt;
Ctrl+2 - Type password&lt;br/&gt;
Ctrl+3 - Type TOTP&lt;br/&gt;
Ctrl+4 - Use Virtual Keyboard (Windows Only)&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Virtual Keyboard</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>BrowserAccessControlDialog</name>

View File

@ -254,7 +254,10 @@ void AutoType::unregisterGlobalShortcut()
/**
* Core Autotype function that will execute actions
*/
void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, const QString& sequence, WId window)
void AutoType::executeAutoTypeActions(const Entry* entry,
const QString& sequence,
WId window,
AutoTypeExecutor::Mode mode)
{
QString error;
auto actions = parseSequence(sequence, entry, error);
@ -274,7 +277,8 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
return;
}
if (hideWindow) {
// Explicitly hide the main window if no target window is specified
if (window == 0) {
#if defined(Q_OS_MACOS)
// Check for accessibility permission
if (!macUtils()->enableAccessibility()) {
@ -289,20 +293,22 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
macUtils()->raiseLastActiveWindow();
m_plugin->hideOwnWindow();
#else
if (getMainWindow()) {
getMainWindow()->minimizeOrHide();
#endif
}
// Restore window state in case app stole focus
#endif
QCoreApplication::processEvents();
window = m_plugin->activeWindow();
} else {
// Restore window state (macOS only) then raise the target window
restoreWindowState();
QCoreApplication::processEvents();
m_plugin->raiseWindow(m_windowForGlobal);
// Used only for selected entry auto-type
if (!window) {
window = m_plugin->activeWindow();
m_plugin->raiseWindow(window);
}
// Restore executor mode
m_executor->mode = mode;
int delay = qMax(100, config()->get(Config::AutoTypeStartDelay).toInt());
Tools::wait(delay);
@ -346,7 +352,7 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
* Single Autotype entry-point function
* Look up the Auto-Type sequence for the given entry then perfom Auto-Type in the active window
*/
void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
void AutoType::performAutoType(const Entry* entry)
{
if (!m_plugin) {
return;
@ -354,7 +360,7 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
auto sequences = entry->autoTypeSequences();
if (!sequences.isEmpty()) {
executeAutoTypeActions(entry, hideWindow, sequences.first());
executeAutoTypeActions(entry, sequences.first());
}
}
@ -362,13 +368,13 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
* Extra Autotype entry-point function
* Perfom Auto-Type of the directly specified sequence in the active window
*/
void AutoType::performAutoTypeWithSequence(const Entry* entry, const QString& sequence, QWidget* hideWindow)
void AutoType::performAutoTypeWithSequence(const Entry* entry, const QString& sequence)
{
if (!m_plugin) {
return;
}
executeAutoTypeActions(entry, hideWindow, sequence);
executeAutoTypeActions(entry, sequence);
}
void AutoType::startGlobalAutoType(const QString& search)
@ -467,10 +473,17 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
}
connect(getMainWindow(), &MainWindow::databaseLocked, selectDialog, &AutoTypeSelectDialog::reject);
connect(selectDialog, &AutoTypeSelectDialog::matchActivated, this, [this](const AutoTypeMatch& match) {
connect(selectDialog,
&AutoTypeSelectDialog::matchActivated,
this,
[this](const AutoTypeMatch& match, bool virtualMode) {
m_lastMatch = match;
m_lastMatchRetypeTimer.start(config()->get(Config::GlobalAutoTypeRetypeTime).toInt() * 1000);
executeAutoTypeActions(match.first, nullptr, match.second, m_windowForGlobal);
executeAutoTypeActions(match.first,
match.second,
m_windowForGlobal,
virtualMode ? AutoTypeExecutor::Mode::VIRTUAL
: AutoTypeExecutor::Mode::NORMAL);
resetAutoTypeState();
});
connect(selectDialog, &QDialog::rejected, this, [this] {
@ -488,7 +501,7 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
selectDialog->activateWindow();
} else if (!matchList.isEmpty()) {
// Only one match and not asking, do it!
executeAutoTypeActions(matchList.first().first, nullptr, matchList.first().second, m_windowForGlobal);
executeAutoTypeActions(matchList.first().first, matchList.first().second, m_windowForGlobal);
resetAutoTypeState();
} else {
// We should never get here
@ -717,6 +730,12 @@ AutoType::parseSequence(const QString& entrySequence, const Entry* entry, QStrin
error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
return {};
}
} else if (placeholder.startsWith("mode=")) {
auto mode = AutoTypeExecutor::Mode::NORMAL;
if (placeholder.endsWith("virtual")) {
mode = AutoTypeExecutor::Mode::VIRTUAL;
}
actions << QSharedPointer<AutoTypeMode>::create(mode);
} else if (placeholder == "beep" || placeholder.startsWith("vkey") || placeholder.startsWith("appactivate")
|| placeholder.startsWith("c:")) {
// Ignore these commands

View File

@ -19,15 +19,15 @@
#ifndef KEEPASSX_AUTOTYPE_H
#define KEEPASSX_AUTOTYPE_H
#include "AutoTypeAction.h"
#include <QMutex>
#include <QObject>
#include <QTimer>
#include <QWidget>
#include "AutoTypeAction.h"
#include "AutoTypeMatch.h"
class AutoTypeAction;
class AutoTypeExecutor;
class AutoTypePlatformInterface;
class Database;
class Entry;
@ -41,8 +41,8 @@ public:
QStringList windowTitles();
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error = nullptr);
void unregisterGlobalShortcut();
void performAutoType(const Entry* entry, QWidget* hideWindow = nullptr);
void performAutoTypeWithSequence(const Entry* entry, const QString& sequence, QWidget* hideWindow = nullptr);
void performAutoType(const Entry* entry);
void performAutoTypeWithSequence(const Entry* entry, const QString& sequence);
static bool verifyAutoTypeSyntax(const QString& sequence, const Entry* entry, QString& error);
@ -80,9 +80,9 @@ private:
~AutoType() override;
void loadPlugin(const QString& pluginPath);
void executeAutoTypeActions(const Entry* entry,
QWidget* hideWindow = nullptr,
const QString& customSequence = QString(),
WId window = 0);
const QString& sequence = QString(),
WId window = 0,
AutoTypeExecutor::Mode mode = AutoTypeExecutor::Mode::NORMAL);
void restoreWindowState();
void resetAutoTypeState();

View File

@ -65,3 +65,14 @@ AutoTypeAction::Result AutoTypeBegin::exec(AutoTypeExecutor* executor) const
{
return executor->execBegin(this);
}
AutoTypeMode::AutoTypeMode(AutoTypeExecutor::Mode mode)
: mode(mode)
{
}
AutoTypeAction::Result AutoTypeMode::exec(AutoTypeExecutor* executor) const
{
executor->mode = mode;
return AutoTypeAction::Result::Ok();
}

View File

@ -121,13 +121,29 @@ public:
class KEEPASSXC_EXPORT AutoTypeExecutor
{
public:
enum class Mode
{
NORMAL,
VIRTUAL
};
virtual ~AutoTypeExecutor() = default;
virtual AutoTypeAction::Result execBegin(const AutoTypeBegin* action) = 0;
virtual AutoTypeAction::Result execType(const AutoTypeKey* action) = 0;
virtual AutoTypeAction::Result execClearField(const AutoTypeClearField* action) = 0;
int execDelayMs = 25;
Mode mode = Mode::NORMAL;
QString error;
};
class KEEPASSXC_EXPORT AutoTypeMode : public AutoTypeAction
{
public:
AutoTypeMode(AutoTypeExecutor::Mode mode = AutoTypeExecutor::Mode::NORMAL);
Result exec(AutoTypeExecutor* executor) const override;
const AutoTypeExecutor::Mode mode;
};
#endif // KEEPASSX_AUTOTYPEACTION_H

View File

@ -58,6 +58,8 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
}
});
m_ui->helpButton->setIcon(icons()->icon("system-help"));
m_ui->search->installEventFilter(this);
m_searchTimer.setInterval(300);
@ -118,7 +120,7 @@ void AutoTypeSelectDialog::submitAutoTypeMatch(AutoTypeMatch match)
if (match.first) {
m_accepted = true;
accept();
emit matchActivated(std::move(match));
emit matchActivated(std::move(match), m_virtualMode);
}
}
@ -274,34 +276,54 @@ void AutoTypeSelectDialog::buildActionMenu()
m_actionMenu->addAction(typeUsernameAction);
m_actionMenu->addAction(typePasswordAction);
m_actionMenu->addAction(typeTotpAction);
#ifdef Q_OS_WIN
auto typeVirtualAction = new QAction(icons()->icon("auto-type"), tr("Use Virtual Keyboard"));
m_actionMenu->addAction(typeVirtualAction);
#endif
m_actionMenu->addAction(copyUsernameAction);
m_actionMenu->addAction(copyPasswordAction);
m_actionMenu->addAction(copyTotpAction);
auto shortcut = new QShortcut(Qt::CTRL + Qt::Key_1, this);
connect(shortcut, &QShortcut::activated, typeUsernameAction, &QAction::trigger);
typeUsernameAction->setShortcut(Qt::CTRL + Qt::Key_1);
connect(typeUsernameAction, &QAction::triggered, this, [&] {
auto match = m_ui->view->currentMatch();
match.second = "{USERNAME}";
submitAutoTypeMatch(match);
});
shortcut = new QShortcut(Qt::CTRL + Qt::Key_2, this);
connect(shortcut, &QShortcut::activated, typePasswordAction, &QAction::trigger);
typePasswordAction->setShortcut(Qt::CTRL + Qt::Key_2);
connect(typePasswordAction, &QAction::triggered, this, [&] {
auto match = m_ui->view->currentMatch();
match.second = "{PASSWORD}";
submitAutoTypeMatch(match);
});
shortcut = new QShortcut(Qt::CTRL + Qt::Key_3, this);
connect(shortcut, &QShortcut::activated, typeTotpAction, &QAction::trigger);
typeTotpAction->setShortcut(Qt::CTRL + Qt::Key_3);
connect(typeTotpAction, &QAction::triggered, this, [&] {
auto match = m_ui->view->currentMatch();
match.second = "{TOTP}";
submitAutoTypeMatch(match);
});
#ifdef Q_OS_WIN
typeVirtualAction->setShortcut(Qt::CTRL + Qt::Key_4);
connect(typeVirtualAction, &QAction::triggered, this, [&] {
m_virtualMode = true;
activateCurrentMatch();
});
#endif
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
// Qt 5.10 introduced a new "feature" to hide shortcuts in context menus
// Unfortunately, Qt::AA_DontShowShortcutsInContextMenus is broken, have to manually enable them
typeUsernameAction->setShortcutVisibleInContextMenu(true);
typePasswordAction->setShortcutVisibleInContextMenu(true);
typeTotpAction->setShortcutVisibleInContextMenu(true);
#ifdef Q_OS_WIN
typeVirtualAction->setShortcutVisibleInContextMenu(true);
#endif
#endif
connect(copyUsernameAction, &QAction::triggered, this, [&] {
auto entry = m_ui->view->currentMatch().first;
if (entry) {

View File

@ -45,7 +45,7 @@ public:
void setSearchString(const QString& search);
signals:
void matchActivated(AutoTypeMatch match);
void matchActivated(AutoTypeMatch match, bool virtualMode = false);
protected:
bool eventFilter(QObject* obj, QEvent* event) override;
@ -69,6 +69,7 @@ private:
QTimer m_searchTimer;
QPointer<QMenu> m_actionMenu;
bool m_virtualMode = false;
bool m_accepted = false;
};

View File

@ -43,34 +43,29 @@
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<widget class="QToolButton" name="helpButton">
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="maximumSize">
<size>
<width>14</width>
<height>14</height>
</size>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>&lt;p&gt;You can use advanced search queries to find any entry in your open databases. The following shortcuts are useful:&lt;br/&gt;
Ctrl+F - Toggle database search&lt;br/&gt;
Ctrl+1 - Type username&lt;br/&gt;
Ctrl+2 - Type password&lt;br/&gt;
Ctrl+3 - Type TOTP&lt;/p&gt;</string>
Ctrl+3 - Type TOTP&lt;br/&gt;
Ctrl+4 - Use Virtual Keyboard (Windows Only)&lt;/p&gt;</string>
</property>
<property name="styleSheet">
<string notr="true">QToolButton {
border: none;
background: none;
}</string>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../share/icons/icons.qrc">:/icons/application/scalable/actions/system-help.svg</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
<string notr="true">?</string>
</property>
</widget>
</item>

View File

@ -81,67 +81,71 @@ bool AutoTypePlatformWin::raiseWindow(WId window)
//
// Send unicode character to foreground window
//
void AutoTypePlatformWin::sendChar(const QChar& ch, bool isKeyDown)
void AutoTypePlatformWin::sendChar(const QChar& ch)
{
auto vkey = VkKeyScanExW(ch.unicode(), GetKeyboardLayout(0));
if (vkey == -1) {
// VKey not found, send as Unicode character
DWORD flags = KEYEVENTF_UNICODE;
if (!isKeyDown) {
flags |= KEYEVENTF_KEYUP;
DWORD nativeFlags = KEYEVENTF_UNICODE;
INPUT in[2];
in[0].type = INPUT_KEYBOARD;
in[0].ki.wVk = 0;
in[0].ki.wScan = ch.unicode();
in[0].ki.dwFlags = KEYEVENTF_UNICODE;
in[0].ki.time = 0;
in[0].ki.dwExtraInfo = ::GetMessageExtraInfo();
in[1] = in[0];
in[1].ki.dwFlags |= KEYEVENTF_KEYUP;
::SendInput(2, &in[0], sizeof(INPUT));
}
INPUT in;
in.type = INPUT_KEYBOARD;
in.ki.wVk = 0;
in.ki.wScan = ch.unicode();
in.ki.dwFlags = flags;
in.ki.time = 0;
in.ki.dwExtraInfo = ::GetMessageExtraInfo();
::SendInput(1, &in, sizeof(INPUT));
void AutoTypePlatformWin::sendCharVirtual(const QChar& ch)
{
auto vKey = VkKeyScanExW(ch.unicode(), GetKeyboardLayout(0));
if (vKey == -1) {
// VKey not found, send as Unicode character
sendChar(ch);
return;
}
if (HIBYTE(vkey) & 0x6) {
sendKey(Qt::Key_AltGr, true);
if (HIBYTE(vKey) & 0x6) {
setKeyState(Qt::Key_AltGr, true);
} else {
if (HIBYTE(vkey) & 0x1) {
sendKey(Qt::Key_Shift, true);
if (HIBYTE(vKey) & 0x1) {
setKeyState(Qt::Key_Shift, true);
}
if (HIBYTE(vkey) & 0x2) {
sendKey(Qt::Key_Control, true);
if (HIBYTE(vKey) & 0x2) {
setKeyState(Qt::Key_Control, true);
}
if (HIBYTE(vkey) & 0x4) {
sendKey(Qt::Key_Alt, true);
if (HIBYTE(vKey) & 0x4) {
setKeyState(Qt::Key_Alt, true);
}
}
DWORD flags = KEYEVENTF_SCANCODE;
if (!isKeyDown) {
flags |= KEYEVENTF_KEYUP;
}
INPUT in[2];
in[0].type = INPUT_KEYBOARD;
in[0].ki.wVk = 0;
in[0].ki.wScan = MapVirtualKey(LOBYTE(vKey), MAPVK_VK_TO_VSC);
in[0].ki.dwFlags = KEYEVENTF_SCANCODE;
in[0].ki.time = 0;
in[0].ki.dwExtraInfo = ::GetMessageExtraInfo();
INPUT in;
in.type = INPUT_KEYBOARD;
in.ki.wVk = 0;
in.ki.wScan = MapVirtualKey(LOBYTE(vkey), MAPVK_VK_TO_VSC);
in.ki.dwFlags = flags;
in.ki.time = 0;
in.ki.dwExtraInfo = ::GetMessageExtraInfo();
in[1] = in[0];
in[1].ki.dwFlags |= KEYEVENTF_KEYUP;
::SendInput(1, &in, sizeof(INPUT));
::SendInput(2, &in[0], sizeof(INPUT));
if (HIBYTE(vkey) & 0x6) {
sendKey(Qt::Key_AltGr, false);
if (HIBYTE(vKey) & 0x6) {
setKeyState(Qt::Key_AltGr, false);
} else {
if (HIBYTE(vkey) & 0x1) {
sendKey(Qt::Key_Shift, false);
if (HIBYTE(vKey) & 0x1) {
setKeyState(Qt::Key_Shift, false);
}
if (HIBYTE(vkey) & 0x2) {
sendKey(Qt::Key_Control, false);
if (HIBYTE(vKey) & 0x2) {
setKeyState(Qt::Key_Control, false);
}
if (HIBYTE(vkey) & 0x4) {
sendKey(Qt::Key_Alt, false);
if (HIBYTE(vKey) & 0x4) {
setKeyState(Qt::Key_Alt, false);
}
}
}
@ -149,14 +153,14 @@ void AutoTypePlatformWin::sendChar(const QChar& ch, bool isKeyDown)
//
// Send virtual key code to foreground window
//
void AutoTypePlatformWin::sendKey(Qt::Key key, bool isKeyDown)
void AutoTypePlatformWin::setKeyState(Qt::Key key, bool down)
{
WORD nativeKeyCode = winUtils()->qtToNativeKeyCode(key);
DWORD nativeFlags = KEYEVENTF_SCANCODE;
if (isExtendedKey(nativeKeyCode)) {
nativeFlags |= KEYEVENTF_EXTENDEDKEY;
}
if (!isKeyDown) {
if (!down) {
nativeFlags |= KEYEVENTF_KEYUP;
}
@ -279,37 +283,40 @@ AutoTypeAction::Result AutoTypeExecutorWin::execBegin(const AutoTypeBegin* actio
AutoTypeAction::Result AutoTypeExecutorWin::execType(const AutoTypeKey* action)
{
if (action->modifiers & Qt::ShiftModifier) {
m_platform->sendKey(Qt::Key_Shift, true);
m_platform->setKeyState(Qt::Key_Shift, true);
}
if (action->modifiers & Qt::ControlModifier) {
m_platform->sendKey(Qt::Key_Control, true);
m_platform->setKeyState(Qt::Key_Control, true);
}
if (action->modifiers & Qt::AltModifier) {
m_platform->sendKey(Qt::Key_Alt, true);
m_platform->setKeyState(Qt::Key_Alt, true);
}
if (action->modifiers & Qt::MetaModifier) {
m_platform->sendKey(Qt::Key_Meta, true);
m_platform->setKeyState(Qt::Key_Meta, true);
}
if (action->key != Qt::Key_unknown) {
m_platform->sendKey(action->key, true);
m_platform->sendKey(action->key, false);
m_platform->setKeyState(action->key, true);
m_platform->setKeyState(action->key, false);
} else {
m_platform->sendChar(action->character, true);
m_platform->sendChar(action->character, false);
if (mode == Mode::VIRTUAL) {
m_platform->sendCharVirtual(action->character);
} else {
m_platform->sendChar(action->character);
}
}
if (action->modifiers & Qt::ShiftModifier) {
m_platform->sendKey(Qt::Key_Shift, false);
m_platform->setKeyState(Qt::Key_Shift, false);
}
if (action->modifiers & Qt::ControlModifier) {
m_platform->sendKey(Qt::Key_Control, false);
m_platform->setKeyState(Qt::Key_Control, false);
}
if (action->modifiers & Qt::AltModifier) {
m_platform->sendKey(Qt::Key_Alt, false);
m_platform->setKeyState(Qt::Key_Alt, false);
}
if (action->modifiers & Qt::MetaModifier) {
m_platform->sendKey(Qt::Key_Meta, false);
m_platform->setKeyState(Qt::Key_Meta, false);
}
Tools::sleep(execDelayMs);

View File

@ -43,8 +43,9 @@ public:
bool raiseWindow(WId window) override;
AutoTypeExecutor* createExecutor() override;
void sendChar(const QChar& ch, bool isKeyDown);
void sendKey(Qt::Key key, bool isKeyDown);
void sendCharVirtual(const QChar& ch);
void sendChar(const QChar& ch);
void setKeyState(Qt::Key key, bool down);
private:
static bool isExtendedKey(DWORD nativeKeyCode);

View File

@ -782,9 +782,9 @@ void DatabaseWidget::performAutoType(const QString& sequence)
}
if (sequence.isEmpty()) {
autoType()->performAutoType(currentEntry, window());
autoType()->performAutoType(currentEntry);
} else {
autoType()->performAutoTypeWithSequence(currentEntry, sequence, window());
autoType()->performAutoTypeWithSequence(currentEntry, sequence);
}
}
}

View File

@ -140,7 +140,7 @@ void TestAutoType::testInternal()
void TestAutoType::testSingleAutoType()
{
m_autoType->performAutoType(m_entry1, nullptr);
m_autoType->performAutoType(m_entry1);
QCOMPARE(m_test->actionCount(), 14);
QCOMPARE(m_test->actionChars(),