Skip to content

Commit

Permalink
Overhaul Auto-Type Action Handling
Browse files Browse the repository at this point in the history
* Close #2603 - Add support for modifier syntax (+, ^, and %)
* Fix #2633 - Allow reference syntax {REF:...} in Auto-Type sequences
* Close #5334  - Tell the user which part of the Auto-Type sequence is invalid for easy correction
* Fix #2401 - Select the right window on macOS prior to starting Auto-Type

* Allow for nested placeholders
  • Loading branch information
droidmonkey committed Dec 24, 2020
1 parent 95f445e commit 445bbae
Show file tree
Hide file tree
Showing 19 changed files with 424 additions and 555 deletions.
522 changes: 201 additions & 321 deletions src/autotype/AutoType.cpp

Large diffs are not rendered by default.

13 changes: 5 additions & 8 deletions src/autotype/AutoType.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,11 @@ class AutoType : public QObject
QStringList windowTitles();
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error = nullptr);
void unregisterGlobalShortcut();
static bool checkSyntax(const QString& string);
static bool checkHighRepetition(const QString& string);
static bool checkSlowKeypress(const QString& string);
static bool checkHighDelay(const QString& string);
static bool verifyAutoTypeSyntax(const QString& sequence);
void performAutoType(const Entry* entry, QWidget* hideWindow = nullptr);
void performAutoTypeWithSequence(const Entry* entry, const QString& sequence, QWidget* hideWindow = nullptr);

static bool verifyAutoTypeSyntax(const QString& sequence, const Entry* entry, QString& error);

inline bool isAvailable()
{
return m_plugin;
Expand Down Expand Up @@ -85,14 +82,14 @@ private slots:
QWidget* hideWindow = nullptr,
const QString& customSequence = QString(),
WId window = 0);
bool parseActions(const QString& sequence, const Entry* entry, QList<AutoTypeAction*>& actions);
QList<AutoTypeAction*> createActionFromTemplate(const QString& tmpl, const Entry* entry);
void restoreWindowState();
void resetAutoTypeState();

static QList<QSharedPointer<AutoTypeAction>>
parseActions(const QString& entrySequence, const Entry* entry, QString* error = nullptr);

QMutex m_inAutoType;
QMutex m_inGlobalAutoTypeDialog;
int m_autoTypeDelay;
QPluginLoader* m_pluginLoader;
AutoTypePlatformInterface* m_plugin;
AutoTypeExecutor* m_executor;
Expand Down
73 changes: 19 additions & 54 deletions src/autotype/AutoTypeAction.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2020 Team KeePassXC <team@keepassxc.org>
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
*
* This program is free software: you can redistribute it and/or modify
Expand All @@ -19,77 +20,41 @@

#include "core/Tools.h"

AutoTypeChar::AutoTypeChar(const QChar& character)
: character(character)
{
}

AutoTypeAction* AutoTypeChar::clone()
{
return new AutoTypeChar(character);
}

void AutoTypeChar::accept(AutoTypeExecutor* executor)
{
executor->execChar(this);
}

AutoTypeKey::AutoTypeKey(Qt::Key key)
AutoTypeKey::AutoTypeKey(Qt::Key key, Qt::KeyboardModifiers modifiers)
: key(key)
, modifiers(modifiers)
{
}

AutoTypeAction* AutoTypeKey::clone()
AutoTypeKey::AutoTypeKey(const QChar& character, Qt::KeyboardModifiers modifiers)
: character(character)
, modifiers(modifiers)
{
return new AutoTypeKey(key);
}

void AutoTypeKey::accept(AutoTypeExecutor* executor)
void AutoTypeKey::exec(AutoTypeExecutor* executor) const
{
executor->execKey(this);
executor->execType(this);
}

AutoTypeDelay::AutoTypeDelay(int delayMs)
AutoTypeDelay::AutoTypeDelay(int delayMs, bool setExecDelay)
: delayMs(delayMs)
, setExecDelay(setExecDelay)
{
}

AutoTypeAction* AutoTypeDelay::clone()
void AutoTypeDelay::exec(AutoTypeExecutor* executor) const
{
return new AutoTypeDelay(delayMs);
if (setExecDelay) {
// Change the delay between actions
executor->execDelayMs = delayMs;
} else {
// Pause execution
Tools::wait(delayMs);
}
}

void AutoTypeDelay::accept(AutoTypeExecutor* executor)
{
executor->execDelay(this);
}

AutoTypeClearField::AutoTypeClearField()
{
}

AutoTypeAction* AutoTypeClearField::clone()
{
return new AutoTypeClearField();
}

void AutoTypeClearField::accept(AutoTypeExecutor* executor)
void AutoTypeClearField::exec(AutoTypeExecutor* executor) const
{
executor->execClearField(this);
}

