mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
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:
parent
392cab2e36
commit
8a7eb36950
Binary file not shown.
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 61 KiB |
@ -60,6 +60,7 @@ image::autotype_entry_sequences.png[]
|
|||||||
|{DELAY X} |Pause typing for X milliseconds
|
|{DELAY X} |Pause typing for X milliseconds
|
||||||
|{CLEARFIELD} |Clear the input field
|
|{CLEARFIELD} |Clear the input field
|
||||||
|{PICKCHARS} |Pick specific password characters from a dialog
|
|{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
|
=== Performing Global Auto-Type
|
||||||
|
@ -705,14 +705,6 @@
|
|||||||
<source>Double click a row to perform Auto-Type or find an entry using the search:</source>
|
<source>Double click a row to perform Auto-Type or find an entry using the search:</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source><p>You can use advanced search queries to find any entry in your open databases. The following shortcuts are useful:<br/>
|
|
||||||
Ctrl+F - Toggle database search<br/>
|
|
||||||
Ctrl+1 - Type username<br/>
|
|
||||||
Ctrl+2 - Type password<br/>
|
|
||||||
Ctrl+3 - Type TOTP</p></source>
|
|
||||||
<translation type="unfinished"></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Search all open databases</source>
|
<source>Search all open databases</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
@ -753,6 +745,19 @@ Ctrl+3 - Type TOTP</p></source>
|
|||||||
<source>Copy TOTP</source>
|
<source>Copy TOTP</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source><p>You can use advanced search queries to find any entry in your open databases. The following shortcuts are useful:<br/>
|
||||||
|
Ctrl+F - Toggle database search<br/>
|
||||||
|
Ctrl+1 - Type username<br/>
|
||||||
|
Ctrl+2 - Type password<br/>
|
||||||
|
Ctrl+3 - Type TOTP<br/>
|
||||||
|
Ctrl+4 - Use Virtual Keyboard (Windows Only)</p></source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Use Virtual Keyboard</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>BrowserAccessControlDialog</name>
|
<name>BrowserAccessControlDialog</name>
|
||||||
|
@ -254,7 +254,10 @@ void AutoType::unregisterGlobalShortcut()
|
|||||||
/**
|
/**
|
||||||
* Core Autotype function that will execute actions
|
* 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;
|
QString error;
|
||||||
auto actions = parseSequence(sequence, entry, error);
|
auto actions = parseSequence(sequence, entry, error);
|
||||||
@ -274,7 +277,8 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hideWindow) {
|
// Explicitly hide the main window if no target window is specified
|
||||||
|
if (window == 0) {
|
||||||
#if defined(Q_OS_MACOS)
|
#if defined(Q_OS_MACOS)
|
||||||
// Check for accessibility permission
|
// Check for accessibility permission
|
||||||
if (!macUtils()->enableAccessibility()) {
|
if (!macUtils()->enableAccessibility()) {
|
||||||
@ -289,20 +293,22 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
|||||||
macUtils()->raiseLastActiveWindow();
|
macUtils()->raiseLastActiveWindow();
|
||||||
m_plugin->hideOwnWindow();
|
m_plugin->hideOwnWindow();
|
||||||
#else
|
#else
|
||||||
getMainWindow()->minimizeOrHide();
|
if (getMainWindow()) {
|
||||||
|
getMainWindow()->minimizeOrHide();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
// Restore window state in case app stole focus
|
|
||||||
restoreWindowState();
|
|
||||||
QCoreApplication::processEvents();
|
|
||||||
m_plugin->raiseWindow(m_windowForGlobal);
|
|
||||||
|
|
||||||
// Used only for selected entry auto-type
|
|
||||||
if (!window) {
|
|
||||||
window = m_plugin->activeWindow();
|
window = m_plugin->activeWindow();
|
||||||
|
} else {
|
||||||
|
// Restore window state (macOS only) then raise the target window
|
||||||
|
restoreWindowState();
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
m_plugin->raiseWindow(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore executor mode
|
||||||
|
m_executor->mode = mode;
|
||||||
|
|
||||||
int delay = qMax(100, config()->get(Config::AutoTypeStartDelay).toInt());
|
int delay = qMax(100, config()->get(Config::AutoTypeStartDelay).toInt());
|
||||||
Tools::wait(delay);
|
Tools::wait(delay);
|
||||||
|
|
||||||
@ -346,7 +352,7 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
|||||||
* Single Autotype entry-point function
|
* Single Autotype entry-point function
|
||||||
* Look up the Auto-Type sequence for the given entry then perfom Auto-Type in the active window
|
* 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) {
|
if (!m_plugin) {
|
||||||
return;
|
return;
|
||||||
@ -354,7 +360,7 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
|
|||||||
|
|
||||||
auto sequences = entry->autoTypeSequences();
|
auto sequences = entry->autoTypeSequences();
|
||||||
if (!sequences.isEmpty()) {
|
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
|
* Extra Autotype entry-point function
|
||||||
* Perfom Auto-Type of the directly specified sequence in the active window
|
* 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) {
|
if (!m_plugin) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
executeAutoTypeActions(entry, hideWindow, sequence);
|
executeAutoTypeActions(entry, sequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoType::startGlobalAutoType(const QString& search)
|
void AutoType::startGlobalAutoType(const QString& search)
|
||||||
@ -467,12 +473,19 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
connect(getMainWindow(), &MainWindow::databaseLocked, selectDialog, &AutoTypeSelectDialog::reject);
|
connect(getMainWindow(), &MainWindow::databaseLocked, selectDialog, &AutoTypeSelectDialog::reject);
|
||||||
connect(selectDialog, &AutoTypeSelectDialog::matchActivated, this, [this](const AutoTypeMatch& match) {
|
connect(selectDialog,
|
||||||
m_lastMatch = match;
|
&AutoTypeSelectDialog::matchActivated,
|
||||||
m_lastMatchRetypeTimer.start(config()->get(Config::GlobalAutoTypeRetypeTime).toInt() * 1000);
|
this,
|
||||||
executeAutoTypeActions(match.first, nullptr, match.second, m_windowForGlobal);
|
[this](const AutoTypeMatch& match, bool virtualMode) {
|
||||||
resetAutoTypeState();
|
m_lastMatch = match;
|
||||||
});
|
m_lastMatchRetypeTimer.start(config()->get(Config::GlobalAutoTypeRetypeTime).toInt() * 1000);
|
||||||
|
executeAutoTypeActions(match.first,
|
||||||
|
match.second,
|
||||||
|
m_windowForGlobal,
|
||||||
|
virtualMode ? AutoTypeExecutor::Mode::VIRTUAL
|
||||||
|
: AutoTypeExecutor::Mode::NORMAL);
|
||||||
|
resetAutoTypeState();
|
||||||
|
});
|
||||||
connect(selectDialog, &QDialog::rejected, this, [this] {
|
connect(selectDialog, &QDialog::rejected, this, [this] {
|
||||||
restoreWindowState();
|
restoreWindowState();
|
||||||
resetAutoTypeState();
|
resetAutoTypeState();
|
||||||
@ -488,7 +501,7 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
|
|||||||
selectDialog->activateWindow();
|
selectDialog->activateWindow();
|
||||||
} else if (!matchList.isEmpty()) {
|
} else if (!matchList.isEmpty()) {
|
||||||
// Only one match and not asking, do it!
|
// 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();
|
resetAutoTypeState();
|
||||||
} else {
|
} else {
|
||||||
// We should never get here
|
// 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);
|
error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
|
||||||
return {};
|
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")
|
} else if (placeholder == "beep" || placeholder.startsWith("vkey") || placeholder.startsWith("appactivate")
|
||||||
|| placeholder.startsWith("c:")) {
|
|| placeholder.startsWith("c:")) {
|
||||||
// Ignore these commands
|
// Ignore these commands
|
||||||
|
@ -19,15 +19,15 @@
|
|||||||
#ifndef KEEPASSX_AUTOTYPE_H
|
#ifndef KEEPASSX_AUTOTYPE_H
|
||||||
#define KEEPASSX_AUTOTYPE_H
|
#define KEEPASSX_AUTOTYPE_H
|
||||||
|
|
||||||
|
#include "AutoTypeAction.h"
|
||||||
|
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QObject>
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "AutoTypeAction.h"
|
||||||
#include "AutoTypeMatch.h"
|
#include "AutoTypeMatch.h"
|
||||||
|
|
||||||
class AutoTypeAction;
|
|
||||||
class AutoTypeExecutor;
|
|
||||||
class AutoTypePlatformInterface;
|
class AutoTypePlatformInterface;
|
||||||
class Database;
|
class Database;
|
||||||
class Entry;
|
class Entry;
|
||||||
@ -41,8 +41,8 @@ public:
|
|||||||
QStringList windowTitles();
|
QStringList windowTitles();
|
||||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error = nullptr);
|
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error = nullptr);
|
||||||
void unregisterGlobalShortcut();
|
void unregisterGlobalShortcut();
|
||||||
void performAutoType(const Entry* entry, QWidget* hideWindow = nullptr);
|
void performAutoType(const Entry* entry);
|
||||||
void performAutoTypeWithSequence(const Entry* entry, const QString& sequence, QWidget* hideWindow = nullptr);
|
void performAutoTypeWithSequence(const Entry* entry, const QString& sequence);
|
||||||
|
|
||||||
static bool verifyAutoTypeSyntax(const QString& sequence, const Entry* entry, QString& error);
|
static bool verifyAutoTypeSyntax(const QString& sequence, const Entry* entry, QString& error);
|
||||||
|
|
||||||
@ -80,9 +80,9 @@ private:
|
|||||||
~AutoType() override;
|
~AutoType() override;
|
||||||
void loadPlugin(const QString& pluginPath);
|
void loadPlugin(const QString& pluginPath);
|
||||||
void executeAutoTypeActions(const Entry* entry,
|
void executeAutoTypeActions(const Entry* entry,
|
||||||
QWidget* hideWindow = nullptr,
|
const QString& sequence = QString(),
|
||||||
const QString& customSequence = QString(),
|
WId window = 0,
|
||||||
WId window = 0);
|
AutoTypeExecutor::Mode mode = AutoTypeExecutor::Mode::NORMAL);
|
||||||
void restoreWindowState();
|
void restoreWindowState();
|
||||||
void resetAutoTypeState();
|
void resetAutoTypeState();
|
||||||
|
|
||||||
|
@ -65,3 +65,14 @@ AutoTypeAction::Result AutoTypeBegin::exec(AutoTypeExecutor* executor) const
|
|||||||
{
|
{
|
||||||
return executor->execBegin(this);
|
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();
|
||||||
|
}
|
||||||
|
@ -121,13 +121,29 @@ public:
|
|||||||
class KEEPASSXC_EXPORT AutoTypeExecutor
|
class KEEPASSXC_EXPORT AutoTypeExecutor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum class Mode
|
||||||
|
{
|
||||||
|
NORMAL,
|
||||||
|
VIRTUAL
|
||||||
|
};
|
||||||
|
|
||||||
virtual ~AutoTypeExecutor() = default;
|
virtual ~AutoTypeExecutor() = default;
|
||||||
virtual AutoTypeAction::Result execBegin(const AutoTypeBegin* action) = 0;
|
virtual AutoTypeAction::Result execBegin(const AutoTypeBegin* action) = 0;
|
||||||
virtual AutoTypeAction::Result execType(const AutoTypeKey* action) = 0;
|
virtual AutoTypeAction::Result execType(const AutoTypeKey* action) = 0;
|
||||||
virtual AutoTypeAction::Result execClearField(const AutoTypeClearField* action) = 0;
|
virtual AutoTypeAction::Result execClearField(const AutoTypeClearField* action) = 0;
|
||||||
|
|
||||||
int execDelayMs = 25;
|
int execDelayMs = 25;
|
||||||
|
Mode mode = Mode::NORMAL;
|
||||||
QString error;
|
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
|
#endif // KEEPASSX_AUTOTYPEACTION_H
|
||||||
|
@ -58,6 +58,8 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_ui->helpButton->setIcon(icons()->icon("system-help"));
|
||||||
|
|
||||||
m_ui->search->installEventFilter(this);
|
m_ui->search->installEventFilter(this);
|
||||||
|
|
||||||
m_searchTimer.setInterval(300);
|
m_searchTimer.setInterval(300);
|
||||||
@ -118,7 +120,7 @@ void AutoTypeSelectDialog::submitAutoTypeMatch(AutoTypeMatch match)
|
|||||||
if (match.first) {
|
if (match.first) {
|
||||||
m_accepted = true;
|
m_accepted = true;
|
||||||
accept();
|
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(typeUsernameAction);
|
||||||
m_actionMenu->addAction(typePasswordAction);
|
m_actionMenu->addAction(typePasswordAction);
|
||||||
m_actionMenu->addAction(typeTotpAction);
|
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(copyUsernameAction);
|
||||||
m_actionMenu->addAction(copyPasswordAction);
|
m_actionMenu->addAction(copyPasswordAction);
|
||||||
m_actionMenu->addAction(copyTotpAction);
|
m_actionMenu->addAction(copyTotpAction);
|
||||||
|
|
||||||
auto shortcut = new QShortcut(Qt::CTRL + Qt::Key_1, this);
|
typeUsernameAction->setShortcut(Qt::CTRL + Qt::Key_1);
|
||||||
connect(shortcut, &QShortcut::activated, typeUsernameAction, &QAction::trigger);
|
|
||||||
connect(typeUsernameAction, &QAction::triggered, this, [&] {
|
connect(typeUsernameAction, &QAction::triggered, this, [&] {
|
||||||
auto match = m_ui->view->currentMatch();
|
auto match = m_ui->view->currentMatch();
|
||||||
match.second = "{USERNAME}";
|
match.second = "{USERNAME}";
|
||||||
submitAutoTypeMatch(match);
|
submitAutoTypeMatch(match);
|
||||||
});
|
});
|
||||||
|
|
||||||
shortcut = new QShortcut(Qt::CTRL + Qt::Key_2, this);
|
typePasswordAction->setShortcut(Qt::CTRL + Qt::Key_2);
|
||||||
connect(shortcut, &QShortcut::activated, typePasswordAction, &QAction::trigger);
|
|
||||||
connect(typePasswordAction, &QAction::triggered, this, [&] {
|
connect(typePasswordAction, &QAction::triggered, this, [&] {
|
||||||
auto match = m_ui->view->currentMatch();
|
auto match = m_ui->view->currentMatch();
|
||||||
match.second = "{PASSWORD}";
|
match.second = "{PASSWORD}";
|
||||||
submitAutoTypeMatch(match);
|
submitAutoTypeMatch(match);
|
||||||
});
|
});
|
||||||
|
|
||||||
shortcut = new QShortcut(Qt::CTRL + Qt::Key_3, this);
|
typeTotpAction->setShortcut(Qt::CTRL + Qt::Key_3);
|
||||||
connect(shortcut, &QShortcut::activated, typeTotpAction, &QAction::trigger);
|
|
||||||
connect(typeTotpAction, &QAction::triggered, this, [&] {
|
connect(typeTotpAction, &QAction::triggered, this, [&] {
|
||||||
auto match = m_ui->view->currentMatch();
|
auto match = m_ui->view->currentMatch();
|
||||||
match.second = "{TOTP}";
|
match.second = "{TOTP}";
|
||||||
submitAutoTypeMatch(match);
|
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, [&] {
|
connect(copyUsernameAction, &QAction::triggered, this, [&] {
|
||||||
auto entry = m_ui->view->currentMatch().first;
|
auto entry = m_ui->view->currentMatch().first;
|
||||||
if (entry) {
|
if (entry) {
|
||||||
|
@ -45,7 +45,7 @@ public:
|
|||||||
void setSearchString(const QString& search);
|
void setSearchString(const QString& search);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void matchActivated(AutoTypeMatch match);
|
void matchActivated(AutoTypeMatch match, bool virtualMode = false);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool eventFilter(QObject* obj, QEvent* event) override;
|
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||||
@ -69,6 +69,7 @@ private:
|
|||||||
QTimer m_searchTimer;
|
QTimer m_searchTimer;
|
||||||
QPointer<QMenu> m_actionMenu;
|
QPointer<QMenu> m_actionMenu;
|
||||||
|
|
||||||
|
bool m_virtualMode = false;
|
||||||
bool m_accepted = false;
|
bool m_accepted = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,34 +43,29 @@
|
|||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_2">
|
<widget class="QToolButton" name="helpButton">
|
||||||
<property name="sizePolicy">
|
<property name="cursor">
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
<cursorShape>PointingHandCursor</cursorShape>
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="focusPolicy">
|
||||||
<size>
|
<enum>Qt::NoFocus</enum>
|
||||||
<width>14</width>
|
|
||||||
<height>14</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><p>You can use advanced search queries to find any entry in your open databases. The following shortcuts are useful:<br/>
|
<string><p>You can use advanced search queries to find any entry in your open databases. The following shortcuts are useful:<br/>
|
||||||
Ctrl+F - Toggle database search<br/>
|
Ctrl+F - Toggle database search<br/>
|
||||||
Ctrl+1 - Type username<br/>
|
Ctrl+1 - Type username<br/>
|
||||||
Ctrl+2 - Type password<br/>
|
Ctrl+2 - Type password<br/>
|
||||||
Ctrl+3 - Type TOTP</p></string>
|
Ctrl+3 - Type TOTP<br/>
|
||||||
|
Ctrl+4 - Use Virtual Keyboard (Windows Only)</p></string>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">QToolButton {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
}</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string notr="true">?</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>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -81,67 +81,71 @@ bool AutoTypePlatformWin::raiseWindow(WId window)
|
|||||||
//
|
//
|
||||||
// Send unicode character to foreground 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));
|
DWORD nativeFlags = KEYEVENTF_UNICODE;
|
||||||
if (vkey == -1) {
|
|
||||||
// VKey not found, send as Unicode character
|
|
||||||
DWORD flags = KEYEVENTF_UNICODE;
|
|
||||||
if (!isKeyDown) {
|
|
||||||
flags |= KEYEVENTF_KEYUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
INPUT in;
|
INPUT in[2];
|
||||||
in.type = INPUT_KEYBOARD;
|
in[0].type = INPUT_KEYBOARD;
|
||||||
in.ki.wVk = 0;
|
in[0].ki.wVk = 0;
|
||||||
in.ki.wScan = ch.unicode();
|
in[0].ki.wScan = ch.unicode();
|
||||||
in.ki.dwFlags = flags;
|
in[0].ki.dwFlags = KEYEVENTF_UNICODE;
|
||||||
in.ki.time = 0;
|
in[0].ki.time = 0;
|
||||||
in.ki.dwExtraInfo = ::GetMessageExtraInfo();
|
in[0].ki.dwExtraInfo = ::GetMessageExtraInfo();
|
||||||
::SendInput(1, &in, sizeof(INPUT));
|
|
||||||
|
in[1] = in[0];
|
||||||
|
in[1].ki.dwFlags |= KEYEVENTF_KEYUP;
|
||||||
|
|
||||||
|
::SendInput(2, &in[0], 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HIBYTE(vkey) & 0x6) {
|
if (HIBYTE(vKey) & 0x6) {
|
||||||
sendKey(Qt::Key_AltGr, true);
|
setKeyState(Qt::Key_AltGr, true);
|
||||||
} else {
|
} else {
|
||||||
if (HIBYTE(vkey) & 0x1) {
|
if (HIBYTE(vKey) & 0x1) {
|
||||||
sendKey(Qt::Key_Shift, true);
|
setKeyState(Qt::Key_Shift, true);
|
||||||
}
|
}
|
||||||
if (HIBYTE(vkey) & 0x2) {
|
if (HIBYTE(vKey) & 0x2) {
|
||||||
sendKey(Qt::Key_Control, true);
|
setKeyState(Qt::Key_Control, true);
|
||||||
}
|
}
|
||||||
if (HIBYTE(vkey) & 0x4) {
|
if (HIBYTE(vKey) & 0x4) {
|
||||||
sendKey(Qt::Key_Alt, true);
|
setKeyState(Qt::Key_Alt, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD flags = KEYEVENTF_SCANCODE;
|
INPUT in[2];
|
||||||
if (!isKeyDown) {
|
in[0].type = INPUT_KEYBOARD;
|
||||||
flags |= KEYEVENTF_KEYUP;
|
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[1] = in[0];
|
||||||
in.type = INPUT_KEYBOARD;
|
in[1].ki.dwFlags |= KEYEVENTF_KEYUP;
|
||||||
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();
|
|
||||||
|
|
||||||
::SendInput(1, &in, sizeof(INPUT));
|
::SendInput(2, &in[0], sizeof(INPUT));
|
||||||
|
|
||||||
if (HIBYTE(vkey) & 0x6) {
|
if (HIBYTE(vKey) & 0x6) {
|
||||||
sendKey(Qt::Key_AltGr, false);
|
setKeyState(Qt::Key_AltGr, false);
|
||||||
} else {
|
} else {
|
||||||
if (HIBYTE(vkey) & 0x1) {
|
if (HIBYTE(vKey) & 0x1) {
|
||||||
sendKey(Qt::Key_Shift, false);
|
setKeyState(Qt::Key_Shift, false);
|
||||||
}
|
}
|
||||||
if (HIBYTE(vkey) & 0x2) {
|
if (HIBYTE(vKey) & 0x2) {
|
||||||
sendKey(Qt::Key_Control, false);
|
setKeyState(Qt::Key_Control, false);
|
||||||
}
|
}
|
||||||
if (HIBYTE(vkey) & 0x4) {
|
if (HIBYTE(vKey) & 0x4) {
|
||||||
sendKey(Qt::Key_Alt, false);
|
setKeyState(Qt::Key_Alt, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,14 +153,14 @@ void AutoTypePlatformWin::sendChar(const QChar& ch, bool isKeyDown)
|
|||||||
//
|
//
|
||||||
// Send virtual key code to foreground window
|
// 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);
|
WORD nativeKeyCode = winUtils()->qtToNativeKeyCode(key);
|
||||||
DWORD nativeFlags = KEYEVENTF_SCANCODE;
|
DWORD nativeFlags = KEYEVENTF_SCANCODE;
|
||||||
if (isExtendedKey(nativeKeyCode)) {
|
if (isExtendedKey(nativeKeyCode)) {
|
||||||
nativeFlags |= KEYEVENTF_EXTENDEDKEY;
|
nativeFlags |= KEYEVENTF_EXTENDEDKEY;
|
||||||
}
|
}
|
||||||
if (!isKeyDown) {
|
if (!down) {
|
||||||
nativeFlags |= KEYEVENTF_KEYUP;
|
nativeFlags |= KEYEVENTF_KEYUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,37 +283,40 @@ AutoTypeAction::Result AutoTypeExecutorWin::execBegin(const AutoTypeBegin* actio
|
|||||||
AutoTypeAction::Result AutoTypeExecutorWin::execType(const AutoTypeKey* action)
|
AutoTypeAction::Result AutoTypeExecutorWin::execType(const AutoTypeKey* action)
|
||||||
{
|
{
|
||||||
if (action->modifiers & Qt::ShiftModifier) {
|
if (action->modifiers & Qt::ShiftModifier) {
|
||||||
m_platform->sendKey(Qt::Key_Shift, true);
|
m_platform->setKeyState(Qt::Key_Shift, true);
|
||||||
}
|
}
|
||||||
if (action->modifiers & Qt::ControlModifier) {
|
if (action->modifiers & Qt::ControlModifier) {
|
||||||
m_platform->sendKey(Qt::Key_Control, true);
|
m_platform->setKeyState(Qt::Key_Control, true);
|
||||||
}
|
}
|
||||||
if (action->modifiers & Qt::AltModifier) {
|
if (action->modifiers & Qt::AltModifier) {
|
||||||
m_platform->sendKey(Qt::Key_Alt, true);
|
m_platform->setKeyState(Qt::Key_Alt, true);
|
||||||
}
|
}
|
||||||
if (action->modifiers & Qt::MetaModifier) {
|
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) {
|
if (action->key != Qt::Key_unknown) {
|
||||||
m_platform->sendKey(action->key, true);
|
m_platform->setKeyState(action->key, true);
|
||||||
m_platform->sendKey(action->key, false);
|
m_platform->setKeyState(action->key, false);
|
||||||
} else {
|
} else {
|
||||||
m_platform->sendChar(action->character, true);
|
if (mode == Mode::VIRTUAL) {
|
||||||
m_platform->sendChar(action->character, false);
|
m_platform->sendCharVirtual(action->character);
|
||||||
|
} else {
|
||||||
|
m_platform->sendChar(action->character);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action->modifiers & Qt::ShiftModifier) {
|
if (action->modifiers & Qt::ShiftModifier) {
|
||||||
m_platform->sendKey(Qt::Key_Shift, false);
|
m_platform->setKeyState(Qt::Key_Shift, false);
|
||||||
}
|
}
|
||||||
if (action->modifiers & Qt::ControlModifier) {
|
if (action->modifiers & Qt::ControlModifier) {
|
||||||
m_platform->sendKey(Qt::Key_Control, false);
|
m_platform->setKeyState(Qt::Key_Control, false);
|
||||||
}
|
}
|
||||||
if (action->modifiers & Qt::AltModifier) {
|
if (action->modifiers & Qt::AltModifier) {
|
||||||
m_platform->sendKey(Qt::Key_Alt, false);
|
m_platform->setKeyState(Qt::Key_Alt, false);
|
||||||
}
|
}
|
||||||
if (action->modifiers & Qt::MetaModifier) {
|
if (action->modifiers & Qt::MetaModifier) {
|
||||||
m_platform->sendKey(Qt::Key_Meta, false);
|
m_platform->setKeyState(Qt::Key_Meta, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tools::sleep(execDelayMs);
|
Tools::sleep(execDelayMs);
|
||||||
|
@ -43,8 +43,9 @@ public:
|
|||||||
bool raiseWindow(WId window) override;
|
bool raiseWindow(WId window) override;
|
||||||
AutoTypeExecutor* createExecutor() override;
|
AutoTypeExecutor* createExecutor() override;
|
||||||
|
|
||||||
void sendChar(const QChar& ch, bool isKeyDown);
|
void sendCharVirtual(const QChar& ch);
|
||||||
void sendKey(Qt::Key key, bool isKeyDown);
|
void sendChar(const QChar& ch);
|
||||||
|
void setKeyState(Qt::Key key, bool down);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool isExtendedKey(DWORD nativeKeyCode);
|
static bool isExtendedKey(DWORD nativeKeyCode);
|
||||||
|
@ -782,9 +782,9 @@ void DatabaseWidget::performAutoType(const QString& sequence)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sequence.isEmpty()) {
|
if (sequence.isEmpty()) {
|
||||||
autoType()->performAutoType(currentEntry, window());
|
autoType()->performAutoType(currentEntry);
|
||||||
} else {
|
} else {
|
||||||
autoType()->performAutoTypeWithSequence(currentEntry, sequence, window());
|
autoType()->performAutoTypeWithSequence(currentEntry, sequence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ void TestAutoType::testInternal()
|
|||||||
|
|
||||||
void TestAutoType::testSingleAutoType()
|
void TestAutoType::testSingleAutoType()
|
||||||
{
|
{
|
||||||
m_autoType->performAutoType(m_entry1, nullptr);
|
m_autoType->performAutoType(m_entry1);
|
||||||
|
|
||||||
QCOMPARE(m_test->actionCount(), 14);
|
QCOMPARE(m_test->actionCount(), 14);
|
||||||
QCOMPARE(m_test->actionChars(),
|
QCOMPARE(m_test->actionChars(),
|
||||||
|
Loading…
Reference in New Issue
Block a user