mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-02-17 13:02:49 -05:00
Merge remote-tracking branch 'origin/develop' into feature/autoreload-db
# Conflicts: # src/gui/DatabaseWidget.cpp # src/gui/DatabaseWidget.h
This commit is contained in:
commit
20c3ca7d37
15
.travis.yml
15
.travis.yml
@ -12,7 +12,12 @@ compiler:
|
|||||||
|
|
||||||
git:
|
git:
|
||||||
depth: 3
|
depth: 3
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- env: BUILD=1
|
||||||
|
services: [docker]
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq update; fi
|
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq update; fi
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install cmake libmicrohttpd10 libmicrohttpd-dev libxi-dev qtbase5-dev libqt5x11extras5-dev qttools5-dev qttools5-dev-tools libgcrypt20-dev zlib1g-dev libxtst-dev xvfb; fi
|
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install cmake libmicrohttpd10 libmicrohttpd-dev libxi-dev qtbase5-dev libqt5x11extras5-dev qttools5-dev qttools5-dev-tools libgcrypt20-dev zlib1g-dev libxtst-dev xvfb; fi
|
||||||
@ -20,14 +25,18 @@ before_install:
|
|||||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew ls | grep -wq cmake || brew install cmake; fi
|
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew ls | grep -wq cmake || brew install cmake; fi
|
||||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew ls | grep -wq qt5 || brew install qt5; fi
|
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew ls | grep -wq qt5 || brew install qt5; fi
|
||||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew ls | grep -wq libgcrypt || brew install libgcrypt; fi
|
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew ls | grep -wq libgcrypt || brew install libgcrypt; fi
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then CMAKE_ARGS="-DCMAKE_PREFIX_PATH=/usr/local/opt/qt5"; fi
|
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then CMAKE_ARGS="-DCMAKE_PREFIX_PATH=/usr/local/opt/qt5"; fi
|
||||||
- mkdir build && pushd build
|
- mkdir build && pushd build
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUI_TESTS=ON $CMAKE_ARGS ..
|
- cmake -DCMAKE_BUILD_TYPE=Release -DWITH_GUI_TESTS=ON $CMAKE_ARGS ..
|
||||||
- make
|
- make
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then make test ARGS+="-E testgui --output-on-failure"; fi
|
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then make test ARGS+="-E testgui --output-on-failure"; fi
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then xvfb-run -a --server-args="-screen 0 800x600x24" make test ARGS+="-R testgui --output-on-failure"; fi
|
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then xvfb-run -a --server-args="-screen 0 800x600x24" make test ARGS+="-R testgui --output-on-failure"; fi
|
||||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then make test ARGS+="--output-on-failure"; fi
|
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then make test ARGS+="--output-on-failure"; fi
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- popd
|
||||||
|
- if [ ! -z "$BUILD" ]; then docker run -v $(pwd):/cwd snapcore/snapcraft sh -c 'cd /cwd && apt update && snapcraft'; fi
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[main]
|
[main]
|
||||||
host = https://www.transifex.com
|
host = https://www.transifex.com
|
||||||
|
|
||||||
[keepassx-reboot.keepassx_ents]
|
[keepassxc.keepassx_ents]
|
||||||
source_file = share/translations/keepassx_en.ts
|
source_file = share/translations/keepassx_en.ts
|
||||||
file_filter = share/translations/keepassx_<lang>.ts
|
file_filter = share/translations/keepassx_<lang>.ts
|
||||||
source_lang = en
|
source_lang = en
|
||||||
|
@ -142,7 +142,7 @@ if(MINGW)
|
|||||||
set(DATA_INSTALL_DIR "share")
|
set(DATA_INSTALL_DIR "share")
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
set(BIN_INSTALL_DIR ".")
|
set(BIN_INSTALL_DIR ".")
|
||||||
set(PLUGIN_INSTALL_DIR ".")
|
set(PLUGIN_INSTALL_DIR "${PROGNAME}.app/Contents/PlugIns")
|
||||||
set(DATA_INSTALL_DIR "${PROGNAME}.app/Contents/Resources")
|
set(DATA_INSTALL_DIR "${PROGNAME}.app/Contents/Resources")
|
||||||
else()
|
else()
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
13
setup/gui/keepassxc.desktop
Normal file
13
setup/gui/keepassxc.desktop
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Name=KeePassXC
|
||||||
|
GenericName=Community Password Manager
|
||||||
|
GenericName[de]=Passwortverwaltung
|
||||||
|
GenericName[es]=Gestor de contraseñas
|
||||||
|
GenericName[fr]=Gestionnaire de mot de passe
|
||||||
|
GenericName[ru]=менеджер паролей
|
||||||
|
Exec=keepassxc %f
|
||||||
|
Icon=${SNAP}/share/icons/hicolor/256x256/apps/keepassxc.png
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Categories=Qt;Utility;
|
||||||
|
MimeType=application/x-keepass2;
|
@ -105,6 +105,7 @@ set(keepassx_SOURCES
|
|||||||
gui/SearchWidget.cpp
|
gui/SearchWidget.cpp
|
||||||
gui/SortFilterHideProxyModel.cpp
|
gui/SortFilterHideProxyModel.cpp
|
||||||
gui/UnlockDatabaseWidget.cpp
|
gui/UnlockDatabaseWidget.cpp
|
||||||
|
gui/UnlockDatabaseDialog.cpp
|
||||||
gui/WelcomeWidget.cpp
|
gui/WelcomeWidget.cpp
|
||||||
gui/entry/AutoTypeAssociationsModel.cpp
|
gui/entry/AutoTypeAssociationsModel.cpp
|
||||||
gui/entry/EditEntryWidget.cpp
|
gui/entry/EditEntryWidget.cpp
|
||||||
@ -216,16 +217,26 @@ if(APPLE)
|
|||||||
set(CPACK_PACKAGE_FILE_NAME "${PROGNAME}-${KEEPASSXC_VERSION_NUM}")
|
set(CPACK_PACKAGE_FILE_NAME "${PROGNAME}-${KEEPASSXC_VERSION_NUM}")
|
||||||
include(CPack)
|
include(CPack)
|
||||||
|
|
||||||
include(DeployQt4)
|
if(NOT DEFINED QT_BINARY_DIR)
|
||||||
install_qt4_executable(${PROGNAME}.app "qjpeg;qgif;qico;qtaccessiblewidgets")
|
set(QT_BINARY_DIR "/usr/local/opt/qt5/bin" CACHE PATH "QT binary folder")
|
||||||
|
endif()
|
||||||
|
add_custom_command(TARGET ${PROGNAME}
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND ${QT_BINARY_DIR}/macdeployqt ${PROGNAME}.app
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src
|
||||||
|
COMMENT "Deploying app bundle")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MINGW )
|
if(MINGW)
|
||||||
set(CPACK_GENERATOR "ZIP")
|
set(CPACK_GENERATOR "ZIP")
|
||||||
set(CPACK_STRIP_FILES ON)
|
set(CPACK_STRIP_FILES ON)
|
||||||
set(CPACK_PACKAGE_FILE_NAME "${PROGNAME}-${KEEPASSXC_VERSION_NUM}")
|
set(CPACK_PACKAGE_FILE_NAME "${PROGNAME}-${KEEPASSXC_VERSION_NUM}")
|
||||||
include(CPack)
|
include(CPack)
|
||||||
|
|
||||||
|
install(CODE "
|
||||||
|
set(gp_tool \"objdump\")
|
||||||
|
" COMPONENT Runtime)
|
||||||
|
|
||||||
include(DeployQt4)
|
include(DeployQt4)
|
||||||
install_qt4_executable(${PROGNAME}.exe "qjpeg;qgif;qico;qtaccessiblewidgets")
|
install_qt4_executable(${PROGNAME}.exe "qjpeg;qgif;qico;qtaccessiblewidgets")
|
||||||
endif()
|
endif()
|
||||||
|
@ -147,7 +147,11 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow, const QS
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hideWindow) {
|
if (hideWindow) {
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
m_plugin->raiseLastActiveWindow();
|
||||||
|
#else
|
||||||
hideWindow->showMinimized();
|
hideWindow->showMinimized();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Tools::wait(m_plugin->initialTimeout());
|
Tools::wait(m_plugin->initialTimeout());
|
||||||
@ -188,9 +192,6 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
|||||||
QList<Entry*> entryList;
|
QList<Entry*> entryList;
|
||||||
QHash<Entry*, QString> sequenceHash;
|
QHash<Entry*, QString> sequenceHash;
|
||||||
|
|
||||||
// TODO: Check if there are any active databases here, if not do nothing
|
|
||||||
// TODO: Check if all databases are locked, if so ask to unlock them
|
|
||||||
|
|
||||||
for (Database* db : dbList) {
|
for (Database* db : dbList) {
|
||||||
const QList<Entry*> dbEntries = db->rootGroup()->entriesRecursive();
|
const QList<Entry*> dbEntries = db->rootGroup()->entriesRecursive();
|
||||||
for (Entry* entry : dbEntries) {
|
for (Entry* entry : dbEntries) {
|
||||||
@ -220,6 +221,10 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
|||||||
SLOT(performAutoTypeFromGlobal(Entry*,QString)));
|
SLOT(performAutoTypeFromGlobal(Entry*,QString)));
|
||||||
connect(selectDialog, SIGNAL(rejected()), SLOT(resetInAutoType()));
|
connect(selectDialog, SIGNAL(rejected()), SLOT(resetInAutoType()));
|
||||||
selectDialog->setEntries(entryList, sequenceHash);
|
selectDialog->setEntries(entryList, sequenceHash);
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
m_plugin->raiseOwnWindow();
|
||||||
|
Tools::wait(500);
|
||||||
|
#endif
|
||||||
selectDialog->show();
|
selectDialog->show();
|
||||||
// necessary when the main window is minimized
|
// necessary when the main window is minimized
|
||||||
selectDialog->activateWindow();
|
selectDialog->activateWindow();
|
||||||
@ -308,7 +313,7 @@ bool AutoType::parseActions(const QString& sequence, const Entry* entry, QList<A
|
|||||||
|
|
||||||
|
|
||||||
for (const QChar& ch : sequence) {
|
for (const QChar& ch : sequence) {
|
||||||
// TODO: implement support for {{}, {}} and {DELAY=X}
|
// TODO: implement support for {{}, {}}
|
||||||
|
|
||||||
if (inTmpl) {
|
if (inTmpl) {
|
||||||
if (ch == '{') {
|
if (ch == '{') {
|
||||||
@ -438,7 +443,7 @@ QList<AutoTypeAction*> AutoType::createActionFromTemplate(const QString& tmpl, c
|
|||||||
else if (tmplName == "ptrsc") {
|
else if (tmplName == "ptrsc") {
|
||||||
list.append(new AutoTypeKey(Qt::Key_Print));
|
list.append(new AutoTypeKey(Qt::Key_Print));
|
||||||
}
|
}
|
||||||
else if (tmplName == "scolllock") {
|
else if (tmplName == "scrolllock") {
|
||||||
list.append(new AutoTypeKey(Qt::Key_ScrollLock));
|
list.append(new AutoTypeKey(Qt::Key_ScrollLock));
|
||||||
}
|
}
|
||||||
// Qt doesn't know about keypad keys so use the normal ones instead
|
// Qt doesn't know about keypad keys so use the normal ones instead
|
||||||
|
@ -39,6 +39,11 @@ public:
|
|||||||
|
|
||||||
virtual AutoTypeExecutor* createExecutor() = 0;
|
virtual AutoTypeExecutor* createExecutor() = 0;
|
||||||
|
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
virtual bool raiseLastActiveWindow() = 0;
|
||||||
|
virtual bool raiseOwnWindow() = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
// implementations should also provide a globalShortcutTriggered() signal
|
// implementations should also provide a globalShortcutTriggered() signal
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,11 +10,12 @@ if(UNIX AND NOT APPLE)
|
|||||||
if(X11_FOUND AND X11_Xi_FOUND AND X11_XTest_FOUND AND Qt5X11Extras_FOUND)
|
if(X11_FOUND AND X11_Xi_FOUND AND X11_XTest_FOUND AND Qt5X11Extras_FOUND)
|
||||||
add_subdirectory(xcb)
|
add_subdirectory(xcb)
|
||||||
endif()
|
endif()
|
||||||
|
elseif(APPLE)
|
||||||
|
add_subdirectory(mac)
|
||||||
|
elseif(WIN32)
|
||||||
|
add_subdirectory(windows)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WITH_TESTS)
|
if(WITH_TESTS)
|
||||||
# autotype non supported on Windows right now
|
add_subdirectory(test)
|
||||||
if(UNIX)
|
endif()
|
||||||
add_subdirectory(test)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
42
src/autotype/mac/AppKit.h
Normal file
42
src/autotype/mac/AppKit.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Lennart Glauer <mail@lennart-glauer.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSX_APPKIT_H
|
||||||
|
#define KEEPASSX_APPKIT_H
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
class AppKit
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AppKit();
|
||||||
|
~AppKit();
|
||||||
|
|
||||||
|
pid_t lastActiveProcessId();
|
||||||
|
pid_t activeProcessId();
|
||||||
|
pid_t ownProcessId();
|
||||||
|
bool activateProcess(pid_t pid);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void *self;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
#endif // KEEPASSX_APPKIT_H
|
31
src/autotype/mac/AppKitImpl.h
Normal file
31
src/autotype/mac/AppKitImpl.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Lennart Glauer <mail@lennart-glauer.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "AppKit.h"
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <AppKit/NSRunningApplication.h>
|
||||||
|
|
||||||
|
@interface AppKitImpl : NSObject
|
||||||
|
|
||||||
|
@property (strong) NSRunningApplication *lastActiveApplication;
|
||||||
|
|
||||||
|
- (pid_t) activeProcessId;
|
||||||
|
- (pid_t) ownProcessId;
|
||||||
|
- (bool) activateProcess:(pid_t) pid;
|
||||||
|
|
||||||
|
@end
|
102
src/autotype/mac/AppKitImpl.mm
Normal file
102
src/autotype/mac/AppKitImpl.mm
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Lennart Glauer <mail@lennart-glauer.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "AppKitImpl.h"
|
||||||
|
|
||||||
|
#import <AppKit/NSWorkspace.h>
|
||||||
|
|
||||||
|
@implementation AppKitImpl
|
||||||
|
|
||||||
|
AppKit::AppKit()
|
||||||
|
{
|
||||||
|
self = [[AppKitImpl alloc] init];
|
||||||
|
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:static_cast<id>(self)
|
||||||
|
selector:@selector(didDeactivateApplicationObserver:)
|
||||||
|
name:NSWorkspaceDidDeactivateApplicationNotification
|
||||||
|
object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
AppKit::~AppKit()
|
||||||
|
{
|
||||||
|
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:static_cast<id>(self)];
|
||||||
|
[static_cast<id>(self) dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Update last active application property
|
||||||
|
//
|
||||||
|
- (void) didDeactivateApplicationObserver:(NSNotification *) notification
|
||||||
|
{
|
||||||
|
NSDictionary *userInfo = notification.userInfo;
|
||||||
|
NSRunningApplication *app = userInfo[NSWorkspaceApplicationKey];
|
||||||
|
|
||||||
|
if (app.processIdentifier != [self ownProcessId]) {
|
||||||
|
self.lastActiveApplication = app;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get process id of frontmost application (-> keyboard input)
|
||||||
|
//
|
||||||
|
- (pid_t) activeProcessId
|
||||||
|
{
|
||||||
|
return [NSWorkspace sharedWorkspace].frontmostApplication.processIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get process id of own process
|
||||||
|
//
|
||||||
|
- (pid_t) ownProcessId
|
||||||
|
{
|
||||||
|
return [NSProcessInfo processInfo].processIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Activate application by process id
|
||||||
|
//
|
||||||
|
- (bool) activateProcess:(pid_t) pid
|
||||||
|
{
|
||||||
|
NSRunningApplication *app = [NSRunningApplication runningApplicationWithProcessIdentifier:pid];
|
||||||
|
|
||||||
|
return [app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ------------------------- C++ Trampolines -------------------------
|
||||||
|
//
|
||||||
|
|
||||||
|
pid_t AppKit::lastActiveProcessId()
|
||||||
|
{
|
||||||
|
return [static_cast<id>(self) lastActiveApplication].processIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t AppKit::activeProcessId()
|
||||||
|
{
|
||||||
|
return [static_cast<id>(self) activeProcessId];
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t AppKit::ownProcessId()
|
||||||
|
{
|
||||||
|
return [static_cast<id>(self) ownProcessId];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppKit::activateProcess(pid_t pid)
|
||||||
|
{
|
||||||
|
return [static_cast<id>(self) activateProcess:pid];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
488
src/autotype/mac/AutoTypeMac.cpp
Normal file
488
src/autotype/mac/AutoTypeMac.cpp
Normal file
@ -0,0 +1,488 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Lennart Glauer <mail@lennart-glauer.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "AutoTypeMac.h"
|
||||||
|
|
||||||
|
#include <ApplicationServices/ApplicationServices.h>
|
||||||
|
|
||||||
|
#define HOTKEY_ID 1
|
||||||
|
#define MAX_WINDOW_TITLE_LENGTH 1024
|
||||||
|
#define INVALID_KEYCODE 0xFFFF
|
||||||
|
|
||||||
|
AutoTypePlatformMac::AutoTypePlatformMac()
|
||||||
|
: m_appkit(new AppKit())
|
||||||
|
, m_hotkeyRef(nullptr)
|
||||||
|
, m_hotkeyId({ 'kpx2', HOTKEY_ID })
|
||||||
|
{
|
||||||
|
EventTypeSpec eventSpec;
|
||||||
|
eventSpec.eventClass = kEventClassKeyboard;
|
||||||
|
eventSpec.eventKind = kEventHotKeyPressed;
|
||||||
|
|
||||||
|
::InstallApplicationEventHandler(AutoTypePlatformMac::hotkeyHandler, 1, &eventSpec, this, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Keepassx requires mac os 10.7
|
||||||
|
//
|
||||||
|
bool AutoTypePlatformMac::isAvailable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get list of visible window titles
|
||||||
|
// see: Quartz Window Services
|
||||||
|
//
|
||||||
|
QStringList AutoTypePlatformMac::windowTitles()
|
||||||
|
{
|
||||||
|
QStringList list;
|
||||||
|
|
||||||
|
CFArrayRef windowList = ::CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
|
||||||
|
if (windowList != nullptr) {
|
||||||
|
CFIndex count = ::CFArrayGetCount(windowList);
|
||||||
|
|
||||||
|
for (CFIndex i = 0; i < count; i++) {
|
||||||
|
CFDictionaryRef window = static_cast<CFDictionaryRef>(::CFArrayGetValueAtIndex(windowList, i));
|
||||||
|
if (windowLayer(window) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString title = windowTitle(window);
|
||||||
|
if (!title.isEmpty()) {
|
||||||
|
list.append(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::CFRelease(windowList);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get active window process id
|
||||||
|
//
|
||||||
|
WId AutoTypePlatformMac::activeWindow()
|
||||||
|
{
|
||||||
|
return m_appkit->activeProcessId();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get active window title
|
||||||
|
// see: Quartz Window Services
|
||||||
|
//
|
||||||
|
QString AutoTypePlatformMac::activeWindowTitle()
|
||||||
|
{
|
||||||
|
QString title;
|
||||||
|
|
||||||
|
CFArrayRef windowList = ::CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
|
||||||
|
if (windowList != nullptr) {
|
||||||
|
CFIndex count = ::CFArrayGetCount(windowList);
|
||||||
|
|
||||||
|
for (CFIndex i = 0; i < count; i++) {
|
||||||
|
CFDictionaryRef window = static_cast<CFDictionaryRef>(::CFArrayGetValueAtIndex(windowList, i));
|
||||||
|
if (windowLayer(window) == 0) {
|
||||||
|
// First toplevel window in list (front to back order)
|
||||||
|
title = windowTitle(window);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::CFRelease(windowList);
|
||||||
|
}
|
||||||
|
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Register global hotkey
|
||||||
|
//
|
||||||
|
bool AutoTypePlatformMac::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||||
|
{
|
||||||
|
uint16 nativeKeyCode = qtToNativeKeyCode(key);
|
||||||
|
if (nativeKeyCode == INVALID_KEYCODE) {
|
||||||
|
qWarning("Invalid key code");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint16 nativeModifiers = qtToNativeModifiers(modifiers);
|
||||||
|
if (::RegisterEventHotKey(nativeKeyCode, nativeModifiers, m_hotkeyId, GetApplicationEventTarget(), 0, &m_hotkeyRef) != noErr) {
|
||||||
|
qWarning("Register hotkey failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Unregister global hotkey
|
||||||
|
//
|
||||||
|
void AutoTypePlatformMac::unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||||
|
{
|
||||||
|
Q_UNUSED(key);
|
||||||
|
Q_UNUSED(modifiers);
|
||||||
|
|
||||||
|
::UnregisterEventHotKey(m_hotkeyRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
int AutoTypePlatformMac::platformEventFilter(void* event)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event);
|
||||||
|
Q_ASSERT(false);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoTypeExecutor* AutoTypePlatformMac::createExecutor()
|
||||||
|
{
|
||||||
|
return new AutoTypeExecutorMac(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
int AutoTypePlatformMac::initialTimeout()
|
||||||
|
{
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Activate window by process id
|
||||||
|
//
|
||||||
|
bool AutoTypePlatformMac::raiseWindow(WId pid)
|
||||||
|
{
|
||||||
|
return m_appkit->activateProcess(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Activate last active window
|
||||||
|
//
|
||||||
|
bool AutoTypePlatformMac::raiseLastActiveWindow()
|
||||||
|
{
|
||||||
|
return m_appkit->activateProcess(m_appkit->lastActiveProcessId());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Activate keepassx window
|
||||||
|
//
|
||||||
|
bool AutoTypePlatformMac::raiseOwnWindow()
|
||||||
|
{
|
||||||
|
return m_appkit->activateProcess(m_appkit->ownProcessId());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Send unicode character to active window
|
||||||
|
// see: Quartz Event Services
|
||||||
|
//
|
||||||
|
void AutoTypePlatformMac::sendChar(const QChar& ch, bool isKeyDown)
|
||||||
|
{
|
||||||
|
CGEventRef keyEvent = ::CGEventCreateKeyboardEvent(nullptr, 0, isKeyDown);
|
||||||
|
if (keyEvent != nullptr) {
|
||||||
|
UniChar unicode = ch.unicode();
|
||||||
|
::CGEventKeyboardSetUnicodeString(keyEvent, 1, &unicode);
|
||||||
|
::CGEventPost(kCGSessionEventTap, keyEvent);
|
||||||
|
::CFRelease(keyEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Send key code to active window
|
||||||
|
// see: Quartz Event Services
|
||||||
|
//
|
||||||
|
void AutoTypePlatformMac::sendKey(Qt::Key key, bool isKeyDown)
|
||||||
|
{
|
||||||
|
uint16 keyCode = qtToNativeKeyCode(key);
|
||||||
|
if (keyCode == INVALID_KEYCODE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGEventRef keyEvent = ::CGEventCreateKeyboardEvent(nullptr, keyCode, isKeyDown);
|
||||||
|
if (keyEvent != nullptr) {
|
||||||
|
::CGEventPost(kCGSessionEventTap, keyEvent);
|
||||||
|
::CFRelease(keyEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Translate qt key code to mac os key code
|
||||||
|
// see: HIToolbox/Events.h
|
||||||
|
//
|
||||||
|
uint16 AutoTypePlatformMac::qtToNativeKeyCode(Qt::Key key)
|
||||||
|
{
|
||||||
|
switch (key) {
|
||||||
|
case Qt::Key_A:
|
||||||
|
return kVK_ANSI_A;
|
||||||
|
case Qt::Key_B:
|
||||||
|
return kVK_ANSI_B;
|
||||||
|
case Qt::Key_C:
|
||||||
|
return kVK_ANSI_C;
|
||||||
|
case Qt::Key_D:
|
||||||
|
return kVK_ANSI_D;
|
||||||
|
case Qt::Key_E:
|
||||||
|
return kVK_ANSI_E;
|
||||||
|
case Qt::Key_F:
|
||||||
|
return kVK_ANSI_F;
|
||||||
|
case Qt::Key_G:
|
||||||
|
return kVK_ANSI_G;
|
||||||
|
case Qt::Key_H:
|
||||||
|
return kVK_ANSI_H;
|
||||||
|
case Qt::Key_I:
|
||||||
|
return kVK_ANSI_I;
|
||||||
|
case Qt::Key_J:
|
||||||
|
return kVK_ANSI_J;
|
||||||
|
case Qt::Key_K:
|
||||||
|
return kVK_ANSI_K;
|
||||||
|
case Qt::Key_L:
|
||||||
|
return kVK_ANSI_L;
|
||||||
|
case Qt::Key_M:
|
||||||
|
return kVK_ANSI_M;
|
||||||
|
case Qt::Key_N:
|
||||||
|
return kVK_ANSI_N;
|
||||||
|
case Qt::Key_O:
|
||||||
|
return kVK_ANSI_O;
|
||||||
|
case Qt::Key_P:
|
||||||
|
return kVK_ANSI_P;
|
||||||
|
case Qt::Key_Q:
|
||||||
|
return kVK_ANSI_Q;
|
||||||
|
case Qt::Key_R:
|
||||||
|
return kVK_ANSI_R;
|
||||||
|
case Qt::Key_S:
|
||||||
|
return kVK_ANSI_S;
|
||||||
|
case Qt::Key_T:
|
||||||
|
return kVK_ANSI_T;
|
||||||
|
case Qt::Key_U:
|
||||||
|
return kVK_ANSI_U;
|
||||||
|
case Qt::Key_V:
|
||||||
|
return kVK_ANSI_V;
|
||||||
|
case Qt::Key_W:
|
||||||
|
return kVK_ANSI_W;
|
||||||
|
case Qt::Key_X:
|
||||||
|
return kVK_ANSI_X;
|
||||||
|
case Qt::Key_Y:
|
||||||
|
return kVK_ANSI_Y;
|
||||||
|
case Qt::Key_Z:
|
||||||
|
return kVK_ANSI_Z;
|
||||||
|
|
||||||
|
case Qt::Key_0:
|
||||||
|
return kVK_ANSI_0;
|
||||||
|
case Qt::Key_1:
|
||||||
|
return kVK_ANSI_1;
|
||||||
|
case Qt::Key_2:
|
||||||
|
return kVK_ANSI_2;
|
||||||
|
case Qt::Key_3:
|
||||||
|
return kVK_ANSI_3;
|
||||||
|
case Qt::Key_4:
|
||||||
|
return kVK_ANSI_4;
|
||||||
|
case Qt::Key_5:
|
||||||
|
return kVK_ANSI_5;
|
||||||
|
case Qt::Key_6:
|
||||||
|
return kVK_ANSI_6;
|
||||||
|
case Qt::Key_7:
|
||||||
|
return kVK_ANSI_7;
|
||||||
|
case Qt::Key_8:
|
||||||
|
return kVK_ANSI_8;
|
||||||
|
case Qt::Key_9:
|
||||||
|
return kVK_ANSI_9;
|
||||||
|
|
||||||
|
case Qt::Key_Equal:
|
||||||
|
return kVK_ANSI_Equal;
|
||||||
|
case Qt::Key_Minus:
|
||||||
|
return kVK_ANSI_Minus;
|
||||||
|
case Qt::Key_BracketRight:
|
||||||
|
return kVK_ANSI_RightBracket;
|
||||||
|
case Qt::Key_BracketLeft:
|
||||||
|
return kVK_ANSI_LeftBracket;
|
||||||
|
case Qt::Key_QuoteDbl:
|
||||||
|
return kVK_ANSI_Quote;
|
||||||
|
case Qt::Key_Semicolon:
|
||||||
|
return kVK_ANSI_Semicolon;
|
||||||
|
case Qt::Key_Backslash:
|
||||||
|
return kVK_ANSI_Backslash;
|
||||||
|
case Qt::Key_Comma:
|
||||||
|
return kVK_ANSI_Comma;
|
||||||
|
case Qt::Key_Slash:
|
||||||
|
return kVK_ANSI_Slash;
|
||||||
|
case Qt::Key_Period:
|
||||||
|
return kVK_ANSI_Period;
|
||||||
|
|
||||||
|
case Qt::Key_Backspace:
|
||||||
|
return kVK_Delete;
|
||||||
|
case Qt::Key_Tab:
|
||||||
|
case Qt::Key_Backtab:
|
||||||
|
return kVK_Tab;
|
||||||
|
case Qt::Key_Enter:
|
||||||
|
case Qt::Key_Return:
|
||||||
|
return kVK_Return;
|
||||||
|
case Qt::Key_CapsLock:
|
||||||
|
return kVK_CapsLock;
|
||||||
|
case Qt::Key_Escape:
|
||||||
|
return kVK_Escape;
|
||||||
|
case Qt::Key_Space:
|
||||||
|
return kVK_Space;
|
||||||
|
case Qt::Key_PageUp:
|
||||||
|
return kVK_PageUp;
|
||||||
|
case Qt::Key_PageDown:
|
||||||
|
return kVK_PageDown;
|
||||||
|
case Qt::Key_End:
|
||||||
|
return kVK_End;
|
||||||
|
case Qt::Key_Home:
|
||||||
|
return kVK_Home;
|
||||||
|
case Qt::Key_Left:
|
||||||
|
return kVK_LeftArrow;
|
||||||
|
case Qt::Key_Up:
|
||||||
|
return kVK_UpArrow;
|
||||||
|
case Qt::Key_Right:
|
||||||
|
return kVK_RightArrow;
|
||||||
|
case Qt::Key_Down:
|
||||||
|
return kVK_DownArrow;
|
||||||
|
case Qt::Key_Delete:
|
||||||
|
return kVK_ForwardDelete;
|
||||||
|
case Qt::Key_Help:
|
||||||
|
return kVK_Help;
|
||||||
|
|
||||||
|
case Qt::Key_F1:
|
||||||
|
return kVK_F1;
|
||||||
|
case Qt::Key_F2:
|
||||||
|
return kVK_F2;
|
||||||
|
case Qt::Key_F3:
|
||||||
|
return kVK_F3;
|
||||||
|
case Qt::Key_F4:
|
||||||
|
return kVK_F4;
|
||||||
|
case Qt::Key_F5:
|
||||||
|
return kVK_F5;
|
||||||
|
case Qt::Key_F6:
|
||||||
|
return kVK_F6;
|
||||||
|
case Qt::Key_F7:
|
||||||
|
return kVK_F7;
|
||||||
|
case Qt::Key_F8:
|
||||||
|
return kVK_F8;
|
||||||
|
case Qt::Key_F9:
|
||||||
|
return kVK_F9;
|
||||||
|
case Qt::Key_F10:
|
||||||
|
return kVK_F10;
|
||||||
|
case Qt::Key_F11:
|
||||||
|
return kVK_F11;
|
||||||
|
case Qt::Key_F12:
|
||||||
|
return kVK_F12;
|
||||||
|
case Qt::Key_F13:
|
||||||
|
return kVK_F13;
|
||||||
|
case Qt::Key_F14:
|
||||||
|
return kVK_F14;
|
||||||
|
case Qt::Key_F15:
|
||||||
|
return kVK_F15;
|
||||||
|
case Qt::Key_F16:
|
||||||
|
return kVK_F16;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return INVALID_KEYCODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Translate qt key modifiers to mac os modifiers
|
||||||
|
// see: https://doc.qt.io/qt-5/osx-issues.html#special-keys
|
||||||
|
//
|
||||||
|
uint16 AutoTypePlatformMac::qtToNativeModifiers(Qt::KeyboardModifiers modifiers)
|
||||||
|
{
|
||||||
|
uint16 nativeModifiers = 0;
|
||||||
|
|
||||||
|
if (modifiers & Qt::ShiftModifier) {
|
||||||
|
nativeModifiers |= shiftKey;
|
||||||
|
}
|
||||||
|
if (modifiers & Qt::ControlModifier) {
|
||||||
|
nativeModifiers |= cmdKey;
|
||||||
|
}
|
||||||
|
if (modifiers & Qt::AltModifier) {
|
||||||
|
nativeModifiers |= optionKey;
|
||||||
|
}
|
||||||
|
if (modifiers & Qt::MetaModifier) {
|
||||||
|
nativeModifiers |= controlKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nativeModifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get window layer/level
|
||||||
|
//
|
||||||
|
int AutoTypePlatformMac::windowLayer(CFDictionaryRef window)
|
||||||
|
{
|
||||||
|
int layer;
|
||||||
|
|
||||||
|
CFNumberRef layerRef = static_cast<CFNumberRef>(::CFDictionaryGetValue(window, kCGWindowLayer));
|
||||||
|
if (layerRef != nullptr
|
||||||
|
&& ::CFNumberGetValue(layerRef, kCFNumberIntType, &layer)) {
|
||||||
|
return layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get window title
|
||||||
|
//
|
||||||
|
QString AutoTypePlatformMac::windowTitle(CFDictionaryRef window)
|
||||||
|
{
|
||||||
|
char buffer[MAX_WINDOW_TITLE_LENGTH];
|
||||||
|
QString title;
|
||||||
|
|
||||||
|
CFStringRef titleRef = static_cast<CFStringRef>(::CFDictionaryGetValue(window, kCGWindowName));
|
||||||
|
if (titleRef != nullptr
|
||||||
|
&& ::CFStringGetCString(titleRef, buffer, MAX_WINDOW_TITLE_LENGTH, kCFStringEncodingUTF8)) {
|
||||||
|
title = QString::fromUtf8(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Carbon hotkey handler
|
||||||
|
//
|
||||||
|
OSStatus AutoTypePlatformMac::hotkeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData)
|
||||||
|
{
|
||||||
|
Q_UNUSED(nextHandler);
|
||||||
|
|
||||||
|
AutoTypePlatformMac *self = static_cast<AutoTypePlatformMac *>(userData);
|
||||||
|
EventHotKeyID hotkeyId;
|
||||||
|
|
||||||
|
if (::GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, nullptr, sizeof(hotkeyId), nullptr, &hotkeyId) == noErr
|
||||||
|
&& hotkeyId.id == HOTKEY_ID) {
|
||||||
|
Q_EMIT self->globalShortcutTriggered();
|
||||||
|
}
|
||||||
|
|
||||||
|
return noErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ------------------------------ AutoTypeExecutorMac ------------------------------
|
||||||
|
//
|
||||||
|
|
||||||
|
AutoTypeExecutorMac::AutoTypeExecutorMac(AutoTypePlatformMac* platform)
|
||||||
|
: m_platform(platform)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeExecutorMac::execChar(AutoTypeChar* action)
|
||||||
|
{
|
||||||
|
m_platform->sendChar(action->character, true);
|
||||||
|
m_platform->sendChar(action->character, false);
|
||||||
|
usleep(25 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeExecutorMac::execKey(AutoTypeKey* action)
|
||||||
|
{
|
||||||
|
m_platform->sendKey(action->key, true);
|
||||||
|
m_platform->sendKey(action->key, false);
|
||||||
|
usleep(25 * 1000);
|
||||||
|
}
|
81
src/autotype/mac/AutoTypeMac.h
Normal file
81
src/autotype/mac/AutoTypeMac.h
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Lennart Glauer <mail@lennart-glauer.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSX_AUTOTYPEMAC_H
|
||||||
|
#define KEEPASSX_AUTOTYPEMAC_H
|
||||||
|
|
||||||
|
#include <Carbon/Carbon.h>
|
||||||
|
#include <QtPlugin>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "AppKit.h"
|
||||||
|
#include "autotype/AutoTypePlatformPlugin.h"
|
||||||
|
#include "autotype/AutoTypeAction.h"
|
||||||
|
|
||||||
|
class AutoTypePlatformMac : public QObject, public AutoTypePlatformInterface
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PLUGIN_METADATA(IID "org.keepassx.AutoTypePlatformMac")
|
||||||
|
Q_INTERFACES(AutoTypePlatformInterface)
|
||||||
|
|
||||||
|
public:
|
||||||
|
AutoTypePlatformMac();
|
||||||
|
bool isAvailable() override;
|
||||||
|
QStringList windowTitles() override;
|
||||||
|
WId activeWindow() override;
|
||||||
|
QString activeWindowTitle() override;
|
||||||
|
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||||
|
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||||
|
int platformEventFilter(void* event) override;
|
||||||
|
int initialTimeout() override;
|
||||||
|
bool raiseWindow(WId pid) override;
|
||||||
|
AutoTypeExecutor* createExecutor() override;
|
||||||
|
|
||||||
|
bool raiseLastActiveWindow() override;
|
||||||
|
bool raiseOwnWindow() override;
|
||||||
|
|
||||||
|
void sendChar(const QChar& ch, bool isKeyDown);
|
||||||
|
void sendKey(Qt::Key key, bool isKeyDown);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void globalShortcutTriggered();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<AppKit> m_appkit;
|
||||||
|
EventHotKeyRef m_hotkeyRef;
|
||||||
|
EventHotKeyID m_hotkeyId;
|
||||||
|
|
||||||
|
static uint16 qtToNativeKeyCode(Qt::Key key);
|
||||||
|
static uint16 qtToNativeModifiers(Qt::KeyboardModifiers modifiers);
|
||||||
|
static int windowLayer(CFDictionaryRef window);
|
||||||
|
static QString windowTitle(CFDictionaryRef window);
|
||||||
|
static OSStatus hotkeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoTypeExecutorMac : public AutoTypeExecutor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AutoTypeExecutorMac(AutoTypePlatformMac* platform);
|
||||||
|
|
||||||
|
void execChar(AutoTypeChar* action) override;
|
||||||
|
void execKey(AutoTypeKey* action) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
AutoTypePlatformMac* const m_platform;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSX_AUTOTYPEMAC_H
|
20
src/autotype/mac/CMakeLists.txt
Normal file
20
src/autotype/mac/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
set(autotype_mac_SOURCES
|
||||||
|
AutoTypeMac.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(autotype_mac_mm_SOURCES
|
||||||
|
AppKitImpl.mm
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(keepassx-autotype-cocoa MODULE ${autotype_mac_SOURCES} ${autotype_mac_mm_SOURCES})
|
||||||
|
set_target_properties(keepassx-autotype-cocoa PROPERTIES LINK_FLAGS "-framework Foundation -framework AppKit -framework Carbon")
|
||||||
|
target_link_libraries(keepassx-autotype-cocoa ${PROGNAME} Qt5::Core Qt5::Widgets)
|
||||||
|
if(NOT DEFINED QT_BINARY_DIR)
|
||||||
|
set(QT_BINARY_DIR "/usr/local/opt/qt5/bin" CACHE PATH "QT binary folder")
|
||||||
|
endif()
|
||||||
|
add_custom_command(TARGET keepassx-autotype-cocoa
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/libkeepassx-autotype-cocoa.so ${PLUGIN_INSTALL_DIR}
|
||||||
|
COMMAND ${QT_BINARY_DIR}/macdeployqt ${PROGNAME}.app -executable=${PLUGIN_INSTALL_DIR}/libkeepassx-autotype-cocoa.so -no-plugins
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src
|
||||||
|
COMMENT "Deploying autotype plugin")
|
@ -115,6 +115,18 @@ bool AutoTypePlatformTest::raiseWindow(WId window)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
bool AutoTypePlatformTest::raiseLastActiveWindow()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoTypePlatformTest::raiseOwnWindow()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
AutoTypeExecturorTest::AutoTypeExecturorTest(AutoTypePlatformTest* platform)
|
AutoTypeExecturorTest::AutoTypeExecturorTest(AutoTypePlatformTest* platform)
|
||||||
: m_platform(platform)
|
: m_platform(platform)
|
||||||
{
|
{
|
||||||
|
@ -46,6 +46,11 @@ public:
|
|||||||
bool raiseWindow(WId window) override;
|
bool raiseWindow(WId window) override;
|
||||||
AutoTypeExecutor* createExecutor() override;
|
AutoTypeExecutor* createExecutor() override;
|
||||||
|
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
bool raiseLastActiveWindow() override;
|
||||||
|
bool raiseOwnWindow() override;
|
||||||
|
#endif
|
||||||
|
|
||||||
void setActiveWindowTitle(const QString& title) override;
|
void setActiveWindowTitle(const QString& title) override;
|
||||||
|
|
||||||
QString actionChars() override;
|
QString actionChars() override;
|
||||||
|
529
src/autotype/windows/AutoTypeWindows.cpp
Normal file
529
src/autotype/windows/AutoTypeWindows.cpp
Normal file
@ -0,0 +1,529 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Lennart Glauer <mail@lennart-glauer.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "AutoTypeWindows.h"
|
||||||
|
|
||||||
|
#include <VersionHelpers.h>
|
||||||
|
|
||||||
|
#define HOTKEY_ID 1
|
||||||
|
#define MAX_WINDOW_TITLE_LENGTH 1024
|
||||||
|
|
||||||
|
#define MOD_NOREPEAT 0x4000 // Missing in MinGW
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test if os version is Windows 7 or later
|
||||||
|
//
|
||||||
|
bool AutoTypePlatformWin::isAvailable()
|
||||||
|
{
|
||||||
|
return IsWindows7OrGreater();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get list of all visible window titles
|
||||||
|
//
|
||||||
|
QStringList AutoTypePlatformWin::windowTitles()
|
||||||
|
{
|
||||||
|
QStringList list;
|
||||||
|
|
||||||
|
::EnumWindows(AutoTypePlatformWin::windowTitleEnumProc, reinterpret_cast<LPARAM>(&list));
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get foreground window hwnd
|
||||||
|
//
|
||||||
|
WId AutoTypePlatformWin::activeWindow()
|
||||||
|
{
|
||||||
|
return reinterpret_cast<WId>(::GetForegroundWindow());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get foreground window title
|
||||||
|
//
|
||||||
|
QString AutoTypePlatformWin::activeWindowTitle()
|
||||||
|
{
|
||||||
|
return windowTitle(::GetForegroundWindow());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Register global hotkey
|
||||||
|
//
|
||||||
|
bool AutoTypePlatformWin::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||||
|
{
|
||||||
|
DWORD nativeKeyCode = qtToNativeKeyCode(key);
|
||||||
|
if (nativeKeyCode < 1 || nativeKeyCode > 254) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DWORD nativeModifiers = qtToNativeModifiers(modifiers);
|
||||||
|
if (!::RegisterHotKey(nullptr, HOTKEY_ID, nativeModifiers | MOD_NOREPEAT, nativeKeyCode)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Unregister global hotkey
|
||||||
|
//
|
||||||
|
void AutoTypePlatformWin::unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||||
|
{
|
||||||
|
Q_UNUSED(key);
|
||||||
|
Q_UNUSED(modifiers);
|
||||||
|
|
||||||
|
::UnregisterHotKey(nullptr, HOTKEY_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Native event filter
|
||||||
|
//
|
||||||
|
int AutoTypePlatformWin::platformEventFilter(void* event)
|
||||||
|
{
|
||||||
|
MSG *msg = static_cast<MSG *>(event);
|
||||||
|
|
||||||
|
if (msg->message == WM_HOTKEY && msg->wParam == HOTKEY_ID) {
|
||||||
|
Q_EMIT globalShortcutTriggered();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoTypeExecutor* AutoTypePlatformWin::createExecutor()
|
||||||
|
{
|
||||||
|
return new AutoTypeExecutorWin(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
int AutoTypePlatformWin::initialTimeout()
|
||||||
|
{
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Set foreground window
|
||||||
|
//
|
||||||
|
bool AutoTypePlatformWin::raiseWindow(WId window)
|
||||||
|
{
|
||||||
|
HWND hwnd = reinterpret_cast<HWND>(window);
|
||||||
|
|
||||||
|
return ::BringWindowToTop(hwnd) && ::SetForegroundWindow(hwnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Send unicode character to foreground window
|
||||||
|
//
|
||||||
|
void AutoTypePlatformWin::sendChar(const QChar& ch, bool isKeyDown)
|
||||||
|
{
|
||||||
|
DWORD nativeFlags = KEYEVENTF_UNICODE;
|
||||||
|
if (!isKeyDown) {
|
||||||
|
nativeFlags |= KEYEVENTF_KEYUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
INPUT in;
|
||||||
|
in.type = INPUT_KEYBOARD;
|
||||||
|
in.ki.wVk = 0;
|
||||||
|
in.ki.wScan = ch.unicode();
|
||||||
|
in.ki.dwFlags = nativeFlags;
|
||||||
|
in.ki.time = 0;
|
||||||
|
in.ki.dwExtraInfo = ::GetMessageExtraInfo();
|
||||||
|
|
||||||
|
::SendInput(1, &in, sizeof(INPUT));
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Send virtual key code to foreground window
|
||||||
|
//
|
||||||
|
void AutoTypePlatformWin::sendKey(Qt::Key key, bool isKeyDown)
|
||||||
|
{
|
||||||
|
DWORD nativeKeyCode = qtToNativeKeyCode(key);
|
||||||
|
if (nativeKeyCode < 1 || nativeKeyCode > 254) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DWORD nativeFlags = 0;
|
||||||
|
if (isExtendedKey(nativeKeyCode)) {
|
||||||
|
nativeFlags |= KEYEVENTF_EXTENDEDKEY;
|
||||||
|
}
|
||||||
|
if (!isKeyDown) {
|
||||||
|
nativeFlags |= KEYEVENTF_KEYUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
INPUT in;
|
||||||
|
in.type = INPUT_KEYBOARD;
|
||||||
|
in.ki.wVk = LOWORD(nativeKeyCode);
|
||||||
|
in.ki.wScan = LOWORD(::MapVirtualKeyW(nativeKeyCode, MAPVK_VK_TO_VSC));
|
||||||
|
in.ki.dwFlags = nativeFlags;
|
||||||
|
in.ki.time = 0;
|
||||||
|
in.ki.dwExtraInfo = ::GetMessageExtraInfo();
|
||||||
|
|
||||||
|
::SendInput(1, &in, sizeof(INPUT));
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Translate qt key code to windows virtual key code
|
||||||
|
// see: https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
|
||||||
|
//
|
||||||
|
DWORD AutoTypePlatformWin::qtToNativeKeyCode(Qt::Key key)
|
||||||
|
{
|
||||||
|
switch (key) {
|
||||||
|
case Qt::Key_Backspace:
|
||||||
|
return VK_BACK; // 0x08
|
||||||
|
case Qt::Key_Tab:
|
||||||
|
case Qt::Key_Backtab:
|
||||||
|
return VK_TAB; // 0x09
|
||||||
|
case Qt::Key_Clear:
|
||||||
|
return VK_CLEAR; // 0x0C
|
||||||
|
case Qt::Key_Enter:
|
||||||
|
case Qt::Key_Return:
|
||||||
|
return VK_RETURN; // 0x0D
|
||||||
|
case Qt::Key_Pause:
|
||||||
|
return VK_PAUSE; // 0x13
|
||||||
|
case Qt::Key_CapsLock:
|
||||||
|
return VK_CAPITAL; // 0x14
|
||||||
|
case Qt::Key_Escape:
|
||||||
|
return VK_ESCAPE; // 0x1B
|
||||||
|
case Qt::Key_Space:
|
||||||
|
return VK_SPACE; // 0x20
|
||||||
|
case Qt::Key_PageUp:
|
||||||
|
return VK_PRIOR; // 0x21
|
||||||
|
case Qt::Key_PageDown:
|
||||||
|
return VK_NEXT; // 0x22
|
||||||
|
case Qt::Key_End:
|
||||||
|
return VK_END; // 0x23
|
||||||
|
case Qt::Key_Home:
|
||||||
|
return VK_HOME; // 0x24
|
||||||
|
case Qt::Key_Left:
|
||||||
|
return VK_LEFT; // 0x25
|
||||||
|
case Qt::Key_Up:
|
||||||
|
return VK_UP; // 0x26
|
||||||
|
case Qt::Key_Right:
|
||||||
|
return VK_RIGHT; // 0x27
|
||||||
|
case Qt::Key_Down:
|
||||||
|
return VK_DOWN; // 0x28
|
||||||
|
case Qt::Key_Print:
|
||||||
|
return VK_SNAPSHOT; // 0x2C
|
||||||
|
case Qt::Key_Insert:
|
||||||
|
return VK_INSERT; // 0x2D
|
||||||
|
case Qt::Key_Delete:
|
||||||
|
return VK_DELETE; // 0x2E
|
||||||
|
case Qt::Key_Help:
|
||||||
|
return VK_HELP; // 0x2F
|
||||||
|
|
||||||
|
case Qt::Key_0:
|
||||||
|
return 0x30; // 0x30
|
||||||
|
case Qt::Key_1:
|
||||||
|
return 0x31; // 0x31
|
||||||
|
case Qt::Key_2:
|
||||||
|
return 0x32; // 0x32
|
||||||
|
case Qt::Key_3:
|
||||||
|
return 0x33; // 0x33
|
||||||
|
case Qt::Key_4:
|
||||||
|
return 0x34; // 0x34
|
||||||
|
case Qt::Key_5:
|
||||||
|
return 0x35; // 0x35
|
||||||
|
case Qt::Key_6:
|
||||||
|
return 0x36; // 0x36
|
||||||
|
case Qt::Key_7:
|
||||||
|
return 0x37; // 0x37
|
||||||
|
case Qt::Key_8:
|
||||||
|
return 0x38; // 0x38
|
||||||
|
case Qt::Key_9:
|
||||||
|
return 0x39; // 0x39
|
||||||
|
|
||||||
|
case Qt::Key_A:
|
||||||
|
return 0x41; // 0x41
|
||||||
|
case Qt::Key_B:
|
||||||
|
return 0x42; // 0x42
|
||||||
|
case Qt::Key_C:
|
||||||
|
return 0x43; // 0x43
|
||||||
|
case Qt::Key_D:
|
||||||
|
return 0x44; // 0x44
|
||||||
|
case Qt::Key_E:
|
||||||
|
return 0x45; // 0x45
|
||||||
|
case Qt::Key_F:
|
||||||
|
return 0x46; // 0x46
|
||||||
|
case Qt::Key_G:
|
||||||
|
return 0x47; // 0x47
|
||||||
|
case Qt::Key_H:
|
||||||
|
return 0x48; // 0x48
|
||||||
|
case Qt::Key_I:
|
||||||
|
return 0x49; // 0x49
|
||||||
|
case Qt::Key_J:
|
||||||
|
return 0x4A; // 0x4A
|
||||||
|
case Qt::Key_K:
|
||||||
|
return 0x4B; // 0x4B
|
||||||
|
case Qt::Key_L:
|
||||||
|
return 0x4C; // 0x4C
|
||||||
|
case Qt::Key_M:
|
||||||
|
return 0x4D; // 0x4D
|
||||||
|
case Qt::Key_N:
|
||||||
|
return 0x4E; // 0x4E
|
||||||
|
case Qt::Key_O:
|
||||||
|
return 0x4F; // 0x4F
|
||||||
|
case Qt::Key_P:
|
||||||
|
return 0x50; // 0x50
|
||||||
|
case Qt::Key_Q:
|
||||||
|
return 0x51; // 0x51
|
||||||
|
case Qt::Key_R:
|
||||||
|
return 0x52; // 0x52
|
||||||
|
case Qt::Key_S:
|
||||||
|
return 0x53; // 0x53
|
||||||
|
case Qt::Key_T:
|
||||||
|
return 0x54; // 0x54
|
||||||
|
case Qt::Key_U:
|
||||||
|
return 0x55; // 0x55
|
||||||
|
case Qt::Key_V:
|
||||||
|
return 0x56; // 0x56
|
||||||
|
case Qt::Key_W:
|
||||||
|
return 0x57; // 0x57
|
||||||
|
case Qt::Key_X:
|
||||||
|
return 0x58; // 0x58
|
||||||
|
case Qt::Key_Y:
|
||||||
|
return 0x59; // 0x59
|
||||||
|
case Qt::Key_Z:
|
||||||
|
return 0x5A; // 0x5A
|
||||||
|
|
||||||
|
case Qt::Key_F1:
|
||||||
|
return VK_F1; // 0x70
|
||||||
|
case Qt::Key_F2:
|
||||||
|
return VK_F2; // 0x71
|
||||||
|
case Qt::Key_F3:
|
||||||
|
return VK_F3; // 0x72
|
||||||
|
case Qt::Key_F4:
|
||||||
|
return VK_F4; // 0x73
|
||||||
|
case Qt::Key_F5:
|
||||||
|
return VK_F5; // 0x74
|
||||||
|
case Qt::Key_F6:
|
||||||
|
return VK_F6; // 0x75
|
||||||
|
case Qt::Key_F7:
|
||||||
|
return VK_F7; // 0x76
|
||||||
|
case Qt::Key_F8:
|
||||||
|
return VK_F8; // 0x77
|
||||||
|
case Qt::Key_F9:
|
||||||
|
return VK_F9; // 0x78
|
||||||
|
case Qt::Key_F10:
|
||||||
|
return VK_F10; // 0x79
|
||||||
|
case Qt::Key_F11:
|
||||||
|
return VK_F11; // 0x7A
|
||||||
|
case Qt::Key_F12:
|
||||||
|
return VK_F12; // 0x7B
|
||||||
|
case Qt::Key_F13:
|
||||||
|
return VK_F13; // 0x7C
|
||||||
|
case Qt::Key_F14:
|
||||||
|
return VK_F14; // 0x7D
|
||||||
|
case Qt::Key_F15:
|
||||||
|
return VK_F15; // 0x7E
|
||||||
|
case Qt::Key_F16:
|
||||||
|
return VK_F16; // 0x7F
|
||||||
|
case Qt::Key_F17:
|
||||||
|
return VK_F17; // 0x80
|
||||||
|
case Qt::Key_F18:
|
||||||
|
return VK_F18; // 0x81
|
||||||
|
case Qt::Key_F19:
|
||||||
|
return VK_F19; // 0x82
|
||||||
|
case Qt::Key_F20:
|
||||||
|
return VK_F20; // 0x83
|
||||||
|
case Qt::Key_F21:
|
||||||
|
return VK_F21; // 0x84
|
||||||
|
case Qt::Key_F22:
|
||||||
|
return VK_F22; // 0x85
|
||||||
|
case Qt::Key_F23:
|
||||||
|
return VK_F23; // 0x86
|
||||||
|
case Qt::Key_F24:
|
||||||
|
return VK_F24; // 0x87
|
||||||
|
|
||||||
|
case Qt::Key_NumLock:
|
||||||
|
return VK_NUMLOCK; // 0x90
|
||||||
|
case Qt::Key_ScrollLock:
|
||||||
|
return VK_SCROLL; // 0x91
|
||||||
|
|
||||||
|
case Qt::Key_Exclam: // !
|
||||||
|
case Qt::Key_QuoteDbl: // "
|
||||||
|
case Qt::Key_NumberSign: // #
|
||||||
|
case Qt::Key_Dollar: // $
|
||||||
|
case Qt::Key_Percent: // %
|
||||||
|
case Qt::Key_Ampersand: // &
|
||||||
|
case Qt::Key_Apostrophe: // '
|
||||||
|
case Qt::Key_ParenLeft: // (
|
||||||
|
case Qt::Key_ParenRight: // )
|
||||||
|
case Qt::Key_Asterisk: // *
|
||||||
|
case Qt::Key_Plus: // +
|
||||||
|
case Qt::Key_Comma: // ,
|
||||||
|
case Qt::Key_Minus: // -
|
||||||
|
case Qt::Key_Period: // .
|
||||||
|
case Qt::Key_Slash: // /
|
||||||
|
case Qt::Key_Colon: // :
|
||||||
|
case Qt::Key_Semicolon: // ;
|
||||||
|
case Qt::Key_Less: // <
|
||||||
|
case Qt::Key_Equal: // =
|
||||||
|
case Qt::Key_Greater: // >
|
||||||
|
case Qt::Key_Question: // ?
|
||||||
|
case Qt::Key_BracketLeft: // [
|
||||||
|
case Qt::Key_Backslash: // '\'
|
||||||
|
case Qt::Key_BracketRight: // ]
|
||||||
|
case Qt::Key_AsciiCircum: // ^
|
||||||
|
case Qt::Key_Underscore: // _
|
||||||
|
case Qt::Key_QuoteLeft: // `
|
||||||
|
case Qt::Key_BraceLeft: // {
|
||||||
|
case Qt::Key_Bar: // |
|
||||||
|
case Qt::Key_BraceRight: // }
|
||||||
|
case Qt::Key_AsciiTilde: // ~
|
||||||
|
return LOBYTE(::VkKeyScanExW(key, ::GetKeyboardLayout(0)));
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// The extended-key flag indicates whether the keystroke message originated
|
||||||
|
// from one of the additional keys on the enhanced keyboard
|
||||||
|
// see: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646267%28v=vs.85%29.aspx#EXTENDED_KEY_FLAG
|
||||||
|
//
|
||||||
|
BOOL AutoTypePlatformWin::isExtendedKey(DWORD nativeKeyCode)
|
||||||
|
{
|
||||||
|
switch (nativeKeyCode) {
|
||||||
|
case VK_RMENU:
|
||||||
|
case VK_RCONTROL:
|
||||||
|
case VK_INSERT:
|
||||||
|
case VK_DELETE:
|
||||||
|
case VK_HOME:
|
||||||
|
case VK_END:
|
||||||
|
case VK_PRIOR:
|
||||||
|
case VK_NEXT:
|
||||||
|
case VK_LEFT:
|
||||||
|
case VK_UP:
|
||||||
|
case VK_RIGHT:
|
||||||
|
case VK_DOWN:
|
||||||
|
case VK_NUMLOCK:
|
||||||
|
case VK_CANCEL:
|
||||||
|
case VK_SNAPSHOT:
|
||||||
|
case VK_DIVIDE:
|
||||||
|
case VK_LWIN:
|
||||||
|
case VK_RWIN:
|
||||||
|
case VK_APPS:
|
||||||
|
return TRUE;
|
||||||
|
default:
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Translate qt key modifiers to windows modifiers
|
||||||
|
//
|
||||||
|
DWORD AutoTypePlatformWin::qtToNativeModifiers(Qt::KeyboardModifiers modifiers)
|
||||||
|
{
|
||||||
|
DWORD nativeModifiers = 0;
|
||||||
|
|
||||||
|
if (modifiers & Qt::ShiftModifier) {
|
||||||
|
nativeModifiers |= MOD_SHIFT;
|
||||||
|
}
|
||||||
|
if (modifiers & Qt::ControlModifier) {
|
||||||
|
nativeModifiers |= MOD_CONTROL;
|
||||||
|
}
|
||||||
|
if (modifiers & Qt::AltModifier) {
|
||||||
|
nativeModifiers |= MOD_ALT;
|
||||||
|
}
|
||||||
|
if (modifiers & Qt::MetaModifier) {
|
||||||
|
nativeModifiers |= MOD_WIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nativeModifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test if window is in Alt+Tab list
|
||||||
|
// see: https://blogs.msdn.microsoft.com/oldnewthing/20071008-00/?p=24863
|
||||||
|
//
|
||||||
|
BOOL AutoTypePlatformWin::isAltTabWindow(HWND hwnd)
|
||||||
|
{
|
||||||
|
if (!::IsWindowVisible(hwnd)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start at the root owner
|
||||||
|
HWND hwndWalk = ::GetAncestor(hwnd, GA_ROOTOWNER);
|
||||||
|
HWND hwndTry;
|
||||||
|
|
||||||
|
// See if we are the last active visible popup
|
||||||
|
while ((hwndTry = ::GetLastActivePopup(hwndWalk)) != hwndWalk) {
|
||||||
|
if (::IsWindowVisible(hwndTry)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hwndWalk = hwndTry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hwndWalk == hwnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Window title enum proc
|
||||||
|
//
|
||||||
|
BOOL CALLBACK AutoTypePlatformWin::windowTitleEnumProc(
|
||||||
|
_In_ HWND hwnd,
|
||||||
|
_In_ LPARAM lParam
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!isAltTabWindow(hwnd)) {
|
||||||
|
// Skip window
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList *list = reinterpret_cast<QStringList *>(lParam);
|
||||||
|
QString title = windowTitle(hwnd);
|
||||||
|
|
||||||
|
if (!title.isEmpty()) {
|
||||||
|
list->append(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get window title
|
||||||
|
//
|
||||||
|
QString AutoTypePlatformWin::windowTitle(HWND hwnd)
|
||||||
|
{
|
||||||
|
wchar_t title[MAX_WINDOW_TITLE_LENGTH];
|
||||||
|
int count = ::GetWindowTextW(hwnd, title, MAX_WINDOW_TITLE_LENGTH);
|
||||||
|
|
||||||
|
return QString::fromUtf16(reinterpret_cast<const ushort *>(title), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ------------------------------ AutoTypeExecutorWin ------------------------------
|
||||||
|
//
|
||||||
|
|
||||||
|
AutoTypeExecutorWin::AutoTypeExecutorWin(AutoTypePlatformWin* platform)
|
||||||
|
: m_platform(platform)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeExecutorWin::execChar(AutoTypeChar* action)
|
||||||
|
{
|
||||||
|
m_platform->sendChar(action->character, true);
|
||||||
|
m_platform->sendChar(action->character, false);
|
||||||
|
::Sleep(25);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeExecutorWin::execKey(AutoTypeKey* action)
|
||||||
|
{
|
||||||
|
m_platform->sendKey(action->key, true);
|
||||||
|
m_platform->sendKey(action->key, false);
|
||||||
|
::Sleep(25);
|
||||||
|
}
|
||||||
|
|
73
src/autotype/windows/AutoTypeWindows.h
Normal file
73
src/autotype/windows/AutoTypeWindows.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Lennart Glauer <mail@lennart-glauer.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSX_AUTOTYPEWINDOWS_H
|
||||||
|
#define KEEPASSX_AUTOTYPEWINDOWS_H
|
||||||
|
|
||||||
|
#include <QtPlugin>
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#include "autotype/AutoTypePlatformPlugin.h"
|
||||||
|
#include "autotype/AutoTypeAction.h"
|
||||||
|
|
||||||
|
class AutoTypePlatformWin : public QObject, public AutoTypePlatformInterface
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PLUGIN_METADATA(IID "org.keepassx.AutoTypePlatformWindows")
|
||||||
|
Q_INTERFACES(AutoTypePlatformInterface)
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool isAvailable() override;
|
||||||
|
QStringList windowTitles() override;
|
||||||
|
WId activeWindow() override;
|
||||||
|
QString activeWindowTitle() override;
|
||||||
|
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||||
|
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||||
|
int platformEventFilter(void* event) override;
|
||||||
|
int initialTimeout() override;
|
||||||
|
bool raiseWindow(WId window) override;
|
||||||
|
AutoTypeExecutor* createExecutor() override;
|
||||||
|
|
||||||
|
void sendChar(const QChar& ch, bool isKeyDown);
|
||||||
|
void sendKey(Qt::Key key, bool isKeyDown);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void globalShortcutTriggered();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static DWORD qtToNativeKeyCode(Qt::Key key);
|
||||||
|
static DWORD qtToNativeModifiers(Qt::KeyboardModifiers modifiers);
|
||||||
|
static BOOL isExtendedKey(DWORD nativeKeyCode);
|
||||||
|
static BOOL isAltTabWindow(HWND hwnd);
|
||||||
|
static BOOL CALLBACK windowTitleEnumProc(_In_ HWND hwnd, _In_ LPARAM lParam);
|
||||||
|
static QString windowTitle(HWND hwnd);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoTypeExecutorWin : public AutoTypeExecutor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AutoTypeExecutorWin(AutoTypePlatformWin* platform);
|
||||||
|
|
||||||
|
void execChar(AutoTypeChar* action) override;
|
||||||
|
void execKey(AutoTypeKey* action) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
AutoTypePlatformWin* const m_platform;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSX_AUTOTYPEWINDOWS_H
|
||||||
|
|
9
src/autotype/windows/CMakeLists.txt
Normal file
9
src/autotype/windows/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
set(autotype_win_SOURCES
|
||||||
|
AutoTypeWindows.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(keepassx-autotype-windows MODULE ${autotype_win_SOURCES})
|
||||||
|
target_link_libraries(keepassx-autotype-windows ${PROGNAME} Qt5::Core Qt5::Widgets)
|
||||||
|
install(TARGETS keepassx-autotype-windows
|
||||||
|
BUNDLE DESTINATION . COMPONENT Runtime
|
||||||
|
LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR} COMPONENT Runtime)
|
@ -185,6 +185,35 @@ QString Entry::defaultAutoTypeSequence() const
|
|||||||
return m_data.defaultAutoTypeSequence;
|
return m_data.defaultAutoTypeSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Entry::effectiveAutoTypeSequence() const
|
||||||
|
{
|
||||||
|
if (!m_data.defaultAutoTypeSequence.isEmpty()) {
|
||||||
|
return m_data.defaultAutoTypeSequence;
|
||||||
|
}
|
||||||
|
QString sequence;
|
||||||
|
|
||||||
|
const Group* grp = group();
|
||||||
|
if(grp) {
|
||||||
|
sequence = grp->effectiveAutoTypeSequence();
|
||||||
|
} else {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sequence.isEmpty() && (!username().isEmpty() || !password().isEmpty())) {
|
||||||
|
if (username().isEmpty()) {
|
||||||
|
sequence = "{PASSWORD}{ENTER}";
|
||||||
|
}
|
||||||
|
else if (password().isEmpty()) {
|
||||||
|
sequence = "{USERNAME}{ENTER}";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sequence;
|
||||||
|
}
|
||||||
|
|
||||||
AutoTypeAssociations* Entry::autoTypeAssociations()
|
AutoTypeAssociations* Entry::autoTypeAssociations()
|
||||||
{
|
{
|
||||||
return m_autoTypeAssociations;
|
return m_autoTypeAssociations;
|
||||||
|
@ -70,6 +70,7 @@ public:
|
|||||||
bool autoTypeEnabled() const;
|
bool autoTypeEnabled() const;
|
||||||
int autoTypeObfuscation() const;
|
int autoTypeObfuscation() const;
|
||||||
QString defaultAutoTypeSequence() const;
|
QString defaultAutoTypeSequence() const;
|
||||||
|
QString effectiveAutoTypeSequence() const;
|
||||||
AutoTypeAssociations* autoTypeAssociations();
|
AutoTypeAssociations* autoTypeAssociations();
|
||||||
const AutoTypeAssociations* autoTypeAssociations() const;
|
const AutoTypeAssociations* autoTypeAssociations() const;
|
||||||
QString title() const;
|
QString title() const;
|
||||||
|
@ -49,6 +49,10 @@ QString FilePath::pluginPath(const QString& name)
|
|||||||
// for TestAutoType
|
// for TestAutoType
|
||||||
pluginPaths << QCoreApplication::applicationDirPath() + "/../src/autotype/test";
|
pluginPaths << QCoreApplication::applicationDirPath() + "/../src/autotype/test";
|
||||||
|
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
pluginPaths << QCoreApplication::applicationDirPath() + "/../PlugIns";
|
||||||
|
#endif
|
||||||
|
|
||||||
pluginPaths << QCoreApplication::applicationDirPath();
|
pluginPaths << QCoreApplication::applicationDirPath();
|
||||||
|
|
||||||
QString configuredPluginDir = KEEPASSX_PLUGIN_DIR;
|
QString configuredPluginDir = KEEPASSX_PLUGIN_DIR;
|
||||||
|
@ -187,6 +187,23 @@ QString Group::defaultAutoTypeSequence() const
|
|||||||
return m_data.defaultAutoTypeSequence;
|
return m_data.defaultAutoTypeSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Group::effectiveAutoTypeSequence() const
|
||||||
|
{
|
||||||
|
QString sequence;
|
||||||
|
|
||||||
|
const Group* group = this;
|
||||||
|
do {
|
||||||
|
if (group->autoTypeEnabled() == Group::Disable) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
sequence = group->defaultAutoTypeSequence();
|
||||||
|
group = group->parentGroup();
|
||||||
|
} while (group && sequence.isEmpty());
|
||||||
|
|
||||||
|
return sequence;
|
||||||
|
}
|
||||||
|
|
||||||
Group::TriState Group::autoTypeEnabled() const
|
Group::TriState Group::autoTypeEnabled() const
|
||||||
{
|
{
|
||||||
return m_data.autoTypeEnabled;
|
return m_data.autoTypeEnabled;
|
||||||
|
@ -66,6 +66,7 @@ public:
|
|||||||
TimeInfo timeInfo() const;
|
TimeInfo timeInfo() const;
|
||||||
bool isExpanded() const;
|
bool isExpanded() const;
|
||||||
QString defaultAutoTypeSequence() const;
|
QString defaultAutoTypeSequence() const;
|
||||||
|
QString effectiveAutoTypeSequence() const;
|
||||||
Group::TriState autoTypeEnabled() const;
|
Group::TriState autoTypeEnabled() const;
|
||||||
Group::TriState searchingEnabled() const;
|
Group::TriState searchingEnabled() const;
|
||||||
Group::MergeMode mergeMode() const;
|
Group::MergeMode mergeMode() const;
|
||||||
|
@ -41,6 +41,25 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
#elif defined(Q_OS_WIN)
|
||||||
|
class WinEventFilter : public QAbstractNativeEventFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override
|
||||||
|
{
|
||||||
|
Q_UNUSED(result);
|
||||||
|
|
||||||
|
if (eventType == QByteArrayLiteral("windows_generic_MSG")
|
||||||
|
|| eventType == QByteArrayLiteral("windows_dispatcher_MSG")) {
|
||||||
|
int retCode = autoType()->callEventFilter(message);
|
||||||
|
if (retCode == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Application::Application(int& argc, char** argv)
|
Application::Application(int& argc, char** argv)
|
||||||
@ -49,6 +68,8 @@ Application::Application(int& argc, char** argv)
|
|||||||
{
|
{
|
||||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX)
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX)
|
||||||
installNativeEventFilter(new XcbEventFilter());
|
installNativeEventFilter(new XcbEventFilter());
|
||||||
|
#elif defined(Q_OS_WIN)
|
||||||
|
installNativeEventFilter(new WinEventFilter());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include "gui/MessageBox.h"
|
#include "gui/MessageBox.h"
|
||||||
#include "gui/entry/EntryView.h"
|
#include "gui/entry/EntryView.h"
|
||||||
#include "gui/group/GroupView.h"
|
#include "gui/group/GroupView.h"
|
||||||
|
#include "gui/UnlockDatabaseDialog.h"
|
||||||
|
|
||||||
DatabaseManagerStruct::DatabaseManagerStruct()
|
DatabaseManagerStruct::DatabaseManagerStruct()
|
||||||
: dbWidget(nullptr)
|
: dbWidget(nullptr)
|
||||||
@ -235,6 +236,7 @@ bool DatabaseTabWidget::closeDatabase(Database* db)
|
|||||||
int index = databaseIndex(db);
|
int index = databaseIndex(db);
|
||||||
Q_ASSERT(index != -1);
|
Q_ASSERT(index != -1);
|
||||||
|
|
||||||
|
dbStruct.dbWidget->closeUnlockDialog();
|
||||||
QString dbName = tabText(index);
|
QString dbName = tabText(index);
|
||||||
if (dbName.right(1) == "*") {
|
if (dbName.right(1) == "*") {
|
||||||
dbName.chop(1);
|
dbName.chop(1);
|
||||||
@ -813,5 +815,9 @@ void DatabaseTabWidget::performGlobalAutoType()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
autoType()->performGlobalAutoType(unlockedDatabases);
|
if (unlockedDatabases.size() > 0) {
|
||||||
|
autoType()->performGlobalAutoType(unlockedDatabases);
|
||||||
|
} else if (m_dbList.size() > 0){
|
||||||
|
indexDatabaseManagerStruct(0).dbWidget->showUnlockDialog();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include "gui/KeePass1OpenWidget.h"
|
#include "gui/KeePass1OpenWidget.h"
|
||||||
#include "gui/MessageBox.h"
|
#include "gui/MessageBox.h"
|
||||||
#include "gui/UnlockDatabaseWidget.h"
|
#include "gui/UnlockDatabaseWidget.h"
|
||||||
|
#include "gui/UnlockDatabaseDialog.h"
|
||||||
#include "gui/entry/EditEntryWidget.h"
|
#include "gui/entry/EditEntryWidget.h"
|
||||||
#include "gui/entry/EntryView.h"
|
#include "gui/entry/EntryView.h"
|
||||||
#include "gui/group/EditGroupWidget.h"
|
#include "gui/group/EditGroupWidget.h"
|
||||||
@ -127,6 +128,8 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
|
|||||||
m_keepass1OpenWidget->setObjectName("keepass1OpenWidget");
|
m_keepass1OpenWidget->setObjectName("keepass1OpenWidget");
|
||||||
m_unlockDatabaseWidget = new UnlockDatabaseWidget();
|
m_unlockDatabaseWidget = new UnlockDatabaseWidget();
|
||||||
m_unlockDatabaseWidget->setObjectName("unlockDatabaseWidget");
|
m_unlockDatabaseWidget->setObjectName("unlockDatabaseWidget");
|
||||||
|
m_unlockDatabaseDialog = new UnlockDatabaseDialog();
|
||||||
|
m_unlockDatabaseDialog->setObjectName("unlockDatabaseDialog");
|
||||||
addWidget(m_mainWidget);
|
addWidget(m_mainWidget);
|
||||||
addWidget(m_editEntryWidget);
|
addWidget(m_editEntryWidget);
|
||||||
addWidget(m_editGroupWidget);
|
addWidget(m_editGroupWidget);
|
||||||
@ -156,6 +159,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
|
|||||||
connect(m_databaseOpenMergeWidget, SIGNAL(editFinished(bool)), SLOT(mergeDatabase(bool)));
|
connect(m_databaseOpenMergeWidget, SIGNAL(editFinished(bool)), SLOT(mergeDatabase(bool)));
|
||||||
connect(m_keepass1OpenWidget, SIGNAL(editFinished(bool)), SLOT(openDatabase(bool)));
|
connect(m_keepass1OpenWidget, SIGNAL(editFinished(bool)), SLOT(openDatabase(bool)));
|
||||||
connect(m_unlockDatabaseWidget, SIGNAL(editFinished(bool)), SLOT(unlockDatabase(bool)));
|
connect(m_unlockDatabaseWidget, SIGNAL(editFinished(bool)), SLOT(unlockDatabase(bool)));
|
||||||
|
connect(m_unlockDatabaseDialog, SIGNAL(unlockDone(bool)), SLOT(unlockDatabase(bool)));
|
||||||
connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(onWatchedFileChanged()));
|
connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(onWatchedFileChanged()));
|
||||||
connect(&m_fileWatchTimer, SIGNAL(timeout()), this, SLOT(reloadDatabaseFile()));
|
connect(&m_fileWatchTimer, SIGNAL(timeout()), this, SLOT(reloadDatabaseFile()));
|
||||||
connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged()));
|
connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged()));
|
||||||
@ -180,7 +184,8 @@ DatabaseWidget::Mode DatabaseWidget::currentMode() const
|
|||||||
else if (currentWidget() == m_mainWidget) {
|
else if (currentWidget() == m_mainWidget) {
|
||||||
return DatabaseWidget::ViewMode;
|
return DatabaseWidget::ViewMode;
|
||||||
}
|
}
|
||||||
else if (currentWidget() == m_unlockDatabaseWidget) {
|
else if (currentWidget() == m_unlockDatabaseWidget ||
|
||||||
|
currentWidget() == m_databaseOpenWidget) {
|
||||||
return DatabaseWidget::LockedMode;
|
return DatabaseWidget::LockedMode;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -705,7 +710,14 @@ void DatabaseWidget::unlockDatabase(bool accepted)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceDatabase(static_cast<DatabaseOpenWidget*>(sender())->database());
|
Database *db = Q_NULLPTR;
|
||||||
|
if (sender() == m_unlockDatabaseDialog) {
|
||||||
|
db = m_unlockDatabaseDialog->database();
|
||||||
|
} else if (sender() == m_unlockDatabaseWidget) {
|
||||||
|
db = m_unlockDatabaseWidget->database();
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceDatabase(db);
|
||||||
|
|
||||||
const QList<Group*> groups = m_db->rootGroup()->groupsRecursive(true);
|
const QList<Group*> groups = m_db->rootGroup()->groupsRecursive(true);
|
||||||
for (Group* group : groups) {
|
for (Group* group : groups) {
|
||||||
@ -719,6 +731,12 @@ void DatabaseWidget::unlockDatabase(bool accepted)
|
|||||||
setCurrentWidget(m_mainWidget);
|
setCurrentWidget(m_mainWidget);
|
||||||
m_unlockDatabaseWidget->clearForms();
|
m_unlockDatabaseWidget->clearForms();
|
||||||
Q_EMIT unlockedDatabase();
|
Q_EMIT unlockedDatabase();
|
||||||
|
|
||||||
|
if (sender() == m_unlockDatabaseDialog) {
|
||||||
|
QList<Database*> dbList;
|
||||||
|
dbList.append(m_db);
|
||||||
|
autoType()->performGlobalAutoType(dbList);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column)
|
void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column)
|
||||||
@ -1066,3 +1084,16 @@ GroupView* DatabaseWidget::groupView() {
|
|||||||
EntryView* DatabaseWidget::entryView() {
|
EntryView* DatabaseWidget::entryView() {
|
||||||
return m_entryView;
|
return m_entryView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DatabaseWidget::showUnlockDialog()
|
||||||
|
{
|
||||||
|
m_unlockDatabaseDialog->clearForms();
|
||||||
|
m_unlockDatabaseDialog->setDBFilename(m_filename);
|
||||||
|
m_unlockDatabaseDialog->show();
|
||||||
|
m_unlockDatabaseDialog->activateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseWidget::closeUnlockDialog()
|
||||||
|
{
|
||||||
|
m_unlockDatabaseDialog->close();
|
||||||
|
}
|
||||||
|
@ -43,6 +43,7 @@ class QMenu;
|
|||||||
class QSplitter;
|
class QSplitter;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class UnlockDatabaseWidget;
|
class UnlockDatabaseWidget;
|
||||||
|
class UnlockDatabaseDialog;
|
||||||
class QFileSystemWatcher;
|
class QFileSystemWatcher;
|
||||||
|
|
||||||
class DatabaseWidget : public QStackedWidget
|
class DatabaseWidget : public QStackedWidget
|
||||||
@ -89,6 +90,8 @@ public:
|
|||||||
bool currentEntryHasNotes();
|
bool currentEntryHasNotes();
|
||||||
GroupView* groupView();
|
GroupView* groupView();
|
||||||
EntryView* entryView();
|
EntryView* entryView();
|
||||||
|
void showUnlockDialog();
|
||||||
|
void closeUnlockDialog();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void closeRequest();
|
void closeRequest();
|
||||||
@ -174,6 +177,7 @@ private:
|
|||||||
DatabaseOpenWidget* m_databaseOpenMergeWidget;
|
DatabaseOpenWidget* m_databaseOpenMergeWidget;
|
||||||
KeePass1OpenWidget* m_keepass1OpenWidget;
|
KeePass1OpenWidget* m_keepass1OpenWidget;
|
||||||
UnlockDatabaseWidget* m_unlockDatabaseWidget;
|
UnlockDatabaseWidget* m_unlockDatabaseWidget;
|
||||||
|
UnlockDatabaseDialog* m_unlockDatabaseDialog;
|
||||||
QSplitter* m_splitter;
|
QSplitter* m_splitter;
|
||||||
GroupView* m_groupView;
|
GroupView* m_groupView;
|
||||||
EntryView* m_entryView;
|
EntryView* m_entryView;
|
||||||
|
56
src/gui/UnlockDatabaseDialog.cpp
Normal file
56
src/gui/UnlockDatabaseDialog.cpp
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 KeePassXC Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "UnlockDatabaseDialog.h"
|
||||||
|
#include "UnlockDatabaseWidget.h"
|
||||||
|
|
||||||
|
#include "autotype/AutoType.h"
|
||||||
|
#include "gui/DragTabBar.h"
|
||||||
|
#include "core/Database.h"
|
||||||
|
|
||||||
|
|
||||||
|
UnlockDatabaseDialog::UnlockDatabaseDialog(QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, m_view(new UnlockDatabaseWidget(this))
|
||||||
|
{
|
||||||
|
connect(m_view, SIGNAL(editFinished(bool)), this, SLOT(complete(bool)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnlockDatabaseDialog::setDBFilename(const QString &filename)
|
||||||
|
{
|
||||||
|
m_view->load(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnlockDatabaseDialog::clearForms()
|
||||||
|
{
|
||||||
|
m_view->clearForms();
|
||||||
|
}
|
||||||
|
|
||||||
|
Database *UnlockDatabaseDialog::database()
|
||||||
|
{
|
||||||
|
return m_view->database();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnlockDatabaseDialog::complete(bool r)
|
||||||
|
{
|
||||||
|
if (r) {
|
||||||
|
accept();
|
||||||
|
Q_EMIT unlockDone(true);
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
}
|
49
src/gui/UnlockDatabaseDialog.h
Normal file
49
src/gui/UnlockDatabaseDialog.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 KeePassXC Team
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 or (at your option)
|
||||||
|
* version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSX_AUTOTYPEUNLOCKDIALOG_H
|
||||||
|
#define KEEPASSX_AUTOTYPEUNLOCKDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
//#include <gui/DatabaseTabWidget.h>
|
||||||
|
|
||||||
|
#include "core/Global.h"
|
||||||
|
|
||||||
|
class UnlockDatabaseWidget;
|
||||||
|
class Database;
|
||||||
|
|
||||||
|
class UnlockDatabaseDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit UnlockDatabaseDialog(QWidget *parent = Q_NULLPTR);
|
||||||
|
void setDBFilename(const QString& filename);
|
||||||
|
void clearForms();
|
||||||
|
Database* database();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void unlockDone(bool);
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void complete(bool r);
|
||||||
|
|
||||||
|
private:
|
||||||
|
UnlockDatabaseWidget* const m_view;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSX_AUTOTYPEUNLOCKDIALOG_H
|
@ -354,12 +354,11 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore)
|
|||||||
m_autoTypeUi->enableButton->setChecked(entry->autoTypeEnabled());
|
m_autoTypeUi->enableButton->setChecked(entry->autoTypeEnabled());
|
||||||
if (entry->defaultAutoTypeSequence().isEmpty()) {
|
if (entry->defaultAutoTypeSequence().isEmpty()) {
|
||||||
m_autoTypeUi->inheritSequenceButton->setChecked(true);
|
m_autoTypeUi->inheritSequenceButton->setChecked(true);
|
||||||
m_autoTypeUi->sequenceEdit->setText("");
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_autoTypeUi->customSequenceButton->setChecked(true);
|
m_autoTypeUi->customSequenceButton->setChecked(true);
|
||||||
m_autoTypeUi->sequenceEdit->setText(entry->defaultAutoTypeSequence());
|
|
||||||
}
|
}
|
||||||
|
m_autoTypeUi->sequenceEdit->setText(entry->effectiveAutoTypeSequence());
|
||||||
m_autoTypeUi->windowTitleCombo->lineEdit()->clear();
|
m_autoTypeUi->windowTitleCombo->lineEdit()->clear();
|
||||||
m_autoTypeUi->defaultWindowSequenceButton->setChecked(true);
|
m_autoTypeUi->defaultWindowSequenceButton->setChecked(true);
|
||||||
m_autoTypeUi->windowSequenceEdit->setText("");
|
m_autoTypeUi->windowSequenceEdit->setText("");
|
||||||
|
@ -55,6 +55,10 @@ void EntryView::keyPressEvent(QKeyEvent* event)
|
|||||||
{
|
{
|
||||||
if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) && currentIndex().isValid()) {
|
if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) && currentIndex().isValid()) {
|
||||||
emitEntryActivated(currentIndex());
|
emitEntryActivated(currentIndex());
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
// Pressing return does not emit the QTreeView::activated signal on mac os
|
||||||
|
Q_EMIT activated(currentIndex());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QTreeView::keyPressEvent(event);
|
QTreeView::keyPressEvent(event);
|
||||||
|
@ -82,7 +82,7 @@ void EditGroupWidget::loadGroup(Group* group, bool create, Database* database)
|
|||||||
else {
|
else {
|
||||||
m_mainUi->autoTypeSequenceCustomRadio->setChecked(true);
|
m_mainUi->autoTypeSequenceCustomRadio->setChecked(true);
|
||||||
}
|
}
|
||||||
m_mainUi->autoTypeSequenceCustomEdit->setText(group->defaultAutoTypeSequence());
|
m_mainUi->autoTypeSequenceCustomEdit->setText(group->effectiveAutoTypeSequence());
|
||||||
|
|
||||||
IconStruct iconStruct;
|
IconStruct iconStruct;
|
||||||
iconStruct.uuid = group->iconUuid();
|
iconStruct.uuid = group->iconUuid();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user