void AutoTypeExecutor::execDelay(AutoTypeDelay* action)
{
Tools::wait(action->delayMs);
}

void AutoTypeExecutor::execClearField(AutoTypeClearField* action)
{
Q_UNUSED(action);
}

AutoTypeAction::~AutoTypeAction()
{
// This makes sure that AutoTypeAction's vtable is placed
// in this translation unit.
}
51 changes: 19 additions & 32 deletions src/autotype/AutoTypeAction.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2020 Team KeePassXC <team@keepassxc.org>
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
*
* This program is free software: you can redistribute it and/or modify
Expand All @@ -19,8 +20,6 @@
#define KEEPASSX_AUTOTYPEACTION_H

#include <QChar>
#include <QObject>
#include <Qt>

#include "core/Global.h"

Expand All @@ -29,59 +28,47 @@ class AutoTypeExecutor;
class KEEPASSX_EXPORT AutoTypeAction
{
public:
virtual AutoTypeAction* clone() = 0;
virtual void accept(AutoTypeExecutor* executor) = 0;
virtual ~AutoTypeAction();
};

class KEEPASSX_EXPORT AutoTypeChar : public AutoTypeAction
{
public:
explicit AutoTypeChar(const QChar& character);
AutoTypeAction* clone() override;
void accept(AutoTypeExecutor* executor) override;

const QChar character;
AutoTypeAction() = default;
virtual void exec(AutoTypeExecutor* executor) const = 0;
virtual ~AutoTypeAction() = default;
};

class KEEPASSX_EXPORT AutoTypeKey : public AutoTypeAction
{
public:
explicit AutoTypeKey(Qt::Key key);
AutoTypeAction* clone() override;
void accept(AutoTypeExecutor* executor) override;
explicit AutoTypeKey(const QChar& character, Qt::KeyboardModifiers modifiers = Qt::NoModifier);
explicit AutoTypeKey(Qt::Key key, Qt::KeyboardModifiers modifiers = Qt::NoModifier);
void exec(AutoTypeExecutor* executor) const override;

const Qt::Key key;
const QChar character;
const Qt::Key key = Qt::Key_unknown;
const Qt::KeyboardModifiers modifiers;
};

class KEEPASSX_EXPORT AutoTypeDelay : public AutoTypeAction
{
public:
explicit AutoTypeDelay(int delayMs);
AutoTypeAction* clone() override;
void accept(AutoTypeExecutor* executor) override;
explicit AutoTypeDelay(int delayMs, bool setExecDelay = false);
void exec(AutoTypeExecutor* executor) const override;

const int delayMs;
const bool setExecDelay;
};

class KEEPASSX_EXPORT AutoTypeClearField : public AutoTypeAction
{
public:
AutoTypeClearField();
AutoTypeAction* clone() override;
void accept(AutoTypeExecutor* executor) override;
void exec(AutoTypeExecutor* executor) const override;
};

class KEEPASSX_EXPORT AutoTypeExecutor
{
public:
virtual ~AutoTypeExecutor()
{
}
virtual void execChar(AutoTypeChar* action) = 0;
virtual void execKey(AutoTypeKey* action) = 0;
virtual void execDelay(AutoTypeDelay* action);
virtual void execClearField(AutoTypeClearField* action);
virtual ~AutoTypeExecutor() = default;
virtual void execType(const AutoTypeKey* action) = 0;
virtual void execClearField(const AutoTypeClearField* action) = 0;

int execDelayMs = 25;
};

#endif // KEEPASSX_AUTOTYPEACTION_H
1 change: 0 additions & 1 deletion src/autotype/AutoTypeSelectDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
}
});


m_ui->search->setFocus();
m_ui->search->installEventFilter(this);

Expand Down
60 changes: 34 additions & 26 deletions src/autotype/mac/AutoTypeMac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

#include "AutoTypeMac.h"
#include "core/Tools.h"
#include "gui/osutils/macutils/MacUtils.h"
#include "gui/MessageBox.h"

Expand Down Expand Up @@ -214,36 +215,43 @@ AutoTypeExecutorMac::AutoTypeExecutorMac(AutoTypePlatformMac* platform)
{
}

void AutoTypeExecutorMac::execChar(AutoTypeChar* action)
void AutoTypeExecutorMac::execType(const AutoTypeKey* action)
{
m_platform->sendChar(action->character, true);
m_platform->sendChar(action->character, false);
}
if (action->modifiers & Qt::ShiftModifier) {
m_platform->sendKey(Qt::Key_Shift, true);
}
if (action->modifiers & Qt::ControlModifier) {
m_platform->sendKey(Qt::Key_Control, true);
}
if (action->modifiers & Qt::AltModifier) {
m_platform->sendKey(Qt::Key_Alt, true);
}

void AutoTypeExecutorMac::execKey(AutoTypeKey* action)
{
m_platform->sendKey(action->key, true);
m_platform->sendKey(action->key, false);
if (action->key != Qt::Key_unknown) {
m_platform->sendKey(action->key, true);
m_platform->sendKey(action->key, false);
} else {
m_platform->sendChar(action->character, true);
m_platform->sendChar(action->character, false);
}

if (action->modifiers & Qt::ShiftModifier) {
m_platform->sendKey(Qt::Key_Shift, false);
}
if (action->modifiers & Qt::ControlModifier) {
m_platform->sendKey(Qt::Key_Control, false);
}
if (action->modifiers & Qt::AltModifier) {
m_platform->sendKey(Qt::Key_Alt, false);
}

Tools::sleep(execDelayMs);
}

void AutoTypeExecutorMac::execClearField(AutoTypeClearField* action = nullptr)
void AutoTypeExecutorMac::execClearField(const AutoTypeClearField* action)
{
Q_UNUSED(action);

m_platform->sendKey(Qt::Key_Control, true, Qt::ControlModifier);
m_platform->sendKey(Qt::Key_Up, true, Qt::ControlModifier);
m_platform->sendKey(Qt::Key_Up, false, Qt::ControlModifier);
m_platform->sendKey(Qt::Key_Control, false);
usleep(25 * 1000);
m_platform->sendKey(Qt::Key_Shift, true, Qt::ShiftModifier);
m_platform->sendKey(Qt::Key_Control, true, Qt::ShiftModifier | Qt::ControlModifier);
m_platform->sendKey(Qt::Key_Down, true, Qt::ShiftModifier | Qt::ControlModifier);
m_platform->sendKey(Qt::Key_Down, false, Qt::ShiftModifier | Qt::ControlModifier);
m_platform->sendKey(Qt::Key_Control, false, Qt::ShiftModifier);
m_platform->sendKey(Qt::Key_Shift, false);
usleep(25 * 1000);
m_platform->sendKey(Qt::Key_Backspace, true);
m_platform->sendKey(Qt::Key_Backspace, false);

usleep(25 * 1000);
execType(new AutoTypeKey(Qt::Key_Up, Qt::ControlModifier));
execType(new AutoTypeKey(Qt::Key_Down, Qt::ControlModifier | Qt::ShiftModifier));
execType(new AutoTypeKey(Qt::Key_Backspace));
}
5 changes: 2 additions & 3 deletions src/autotype/mac/AutoTypeMac.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,8 @@ class AutoTypeExecutorMac : public AutoTypeExecutor
public:
explicit AutoTypeExecutorMac(AutoTypePlatformMac* platform);

void execChar(AutoTypeChar* action) override;
void execKey(AutoTypeKey* action) override;
void execClearField(AutoTypeClearField* action) override;
void execType(const AutoTypeKey* action) override;
void execClearField(const AutoTypeClearField* action) override;

private:
AutoTypePlatformMac* const m_platform;
Expand Down
30 changes: 13 additions & 17 deletions src/autotype/test/AutoTypeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,27 +59,23 @@ QString AutoTypePlatformTest::actionChars()

int AutoTypePlatformTest::actionCount()
{
return m_actionList.size();
return m_actionCount;
}

void AutoTypePlatformTest::clearActions()
{
qDeleteAll(m_actionList);
m_actionList.clear();

m_actionChars.clear();
m_actionCount = 0;
}

void AutoTypePlatformTest::addActionChar(AutoTypeChar* action)
{
m_actionList.append(action->clone());
m_actionChars += action->character;
}

void AutoTypePlatformTest::addActionKey(AutoTypeKey* action)
void AutoTypePlatformTest::addAction(const AutoTypeKey* action)
{
m_actionList.append(action->clone());
m_actionChars.append(keyToString(action->key));
++m_actionCount;
if (action->key != Qt::Key_unknown) {
m_actionChars += keyToString(action->key);
} else {
m_actionChars += action->character;
}
}

bool AutoTypePlatformTest::raiseWindow(WId window)
Expand All @@ -106,12 +102,12 @@ AutoTypeExecutorTest::AutoTypeExecutorTest(AutoTypePlatformTest* platform)
{
}

void AutoTypeExecutorTest::execChar(AutoTypeChar* action)
void AutoTypeExecutorTest::execType(const AutoTypeKey* action)
{
m_platform->addActionChar(action);
m_platform->addAction(action);
}

void AutoTypeExecutorTest::execKey(AutoTypeKey* action)
void AutoTypeExecutorTest::execClearField(const AutoTypeClearField* action)
{
m_platform->addActionKey(action);
Q_UNUSED(action);
}
Loading

0 comments on commit 445bbae

Please sign in to comment.