diff --git a/CMakeLists.txt b/CMakeLists.txt
index 642bea7..2917a49 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,21 +29,25 @@ set(HEADERS
src/controller.h
src/stdafx.h
src/updater.h
+ src/hotkey.h
)
set(SOURCES
- "src/main.cpp"
- "src/controller.cpp"
- "src/updater.cpp"
+ src/main.cpp
+ src/controller.cpp
+ src/updater.cpp
+ src/hotkey.cpp
)
set(RESOURCES
GPT_Translator.qrc
)
+add_subdirectory(lib/)
IF (CMAKE_SYSTEM_NAME MATCHES "Linux")
MESSAGE(STATUS "current platform: Linux ")
+ find_package(X11 REQUIRED)
qt_add_executable(GPT_Translator
${SOURCES} ${HEADERS} ${RESOURCES}
@@ -66,7 +70,7 @@ ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Windows")
)
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin")
MESSAGE(STATUS "current platform: Darwin")
-
+ find_library( APP_SERVICES_LIBRARY ApplicationServices )
set(MACOSX_BUNDLE_ICON_FILE logo.icns)
# And the following tells CMake where to find and install the file itself.
@@ -99,19 +103,36 @@ IF (CMAKE_SYSTEM_NAME MATCHES "Windows")
PRIVATE
Qt6::Quick
Qt6::TextToSpeech
+ qhotkey
+ user32
)
else()
target_link_libraries(GPT_Translator
PRIVATE
Qt6::Quick
Qt6::TextToSpeech
+ qhotkey
+ user32
)
endif()
-ELSE()
+ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Linux")
+ target_include_directories(GPT_Translator PRIVATE ${X11_INCLUDE_DIR})
+
+ target_link_libraries(GPT_Translator
+ PRIVATE
+ Qt6::Quick
+ Qt6::TextToSpeech
+ qhotkey
+ ${X11_LIBRARIES}
+ )
+
+ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin")
target_link_libraries(GPT_Translator
PRIVATE
Qt6::Quick
Qt6::TextToSpeech
+ qhotkey
+ ${APP_SERVICES_LIBRARY}
)
ENDIF()
diff --git a/GPT_Translator.qrc b/GPT_Translator.qrc
index eb0d560..5dd1967 100644
--- a/GPT_Translator.qrc
+++ b/GPT_Translator.qrc
@@ -23,5 +23,7 @@
res/speak1.png
res/speak2.png
res/tray.png
+ qml/GPopWindow.qml
+ res/icon.png
diff --git a/doc/screenshot.png b/doc/screenshot.png
index 296cebb..dca0086 100644
Binary files a/doc/screenshot.png and b/doc/screenshot.png differ
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
new file mode 100644
index 0000000..ab4d751
--- /dev/null
+++ b/lib/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 3.16)
+
+
+add_subdirectory(QHotkey-1.5.0)
diff --git a/lib/QHotkey-1.5.0/.gitignore b/lib/QHotkey-1.5.0/.gitignore
new file mode 100644
index 0000000..04b7a50
--- /dev/null
+++ b/lib/QHotkey-1.5.0/.gitignore
@@ -0,0 +1,34 @@
+# C++ objects and libs
+
+*.slo
+*.lo
+*.o
+*.a
+*.la
+*.lai
+*.so
+*.dll
+*.dylib
+
+# Qt-es
+
+/.qmake.cache
+/.qmake.stash
+*.pro.user
+*.pro.user.*
+*.qbs.user
+*.qbs.user.*
+*.moc
+moc_*.cpp
+qrc_*.cpp
+ui_*.h
+Makefile*
+*build-*
+
+# QtCreator
+
+*.autosave
+
+#QtCtreator Qml
+*.qmlproject.user
+*.qmlproject.user.*
diff --git a/lib/QHotkey-1.5.0/CMakeLists.txt b/lib/QHotkey-1.5.0/CMakeLists.txt
new file mode 100644
index 0000000..a3d1097
--- /dev/null
+++ b/lib/QHotkey-1.5.0/CMakeLists.txt
@@ -0,0 +1,96 @@
+cmake_minimum_required(VERSION 3.1)
+
+project(qhotkey VERSION 1.5.0 LANGUAGES CXX)
+
+option(QHOTKEY_EXAMPLES "Build examples" OFF)
+option(QHOTKEY_INSTALL "Enable install rule" ON)
+
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+set(CMAKE_AUTOMOC ON)
+
+if(NOT QT_DEFAULT_MAJOR_VERSION)
+ set(QT_DEFAULT_MAJOR_VERSION 5 CACHE STRING "Qt version to use (5 or 6), defaults to 5")
+endif()
+
+if(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
+ find_package(Qt${QT_DEFAULT_MAJOR_VERSION} 6.2.0 COMPONENTS Core Gui REQUIRED)
+else()
+ find_package(Qt${QT_DEFAULT_MAJOR_VERSION} COMPONENTS Core Gui REQUIRED)
+endif()
+
+add_library(qhotkey QHotkey/qhotkey.cpp)
+add_library(QHotkey::QHotkey ALIAS qhotkey)
+target_link_libraries(qhotkey PUBLIC Qt${QT_DEFAULT_MAJOR_VERSION}::Core Qt${QT_DEFAULT_MAJOR_VERSION}::Gui)
+
+if(BUILD_SHARED_LIBS)
+ target_compile_definitions(qhotkey PRIVATE QHOTKEY_LIBRARY)
+ target_compile_definitions(qhotkey PUBLIC QHOTKEY_SHARED)
+endif()
+
+if(APPLE)
+ find_library(CARBON_LIBRARY Carbon)
+ mark_as_advanced(CARBON_LIBRARY)
+
+ target_sources(qhotkey PRIVATE QHotkey/qhotkey_mac.cpp)
+ target_link_libraries(qhotkey PRIVATE ${CARBON_LIBRARY})
+elseif(WIN32)
+ target_sources(qhotkey PRIVATE QHotkey/qhotkey_win.cpp)
+else()
+ find_package(X11 REQUIRED)
+ if(QT_DEFAULT_MAJOR_VERSION GREATER_EQUAL 6)
+ target_link_libraries(qhotkey PRIVATE ${X11_LIBRARIES})
+ else()
+ find_package(Qt${QT_DEFAULT_MAJOR_VERSION} COMPONENTS X11Extras REQUIRED)
+ target_link_libraries(qhotkey
+ PRIVATE
+ ${X11_LIBRARIES}
+ Qt${QT_DEFAULT_MAJOR_VERSION}::X11Extras)
+ endif()
+
+ include_directories(${X11_INCLUDE_DIR})
+ target_sources(qhotkey PRIVATE QHotkey/qhotkey_x11.cpp)
+endif()
+
+include(GNUInstallDirs)
+
+target_include_directories(qhotkey
+ PUBLIC
+ $
+ $)
+
+include(CMakePackageConfigHelpers)
+
+set_target_properties(qhotkey PROPERTIES
+ SOVERSION ${PROJECT_VERSION_MAJOR}
+ VERSION ${PROJECT_VERSION}
+ INTERFACE_QHotkey_MAJOR_VERSION ${PROJECT_VERSION_MAJOR}
+ COMPATIBLE_INTERFACE_STRING QHotkey_MAJOR_VERSION)
+
+write_basic_package_version_file(
+ ${CMAKE_CURRENT_BINARY_DIR}/QHotkeyConfigVersion.cmake
+ VERSION "${PROJECT_VERSION}"
+ COMPATIBILITY AnyNewerVersion)
+
+if(QHOTKEY_EXAMPLES)
+ add_subdirectory(HotkeyTest)
+endif()
+
+if(QHOTKEY_INSTALL)
+ set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/QHotkey)
+
+ install(
+ TARGETS qhotkey EXPORT QHotkeyConfig
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+ install(FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/QHotkey/qhotkey.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/QHotkey/QHotkey
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+ install(FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/QHotkeyConfigVersion.cmake
+ DESTINATION ${INSTALL_CONFIGDIR})
+ install(EXPORT QHotkeyConfig DESTINATION ${INSTALL_CONFIGDIR})
+
+ export(TARGETS qhotkey FILE QHotkeyConfig.cmake)
+endif()
diff --git a/lib/QHotkey-1.5.0/QHotkey/QHotkey b/lib/QHotkey-1.5.0/QHotkey/QHotkey
new file mode 100644
index 0000000..21d5e57
--- /dev/null
+++ b/lib/QHotkey-1.5.0/QHotkey/QHotkey
@@ -0,0 +1 @@
+#include "qhotkey.h"
diff --git a/lib/QHotkey-1.5.0/QHotkey/QHotkey.pro b/lib/QHotkey-1.5.0/QHotkey/QHotkey.pro
new file mode 100644
index 0000000..3a79376
--- /dev/null
+++ b/lib/QHotkey-1.5.0/QHotkey/QHotkey.pro
@@ -0,0 +1,16 @@
+TEMPLATE = lib
+win32: CONFIG += dll
+
+TARGET = QHotkey
+VERSION = 1.5.0
+
+include(../qhotkey.pri)
+
+DEFINES += QHOTKEY_SHARED QHOTKEY_LIBRARY
+
+# use INSTALL_ROOT to modify the install location
+headers.files = $$PUBLIC_HEADERS
+headers.path = $$[QT_INSTALL_HEADERS]
+target.path = $$[QT_INSTALL_LIBS]
+INSTALLS += target headers
+
diff --git a/lib/QHotkey-1.5.0/QHotkey/qhotkey.cpp b/lib/QHotkey-1.5.0/QHotkey/qhotkey.cpp
new file mode 100644
index 0000000..3b76d9c
--- /dev/null
+++ b/lib/QHotkey-1.5.0/QHotkey/qhotkey.cpp
@@ -0,0 +1,377 @@
+#include "qhotkey.h"
+#include "qhotkey_p.h"
+#include
+#include
+#include
+#include
+#include
+
+Q_LOGGING_CATEGORY(logQHotkey, "QHotkey")
+
+void QHotkey::addGlobalMapping(const QKeySequence &shortcut, QHotkey::NativeShortcut nativeShortcut)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ const int key = shortcut[0].toCombined();
+#else
+ const int key = shortcut[0];
+#endif
+
+ QMetaObject::invokeMethod(QHotkeyPrivate::instance(), "addMappingInvoked", Qt::QueuedConnection,
+ Q_ARG(Qt::Key, Qt::Key(key & ~Qt::KeyboardModifierMask)),
+ Q_ARG(Qt::KeyboardModifiers, Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask)),
+ Q_ARG(QHotkey::NativeShortcut, nativeShortcut));
+}
+
+bool QHotkey::isPlatformSupported()
+{
+ return QHotkeyPrivate::isPlatformSupported();
+}
+
+QHotkey::QHotkey(QObject *parent) :
+ QObject(parent),
+ _keyCode(Qt::Key_unknown),
+ _modifiers(Qt::NoModifier),
+ _registered(false)
+{}
+
+QHotkey::QHotkey(const QKeySequence &shortcut, bool autoRegister, QObject *parent) :
+ QHotkey(parent)
+{
+ setShortcut(shortcut, autoRegister);
+}
+
+QHotkey::QHotkey(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister, QObject *parent) :
+ QHotkey(parent)
+{
+ setShortcut(keyCode, modifiers, autoRegister);
+}
+
+QHotkey::QHotkey(QHotkey::NativeShortcut shortcut, bool autoRegister, QObject *parent) :
+ QHotkey(parent)
+{
+ setNativeShortcut(shortcut, autoRegister);
+}
+
+QHotkey::~QHotkey()
+{
+ if(_registered)
+ QHotkeyPrivate::instance()->removeShortcut(this);
+}
+
+QKeySequence QHotkey::shortcut() const
+{
+ if(_keyCode == Qt::Key_unknown)
+ return QKeySequence();
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ return QKeySequence((_keyCode | _modifiers).toCombined());
+#else
+ return QKeySequence(static_cast(_keyCode | _modifiers));
+#endif
+}
+
+Qt::Key QHotkey::keyCode() const
+{
+ return _keyCode;
+}
+
+Qt::KeyboardModifiers QHotkey::modifiers() const
+{
+ return _modifiers;
+}
+
+QHotkey::NativeShortcut QHotkey::currentNativeShortcut() const
+{
+ return _nativeShortcut;
+}
+
+bool QHotkey::isRegistered() const
+{
+ return _registered;
+}
+
+bool QHotkey::setShortcut(const QKeySequence &shortcut, bool autoRegister)
+{
+ if(shortcut.isEmpty())
+ return resetShortcut();
+ if(shortcut.count() > 1) {
+ qCWarning(logQHotkey, "Keysequences with multiple shortcuts are not allowed! "
+ "Only the first shortcut will be used!");
+ }
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ const int key = shortcut[0].toCombined();
+#else
+ const int key = shortcut[0];
+#endif
+
+ return setShortcut(Qt::Key(key & ~Qt::KeyboardModifierMask),
+ Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask),
+ autoRegister);
+}
+
+bool QHotkey::setShortcut(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister)
+{
+ if(_registered) {
+ if(autoRegister) {
+ if(!QHotkeyPrivate::instance()->removeShortcut(this))
+ return false;
+ } else
+ return false;
+ }
+
+ if(keyCode == Qt::Key_unknown) {
+ _keyCode = Qt::Key_unknown;
+ _modifiers = Qt::NoModifier;
+ _nativeShortcut = NativeShortcut();
+ return true;
+ }
+
+ _keyCode = keyCode;
+ _modifiers = modifiers;
+ _nativeShortcut = QHotkeyPrivate::instance()->nativeShortcut(keyCode, modifiers);
+ if(_nativeShortcut.isValid()) {
+ if(autoRegister)
+ return QHotkeyPrivate::instance()->addShortcut(this);
+ return true;
+ }
+
+ qCWarning(logQHotkey) << "Unable to map shortcut to native keys. Key:" << keyCode << "Modifiers:" << modifiers;
+ _keyCode = Qt::Key_unknown;
+ _modifiers = Qt::NoModifier;
+ _nativeShortcut = NativeShortcut();
+ return false;
+}
+
+bool QHotkey::resetShortcut()
+{
+ if(_registered &&
+ !QHotkeyPrivate::instance()->removeShortcut(this)) {
+ return false;
+ }
+
+ _keyCode = Qt::Key_unknown;
+ _modifiers = Qt::NoModifier;
+ _nativeShortcut = NativeShortcut();
+ return true;
+}
+
+bool QHotkey::setNativeShortcut(QHotkey::NativeShortcut nativeShortcut, bool autoRegister)
+{
+ if(_registered) {
+ if(autoRegister) {
+ if(!QHotkeyPrivate::instance()->removeShortcut(this))
+ return false;
+ } else
+ return false;
+ }
+
+ if(nativeShortcut.isValid()) {
+ _keyCode = Qt::Key_unknown;
+ _modifiers = Qt::NoModifier;
+ _nativeShortcut = nativeShortcut;
+ if(autoRegister)
+ return QHotkeyPrivate::instance()->addShortcut(this);
+ return true;
+ }
+
+ _keyCode = Qt::Key_unknown;
+ _modifiers = Qt::NoModifier;
+ _nativeShortcut = NativeShortcut();
+ return true;
+}
+
+bool QHotkey::setRegistered(bool registered)
+{
+ if(_registered && !registered)
+ return QHotkeyPrivate::instance()->removeShortcut(this);
+ if(!_registered && registered) {
+ if(!_nativeShortcut.isValid())
+ return false;
+ return QHotkeyPrivate::instance()->addShortcut(this);
+ }
+ return true;
+}
+
+
+
+// ---------- QHotkeyPrivate implementation ----------
+
+QHotkeyPrivate::QHotkeyPrivate()
+{
+ Q_ASSERT_X(qApp, Q_FUNC_INFO, "QHotkey requires QCoreApplication to be instantiated");
+ qApp->eventDispatcher()->installNativeEventFilter(this);
+}
+
+QHotkeyPrivate::~QHotkeyPrivate()
+{
+ if(!shortcuts.isEmpty())
+ qCWarning(logQHotkey) << "QHotkeyPrivate destroyed with registered shortcuts!";
+ if(qApp && qApp->eventDispatcher())
+ qApp->eventDispatcher()->removeNativeEventFilter(this);
+}
+
+QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcut(Qt::Key keycode, Qt::KeyboardModifiers modifiers)
+{
+ Qt::ConnectionType conType = (QThread::currentThread() == thread() ?
+ Qt::DirectConnection :
+ Qt::BlockingQueuedConnection);
+ QHotkey::NativeShortcut res;
+ if(!QMetaObject::invokeMethod(this, "nativeShortcutInvoked", conType,
+ Q_RETURN_ARG(QHotkey::NativeShortcut, res),
+ Q_ARG(Qt::Key, keycode),
+ Q_ARG(Qt::KeyboardModifiers, modifiers))) {
+ return QHotkey::NativeShortcut();
+ }
+ return res;
+}
+
+bool QHotkeyPrivate::addShortcut(QHotkey *hotkey)
+{
+ if(hotkey->_registered)
+ return false;
+
+ Qt::ConnectionType conType = (QThread::currentThread() == thread() ?
+ Qt::DirectConnection :
+ Qt::BlockingQueuedConnection);
+ bool res = false;
+ if(!QMetaObject::invokeMethod(this, "addShortcutInvoked", conType,
+ Q_RETURN_ARG(bool, res),
+ Q_ARG(QHotkey*, hotkey))) {
+ return false;
+ }
+
+ if(res)
+ emit hotkey->registeredChanged(true);
+ return res;
+}
+
+bool QHotkeyPrivate::removeShortcut(QHotkey *hotkey)
+{
+ if(!hotkey->_registered)
+ return false;
+
+ Qt::ConnectionType conType = (QThread::currentThread() == thread() ?
+ Qt::DirectConnection :
+ Qt::BlockingQueuedConnection);
+ bool res = false;
+ if(!QMetaObject::invokeMethod(this, "removeShortcutInvoked", conType,
+ Q_RETURN_ARG(bool, res),
+ Q_ARG(QHotkey*, hotkey))) {
+ return false;
+ }
+
+ if(res)
+ emit hotkey->registeredChanged(false);
+ return res;
+}
+
+void QHotkeyPrivate::activateShortcut(QHotkey::NativeShortcut shortcut)
+{
+ QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::activated);
+ for(QHotkey *hkey : shortcuts.values(shortcut))
+ signal.invoke(hkey, Qt::QueuedConnection);
+}
+
+void QHotkeyPrivate::releaseShortcut(QHotkey::NativeShortcut shortcut)
+{
+ QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::released);
+ for(QHotkey *hkey : shortcuts.values(shortcut))
+ signal.invoke(hkey, Qt::QueuedConnection);
+}
+
+void QHotkeyPrivate::addMappingInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers, QHotkey::NativeShortcut nativeShortcut)
+{
+ mapping.insert({keycode, modifiers}, nativeShortcut);
+}
+
+bool QHotkeyPrivate::addShortcutInvoked(QHotkey *hotkey)
+{
+ QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut;
+
+ if(!shortcuts.contains(shortcut)) {
+ if(!registerShortcut(shortcut)) {
+ qCWarning(logQHotkey) << QHotkey::tr("Failed to register %1. Error: %2").arg(hotkey->shortcut().toString(), error);
+ return false;
+ }
+ }
+
+ shortcuts.insert(shortcut, hotkey);
+ hotkey->_registered = true;
+ return true;
+}
+
+bool QHotkeyPrivate::removeShortcutInvoked(QHotkey *hotkey)
+{
+ QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut;
+
+ if(shortcuts.remove(shortcut, hotkey) == 0)
+ return false;
+ hotkey->_registered = false;
+ emit hotkey->registeredChanged(true);
+ if(shortcuts.count(shortcut) == 0) {
+ if (!unregisterShortcut(shortcut)) {
+ qCWarning(logQHotkey) << QHotkey::tr("Failed to unregister %1. Error: %2").arg(hotkey->shortcut().toString(), error);
+ return false;
+ }
+ return true;
+ }
+ return true;
+}
+
+QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcutInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers)
+{
+ if(mapping.contains({keycode, modifiers}))
+ return mapping.value({keycode, modifiers});
+
+ bool ok1 = false;
+ auto k = nativeKeycode(keycode, ok1);
+ bool ok2 = false;
+ auto m = nativeModifiers(modifiers, ok2);
+ if(ok1 && ok2)
+ return {k, m};
+ return {};
+}
+
+
+
+QHotkey::NativeShortcut::NativeShortcut() :
+ key(),
+ modifier(),
+ valid(false)
+{}
+
+QHotkey::NativeShortcut::NativeShortcut(quint32 key, quint32 modifier) :
+ key(key),
+ modifier(modifier),
+ valid(true)
+{}
+
+bool QHotkey::NativeShortcut::isValid() const
+{
+ return valid;
+}
+
+bool QHotkey::NativeShortcut::operator ==(QHotkey::NativeShortcut other) const
+{
+ return (key == other.key) &&
+ (modifier == other.modifier) &&
+ valid == other.valid;
+}
+
+bool QHotkey::NativeShortcut::operator !=(QHotkey::NativeShortcut other) const
+{
+ return (key != other.key) ||
+ (modifier != other.modifier) ||
+ valid != other.valid;
+}
+
+QHOTKEY_HASH_SEED qHash(QHotkey::NativeShortcut key)
+{
+ return qHash(key.key) ^ qHash(key.modifier);
+}
+
+QHOTKEY_HASH_SEED qHash(QHotkey::NativeShortcut key, QHOTKEY_HASH_SEED seed)
+{
+ return qHash(key.key, seed) ^ qHash(key.modifier, seed);
+}
diff --git a/lib/QHotkey-1.5.0/QHotkey/qhotkey.h b/lib/QHotkey-1.5.0/QHotkey/qhotkey.h
new file mode 100644
index 0000000..3697c8e
--- /dev/null
+++ b/lib/QHotkey-1.5.0/QHotkey/qhotkey.h
@@ -0,0 +1,130 @@
+#ifndef QHOTKEY_H
+#define QHOTKEY_H
+
+#include
+#include
+#include
+#include
+
+#ifdef QHOTKEY_SHARED
+# ifdef QHOTKEY_LIBRARY
+# define QHOTKEY_EXPORT Q_DECL_EXPORT
+# else
+# define QHOTKEY_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define QHOTKEY_EXPORT
+#endif
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ #define QHOTKEY_HASH_SEED size_t
+#else
+ #define QHOTKEY_HASH_SEED uint
+#endif
+
+//! A class to define global, systemwide Hotkeys
+class QHOTKEY_EXPORT QHotkey : public QObject
+{
+ Q_OBJECT
+ //! @private
+ friend class QHotkeyPrivate;
+
+ //! Specifies whether this hotkey is currently registered or not
+ Q_PROPERTY(bool registered READ isRegistered WRITE setRegistered NOTIFY registeredChanged)
+ //! Holds the shortcut this hotkey will be triggered on
+ Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut RESET resetShortcut)
+
+public:
+ //! Defines shortcut with native keycodes
+ class QHOTKEY_EXPORT NativeShortcut {
+ public:
+ //! The native keycode
+ quint32 key;
+ //! The native modifiers
+ quint32 modifier;
+
+ //! Creates an invalid native shortcut
+ NativeShortcut();
+ //! Creates a valid native shortcut, with the given key and modifiers
+ NativeShortcut(quint32 key, quint32 modifier = 0);
+
+ //! Checks, whether this shortcut is valid or not
+ bool isValid() const;
+
+ //! Equality operator
+ bool operator ==(NativeShortcut other) const;
+ //! Inequality operator
+ bool operator !=(NativeShortcut other) const;
+
+ private:
+ bool valid;
+ };
+
+ //! Adds a global mapping of a key sequence to a replacement native shortcut
+ static void addGlobalMapping(const QKeySequence &shortcut, NativeShortcut nativeShortcut);
+
+ //! Checks if global shortcuts are supported by the current platform
+ static bool isPlatformSupported();
+
+ //! Default Constructor
+ explicit QHotkey(QObject *parent = nullptr);
+ //! Constructs a hotkey with a shortcut and optionally registers it
+ explicit QHotkey(const QKeySequence &shortcut, bool autoRegister = false, QObject *parent = nullptr);
+ //! Constructs a hotkey with a key and modifiers and optionally registers it
+ explicit QHotkey(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister = false, QObject *parent = nullptr);
+ //! Constructs a hotkey from a native shortcut and optionally registers it
+ explicit QHotkey(NativeShortcut shortcut, bool autoRegister = false, QObject *parent = nullptr);
+ ~QHotkey() override;
+
+ //! @readAcFn{QHotkey::registered}
+ bool isRegistered() const;
+ //! @readAcFn{QHotkey::shortcut}
+ QKeySequence shortcut() const;
+ //! @readAcFn{QHotkey::shortcut} - the key only
+ Qt::Key keyCode() const;
+ //! @readAcFn{QHotkey::shortcut} - the modifiers only
+ Qt::KeyboardModifiers modifiers() const;
+
+ //! Get the current native shortcut
+ NativeShortcut currentNativeShortcut() const;
+
+public slots:
+ //! @writeAcFn{QHotkey::registered}
+ bool setRegistered(bool registered);
+
+ //! @writeAcFn{QHotkey::shortcut}
+ bool setShortcut(const QKeySequence &shortcut, bool autoRegister = false);
+ //! @writeAcFn{QHotkey::shortcut}
+ bool setShortcut(Qt::Key keyCode, Qt::KeyboardModifiers modifiers, bool autoRegister = false);
+ //! @resetAcFn{QHotkey::shortcut}
+ bool resetShortcut();
+
+ //! Set this hotkey to a native shortcut
+ bool setNativeShortcut(QHotkey::NativeShortcut nativeShortcut, bool autoRegister = false);
+
+signals:
+ //! Will be emitted if the shortcut is pressed
+ void activated(QPrivateSignal);
+
+ //! Will be emitted if the shortcut press is released
+ void released(QPrivateSignal);
+
+ //! @notifyAcFn{QHotkey::registered}
+ void registeredChanged(bool registered);
+
+private:
+ Qt::Key _keyCode;
+ Qt::KeyboardModifiers _modifiers;
+
+ NativeShortcut _nativeShortcut;
+ bool _registered;
+};
+
+QHOTKEY_HASH_SEED QHOTKEY_EXPORT qHash(QHotkey::NativeShortcut key);
+QHOTKEY_HASH_SEED QHOTKEY_EXPORT qHash(QHotkey::NativeShortcut key, QHOTKEY_HASH_SEED seed);
+
+QHOTKEY_EXPORT Q_DECLARE_LOGGING_CATEGORY(logQHotkey)
+
+Q_DECLARE_METATYPE(QHotkey::NativeShortcut)
+
+#endif // QHOTKEY_H
diff --git a/lib/QHotkey-1.5.0/QHotkey/qhotkey.pri b/lib/QHotkey-1.5.0/QHotkey/qhotkey.pri
new file mode 100644
index 0000000..a7c9725
--- /dev/null
+++ b/lib/QHotkey-1.5.0/QHotkey/qhotkey.pri
@@ -0,0 +1 @@
+message(The pri file has been moved one directory up! use that one instead)
diff --git a/lib/QHotkey-1.5.0/QHotkey/qhotkey_mac.cpp b/lib/QHotkey-1.5.0/QHotkey/qhotkey_mac.cpp
new file mode 100644
index 0000000..2cf0db4
--- /dev/null
+++ b/lib/QHotkey-1.5.0/QHotkey/qhotkey_mac.cpp
@@ -0,0 +1,291 @@
+#include "qhotkey.h"
+#include "qhotkey_p.h"
+#include
+#include
+
+class QHotkeyPrivateMac : public QHotkeyPrivate
+{
+public:
+ // QAbstractNativeEventFilter interface
+ bool nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result) override;
+
+ static OSStatus hotkeyPressEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data);
+ static OSStatus hotkeyReleaseEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data);
+
+protected:
+ // QHotkeyPrivate interface
+ quint32 nativeKeycode(Qt::Key keycode, bool &ok) Q_DECL_OVERRIDE;
+ quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) Q_DECL_OVERRIDE;
+ bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
+ bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
+
+private:
+ static bool isHotkeyHandlerRegistered;
+ static QHash hotkeyRefs;
+};
+NATIVE_INSTANCE(QHotkeyPrivateMac)
+
+bool QHotkeyPrivate::isPlatformSupported()
+{
+ return true;
+}
+
+bool QHotkeyPrivateMac::isHotkeyHandlerRegistered = false;
+QHash QHotkeyPrivateMac::hotkeyRefs;
+
+bool QHotkeyPrivateMac::nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result)
+{
+ Q_UNUSED(eventType)
+ Q_UNUSED(message)
+ Q_UNUSED(result)
+ return false;
+}
+
+quint32 QHotkeyPrivateMac::nativeKeycode(Qt::Key keycode, bool &ok)
+{
+ // Constants found in NSEvent.h from AppKit.framework
+ ok = true;
+ switch (keycode) {
+ case Qt::Key_Return:
+ return kVK_Return;
+ case Qt::Key_Enter:
+ return kVK_ANSI_KeypadEnter;
+ case Qt::Key_Tab:
+ return kVK_Tab;
+ case Qt::Key_Space:
+ return kVK_Space;
+ case Qt::Key_Backspace:
+ return kVK_Delete;
+ case Qt::Key_Escape:
+ return kVK_Escape;
+ case Qt::Key_CapsLock:
+ return kVK_CapsLock;
+ case Qt::Key_Option:
+ return kVK_Option;
+ case Qt::Key_F17:
+ return kVK_F17;
+ case Qt::Key_VolumeUp:
+ return kVK_VolumeUp;
+ case Qt::Key_VolumeDown:
+ return kVK_VolumeDown;
+ case Qt::Key_F18:
+ return kVK_F18;
+ case Qt::Key_F19:
+ return kVK_F19;
+ case Qt::Key_F20:
+ return kVK_F20;
+ case Qt::Key_F5:
+ return kVK_F5;
+ case Qt::Key_F6:
+ return kVK_F6;
+ case Qt::Key_F7:
+ return kVK_F7;
+ case Qt::Key_F3:
+ return kVK_F3;
+ case Qt::Key_F8:
+ return kVK_F8;
+ case Qt::Key_F9:
+ return kVK_F9;
+ case Qt::Key_F11:
+ return kVK_F11;
+ case Qt::Key_F13:
+ return kVK_F13;
+ case Qt::Key_F16:
+ return kVK_F16;
+ case Qt::Key_F14:
+ return kVK_F14;
+ case Qt::Key_F10:
+ return kVK_F10;
+ case Qt::Key_F12:
+ return kVK_F12;
+ case Qt::Key_F15:
+ return kVK_F15;
+ case Qt::Key_Help:
+ return kVK_Help;
+ case Qt::Key_Home:
+ return kVK_Home;
+ case Qt::Key_PageUp:
+ return kVK_PageUp;
+ case Qt::Key_Delete:
+ return kVK_ForwardDelete;
+ case Qt::Key_F4:
+ return kVK_F4;
+ case Qt::Key_End:
+ return kVK_End;
+ case Qt::Key_F2:
+ return kVK_F2;
+ case Qt::Key_PageDown:
+ return kVK_PageDown;
+ case Qt::Key_F1:
+ return kVK_F1;
+ case Qt::Key_Left:
+ return kVK_LeftArrow;
+ case Qt::Key_Right:
+ return kVK_RightArrow;
+ case Qt::Key_Down:
+ return kVK_DownArrow;
+ case Qt::Key_Up:
+ return kVK_UpArrow;
+ default:
+ ok = false;
+ break;
+ }
+
+ UTF16Char ch = keycode;
+
+ CFDataRef currentLayoutData;
+ TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
+
+ if (currentKeyboard == NULL)
+ return 0;
+
+ currentLayoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
+ CFRelease(currentKeyboard);
+ if (currentLayoutData == NULL)
+ return 0;
+
+ UCKeyboardLayout* header = (UCKeyboardLayout*)CFDataGetBytePtr(currentLayoutData);
+ UCKeyboardTypeHeader* table = header->keyboardTypeList;
+
+ uint8_t *data = (uint8_t*)header;
+ for (quint32 i=0; i < header->keyboardTypeCount; i++) {
+ UCKeyStateRecordsIndex* stateRec = 0;
+ if (table[i].keyStateRecordsIndexOffset != 0) {
+ stateRec = reinterpret_cast(data + table[i].keyStateRecordsIndexOffset);
+ if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) stateRec = 0;
+ }
+
+ UCKeyToCharTableIndex* charTable = reinterpret_cast(data + table[i].keyToCharTableIndexOffset);
+ if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) continue;
+
+ for (quint32 j=0; j < charTable->keyToCharTableCount; j++) {
+ UCKeyOutput* keyToChar = reinterpret_cast(data + charTable->keyToCharTableOffsets[j]);
+ for (quint32 k=0; k < charTable->keyToCharTableSize; k++) {
+ if (keyToChar[k] & kUCKeyOutputTestForIndexMask) {
+ long idx = keyToChar[k] & kUCKeyOutputGetIndexMask;
+ if (stateRec && idx < stateRec->keyStateRecordCount) {
+ UCKeyStateRecord* rec = reinterpret_cast(data + stateRec->keyStateRecordOffsets[idx]);
+ if (rec->stateZeroCharData == ch) {
+ ok = true;
+ return k;
+ }
+ }
+ }
+ else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && keyToChar[k] < 0xFFFE) {
+ if (keyToChar[k] == ch) {
+ ok = true;
+ return k;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+quint32 QHotkeyPrivateMac::nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok)
+{
+ quint32 nMods = 0;
+ if (modifiers & Qt::ShiftModifier)
+ nMods |= shiftKey;
+ if (modifiers & Qt::ControlModifier)
+ nMods |= cmdKey;
+ if (modifiers & Qt::AltModifier)
+ nMods |= optionKey;
+ if (modifiers & Qt::MetaModifier)
+ nMods |= controlKey;
+ if (modifiers & Qt::KeypadModifier)
+ nMods |= kEventKeyModifierNumLockMask;
+ ok = true;
+ return nMods;
+}
+
+bool QHotkeyPrivateMac::registerShortcut(QHotkey::NativeShortcut shortcut)
+{
+ if (!this->isHotkeyHandlerRegistered)
+ {
+ EventTypeSpec pressEventSpec;
+ pressEventSpec.eventClass = kEventClassKeyboard;
+ pressEventSpec.eventKind = kEventHotKeyPressed;
+ InstallApplicationEventHandler(&QHotkeyPrivateMac::hotkeyPressEventHandler, 1, &pressEventSpec, NULL, NULL);
+
+ EventTypeSpec releaseEventSpec;
+ releaseEventSpec.eventClass = kEventClassKeyboard;
+ releaseEventSpec.eventKind = kEventHotKeyReleased;
+ InstallApplicationEventHandler(&QHotkeyPrivateMac::hotkeyReleaseEventHandler, 1, &releaseEventSpec, NULL, NULL);
+ }
+
+ EventHotKeyID hkeyID;
+ hkeyID.signature = shortcut.key;
+ hkeyID.id = shortcut.modifier;
+
+ EventHotKeyRef eventRef = 0;
+ OSStatus status = RegisterEventHotKey(shortcut.key,
+ shortcut.modifier,
+ hkeyID,
+ GetApplicationEventTarget(),
+ 0,
+ &eventRef);
+ if (status != noErr) {
+ error = QString::number(status);
+ return false;
+ } else {
+ this->hotkeyRefs.insert(shortcut, eventRef);
+ return true;
+ }
+}
+
+bool QHotkeyPrivateMac::unregisterShortcut(QHotkey::NativeShortcut shortcut)
+{
+ EventHotKeyRef eventRef = QHotkeyPrivateMac::hotkeyRefs.value(shortcut);
+ OSStatus status = UnregisterEventHotKey(eventRef);
+ if (status != noErr) {
+ error = QString::number(status);
+ return false;
+ } else {
+ this->hotkeyRefs.remove(shortcut);
+ return true;
+ }
+}
+
+OSStatus QHotkeyPrivateMac::hotkeyPressEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data)
+{
+ Q_UNUSED(nextHandler);
+ Q_UNUSED(data);
+
+ if (GetEventClass(event) == kEventClassKeyboard &&
+ GetEventKind(event) == kEventHotKeyPressed) {
+ EventHotKeyID hkeyID;
+ GetEventParameter(event,
+ kEventParamDirectObject,
+ typeEventHotKeyID,
+ NULL,
+ sizeof(EventHotKeyID),
+ NULL,
+ &hkeyID);
+ hotkeyPrivate->activateShortcut({hkeyID.signature, hkeyID.id});
+ }
+
+ return noErr;
+}
+
+OSStatus QHotkeyPrivateMac::hotkeyReleaseEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data)
+{
+ Q_UNUSED(nextHandler);
+ Q_UNUSED(data);
+
+ if (GetEventClass(event) == kEventClassKeyboard &&
+ GetEventKind(event) == kEventHotKeyReleased) {
+ EventHotKeyID hkeyID;
+ GetEventParameter(event,
+ kEventParamDirectObject,
+ typeEventHotKeyID,
+ NULL,
+ sizeof(EventHotKeyID),
+ NULL,
+ &hkeyID);
+ hotkeyPrivate->releaseShortcut({hkeyID.signature, hkeyID.id});
+ }
+
+ return noErr;
+}
diff --git a/lib/QHotkey-1.5.0/QHotkey/qhotkey_p.h b/lib/QHotkey-1.5.0/QHotkey/qhotkey_p.h
new file mode 100644
index 0000000..8bc5ab6
--- /dev/null
+++ b/lib/QHotkey-1.5.0/QHotkey/qhotkey_p.h
@@ -0,0 +1,62 @@
+#ifndef QHOTKEY_P_H
+#define QHOTKEY_P_H
+
+#include "qhotkey.h"
+#include
+#include
+#include
+#include
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ #define _NATIVE_EVENT_RESULT qintptr
+#else
+ #define _NATIVE_EVENT_RESULT long
+#endif
+
+class QHOTKEY_EXPORT QHotkeyPrivate : public QObject, public QAbstractNativeEventFilter
+{
+ Q_OBJECT
+
+public:
+ QHotkeyPrivate();//singleton!!!
+ ~QHotkeyPrivate();
+
+ static QHotkeyPrivate *instance();
+ static bool isPlatformSupported();
+
+ QHotkey::NativeShortcut nativeShortcut(Qt::Key keycode, Qt::KeyboardModifiers modifiers);
+
+ bool addShortcut(QHotkey *hotkey);
+ bool removeShortcut(QHotkey *hotkey);
+
+protected:
+ void activateShortcut(QHotkey::NativeShortcut shortcut);
+ void releaseShortcut(QHotkey::NativeShortcut shortcut);
+
+ virtual quint32 nativeKeycode(Qt::Key keycode, bool &ok) = 0;//platform implement
+ virtual quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) = 0;//platform implement
+
+ virtual bool registerShortcut(QHotkey::NativeShortcut shortcut) = 0;//platform implement
+ virtual bool unregisterShortcut(QHotkey::NativeShortcut shortcut) = 0;//platform implement
+
+ QString error;
+
+private:
+ QHash, QHotkey::NativeShortcut> mapping;
+ QMultiHash shortcuts;
+
+ Q_INVOKABLE void addMappingInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers, QHotkey::NativeShortcut nativeShortcut);
+ Q_INVOKABLE bool addShortcutInvoked(QHotkey *hotkey);
+ Q_INVOKABLE bool removeShortcutInvoked(QHotkey *hotkey);
+ Q_INVOKABLE QHotkey::NativeShortcut nativeShortcutInvoked(Qt::Key keycode, Qt::KeyboardModifiers modifiers);
+};
+
+#define NATIVE_INSTANCE(ClassName) \
+ Q_GLOBAL_STATIC(ClassName, hotkeyPrivate) \
+ \
+ QHotkeyPrivate *QHotkeyPrivate::instance()\
+ {\
+ return hotkeyPrivate;\
+ }
+
+#endif // QHOTKEY_P_H
diff --git a/lib/QHotkey-1.5.0/QHotkey/qhotkey_win.cpp b/lib/QHotkey-1.5.0/QHotkey/qhotkey_win.cpp
new file mode 100644
index 0000000..715b92e
--- /dev/null
+++ b/lib/QHotkey-1.5.0/QHotkey/qhotkey_win.cpp
@@ -0,0 +1,302 @@
+#include "qhotkey.h"
+#include "qhotkey_p.h"
+#include
+#include
+#include
+
+#define HKEY_ID(nativeShortcut) (((nativeShortcut.key ^ (nativeShortcut.modifier << 8)) & 0x0FFF) | 0x7000)
+
+#if !defined(MOD_NOREPEAT)
+#define MOD_NOREPEAT 0x4000
+#endif
+
+class QHotkeyPrivateWin : public QHotkeyPrivate
+{
+public:
+ QHotkeyPrivateWin();
+ // QAbstractNativeEventFilter interface
+ bool nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result) override;
+
+protected:
+ void pollForHotkeyRelease();
+ // QHotkeyPrivate interface
+ quint32 nativeKeycode(Qt::Key keycode, bool &ok) Q_DECL_OVERRIDE;
+ quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) Q_DECL_OVERRIDE;
+ bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
+ bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
+
+private:
+ static QString formatWinError(DWORD winError);
+ QTimer pollTimer;
+ QHotkey::NativeShortcut polledShortcut;
+};
+NATIVE_INSTANCE(QHotkeyPrivateWin)
+
+QHotkeyPrivateWin::QHotkeyPrivateWin(){
+ pollTimer.setInterval(50);
+ connect(&pollTimer, &QTimer::timeout, this, &QHotkeyPrivateWin::pollForHotkeyRelease);
+}
+
+bool QHotkeyPrivate::isPlatformSupported()
+{
+ return true;
+}
+
+bool QHotkeyPrivateWin::nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result)
+{
+ Q_UNUSED(eventType)
+ Q_UNUSED(result)
+
+ MSG* msg = static_cast(message);
+ if(msg->message == WM_HOTKEY) {
+ QHotkey::NativeShortcut shortcut = {HIWORD(msg->lParam), LOWORD(msg->lParam)};
+ this->activateShortcut(shortcut);
+ this->polledShortcut = shortcut;
+ this->pollTimer.start();
+ }
+
+ return false;
+}
+
+void QHotkeyPrivateWin::pollForHotkeyRelease()
+{
+ bool pressed = (GetAsyncKeyState(this->polledShortcut.key) & (1 << 15)) != 0;
+ if(!pressed) {
+ this->pollTimer.stop();
+ this->releaseShortcut(this->polledShortcut);
+ }
+}
+
+quint32 QHotkeyPrivateWin::nativeKeycode(Qt::Key keycode, bool &ok)
+{
+ ok = true;
+ if(keycode <= 0xFFFF) {//Try to obtain the key from it's "character"
+ const SHORT vKey = VkKeyScanW(static_cast(keycode));
+ if(vKey > -1)
+ return LOBYTE(vKey);
+ }
+
+ //find key from switch/case --> Only finds a very small subset of keys
+ switch (keycode)
+ {
+ case Qt::Key_Escape:
+ return VK_ESCAPE;
+ case Qt::Key_Tab:
+ case Qt::Key_Backtab:
+ return VK_TAB;
+ case Qt::Key_Backspace:
+ return VK_BACK;
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ return VK_RETURN;
+ case Qt::Key_Insert:
+ return VK_INSERT;
+ case Qt::Key_Delete:
+ return VK_DELETE;
+ case Qt::Key_Pause:
+ return VK_PAUSE;
+ case Qt::Key_Print:
+ return VK_PRINT;
+ case Qt::Key_Clear:
+ return VK_CLEAR;
+ case Qt::Key_Home:
+ return VK_HOME;
+ case Qt::Key_End:
+ return VK_END;
+ case Qt::Key_Left:
+ return VK_LEFT;
+ case Qt::Key_Up:
+ return VK_UP;
+ case Qt::Key_Right:
+ return VK_RIGHT;
+ case Qt::Key_Down:
+ return VK_DOWN;
+ case Qt::Key_PageUp:
+ return VK_PRIOR;
+ case Qt::Key_PageDown:
+ return VK_NEXT;
+ case Qt::Key_CapsLock:
+ return VK_CAPITAL;
+ case Qt::Key_NumLock:
+ return VK_NUMLOCK;
+ case Qt::Key_ScrollLock:
+ return VK_SCROLL;
+
+ case Qt::Key_F1:
+ return VK_F1;
+ case Qt::Key_F2:
+ return VK_F2;
+ case Qt::Key_F3:
+ return VK_F3;
+ case Qt::Key_F4:
+ return VK_F4;
+ case Qt::Key_F5:
+ return VK_F5;
+ case Qt::Key_F6:
+ return VK_F6;
+ case Qt::Key_F7:
+ return VK_F7;
+ case Qt::Key_F8:
+ return VK_F8;
+ case Qt::Key_F9:
+ return VK_F9;
+ case Qt::Key_F10:
+ return VK_F10;
+ case Qt::Key_F11:
+ return VK_F11;
+ case Qt::Key_F12:
+ return VK_F12;
+ case Qt::Key_F13:
+ return VK_F13;
+ case Qt::Key_F14:
+ return VK_F14;
+ case Qt::Key_F15:
+ return VK_F15;
+ case Qt::Key_F16:
+ return VK_F16;
+ case Qt::Key_F17:
+ return VK_F17;
+ case Qt::Key_F18:
+ return VK_F18;
+ case Qt::Key_F19:
+ return VK_F19;
+ case Qt::Key_F20:
+ return VK_F20;
+ case Qt::Key_F21:
+ return VK_F21;
+ case Qt::Key_F22:
+ return VK_F22;
+ case Qt::Key_F23:
+ return VK_F23;
+ case Qt::Key_F24:
+ return VK_F24;
+
+ case Qt::Key_Menu:
+ return VK_APPS;
+ case Qt::Key_Help:
+ return VK_HELP;
+ case Qt::Key_MediaNext:
+ return VK_MEDIA_NEXT_TRACK;
+ case Qt::Key_MediaPrevious:
+ return VK_MEDIA_PREV_TRACK;
+ case Qt::Key_MediaPlay:
+ return VK_MEDIA_PLAY_PAUSE;
+ case Qt::Key_MediaStop:
+ return VK_MEDIA_STOP;
+ case Qt::Key_VolumeDown:
+ return VK_VOLUME_DOWN;
+ case Qt::Key_VolumeUp:
+ return VK_VOLUME_UP;
+ case Qt::Key_VolumeMute:
+ return VK_VOLUME_MUTE;
+ case Qt::Key_Mode_switch:
+ return VK_MODECHANGE;
+ case Qt::Key_Select:
+ return VK_SELECT;
+ case Qt::Key_Printer:
+ return VK_PRINT;
+ case Qt::Key_Execute:
+ return VK_EXECUTE;
+ case Qt::Key_Sleep:
+ return VK_SLEEP;
+ case Qt::Key_Period:
+ return VK_DECIMAL;
+ case Qt::Key_Play:
+ return VK_PLAY;
+ case Qt::Key_Cancel:
+ return VK_CANCEL;
+
+ case Qt::Key_Forward:
+ return VK_BROWSER_FORWARD;
+ case Qt::Key_Refresh:
+ return VK_BROWSER_REFRESH;
+ case Qt::Key_Stop:
+ return VK_BROWSER_STOP;
+ case Qt::Key_Search:
+ return VK_BROWSER_SEARCH;
+ case Qt::Key_Favorites:
+ return VK_BROWSER_FAVORITES;
+ case Qt::Key_HomePage:
+ return VK_BROWSER_HOME;
+
+ case Qt::Key_LaunchMail:
+ return VK_LAUNCH_MAIL;
+ case Qt::Key_LaunchMedia:
+ return VK_LAUNCH_MEDIA_SELECT;
+ case Qt::Key_Launch0:
+ return VK_LAUNCH_APP1;
+ case Qt::Key_Launch1:
+ return VK_LAUNCH_APP2;
+
+ case Qt::Key_Massyo:
+ return VK_OEM_FJ_MASSHOU;
+ case Qt::Key_Touroku:
+ return VK_OEM_FJ_TOUROKU;
+
+ default:
+ if(keycode <= 0xFFFF)
+ return (byte)keycode;
+ else {
+ ok = false;
+ return 0;
+ }
+ }
+}
+
+quint32 QHotkeyPrivateWin::nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok)
+{
+ quint32 nMods = 0;
+ if (modifiers & Qt::ShiftModifier)
+ nMods |= MOD_SHIFT;
+ if (modifiers & Qt::ControlModifier)
+ nMods |= MOD_CONTROL;
+ if (modifiers & Qt::AltModifier)
+ nMods |= MOD_ALT;
+ if (modifiers & Qt::MetaModifier)
+ nMods |= MOD_WIN;
+ ok = true;
+ return nMods;
+}
+
+bool QHotkeyPrivateWin::registerShortcut(QHotkey::NativeShortcut shortcut)
+{
+ BOOL ok = RegisterHotKey(NULL,
+ HKEY_ID(shortcut),
+ shortcut.modifier + MOD_NOREPEAT,
+ shortcut.key);
+ if(ok)
+ return true;
+ else {
+ error = QHotkeyPrivateWin::formatWinError(::GetLastError());
+ return false;
+ }
+}
+
+bool QHotkeyPrivateWin::unregisterShortcut(QHotkey::NativeShortcut shortcut)
+{
+ BOOL ok = UnregisterHotKey(NULL, HKEY_ID(shortcut));
+ if(ok)
+ return true;
+ else {
+ error = QHotkeyPrivateWin::formatWinError(::GetLastError());
+ return false;
+ }
+}
+
+QString QHotkeyPrivateWin::formatWinError(DWORD winError)
+{
+ wchar_t *buffer = NULL;
+ DWORD num = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ winError,
+ 0,
+ (LPWSTR)&buffer,
+ 0,
+ NULL);
+ if(buffer) {
+ QString res = QString::fromWCharArray(buffer, num);
+ LocalFree(buffer);
+ return res;
+ } else
+ return QString();
+}
diff --git a/lib/QHotkey-1.5.0/QHotkey/qhotkey_x11.cpp b/lib/QHotkey-1.5.0/QHotkey/qhotkey_x11.cpp
new file mode 100644
index 0000000..ce408e6
--- /dev/null
+++ b/lib/QHotkey-1.5.0/QHotkey/qhotkey_x11.cpp
@@ -0,0 +1,268 @@
+#include "qhotkey.h"
+#include "qhotkey_p.h"
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
+ #include
+#else
+ #include
+ #include
+#endif
+
+#include
+#include
+#include
+#include
+
+//compability to pre Qt 5.8
+#ifndef Q_FALLTHROUGH
+#define Q_FALLTHROUGH() (void)0
+#endif
+
+class QHotkeyPrivateX11 : public QHotkeyPrivate
+{
+public:
+ // QAbstractNativeEventFilter interface
+ bool nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result) override;
+
+protected:
+ // QHotkeyPrivate interface
+ quint32 nativeKeycode(Qt::Key keycode, bool &ok) Q_DECL_OVERRIDE;
+ quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) Q_DECL_OVERRIDE;
+ static QString getX11String(Qt::Key keycode);
+ bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
+ bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
+
+private:
+ static const QVector specialModifiers;
+ static const quint32 validModsMask;
+ xcb_key_press_event_t prevHandledEvent;
+ xcb_key_press_event_t prevEvent;
+
+ static QString formatX11Error(Display *display, int errorCode);
+
+ class HotkeyErrorHandler {
+ public:
+ HotkeyErrorHandler();
+ ~HotkeyErrorHandler();
+
+ static bool hasError;
+ static QString errorString;
+
+ private:
+ XErrorHandler prevHandler;
+
+ static int handleError(Display *display, XErrorEvent *error);
+ };
+};
+NATIVE_INSTANCE(QHotkeyPrivateX11)
+
+bool QHotkeyPrivate::isPlatformSupported()
+{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
+ return qGuiApp->nativeInterface();
+#else
+ return QX11Info::isPlatformX11();
+#endif
+}
+
+const QVector QHotkeyPrivateX11::specialModifiers = {0, Mod2Mask, LockMask, (Mod2Mask | LockMask)};
+const quint32 QHotkeyPrivateX11::validModsMask = ShiftMask | ControlMask | Mod1Mask | Mod4Mask;
+
+bool QHotkeyPrivateX11::nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result)
+{
+ Q_UNUSED(eventType)
+ Q_UNUSED(result)
+
+ auto *genericEvent = static_cast(message);
+ if (genericEvent->response_type == XCB_KEY_PRESS) {
+ xcb_key_press_event_t keyEvent = *static_cast(message);
+ this->prevEvent = keyEvent;
+ if (this->prevHandledEvent.response_type == XCB_KEY_RELEASE) {
+ if(this->prevHandledEvent.time == keyEvent.time) return false;
+ }
+ this->activateShortcut({keyEvent.detail, keyEvent.state & QHotkeyPrivateX11::validModsMask});
+ } else if (genericEvent->response_type == XCB_KEY_RELEASE) {
+ xcb_key_release_event_t keyEvent = *static_cast(message);
+ this->prevEvent = keyEvent;
+ QTimer::singleShot(50, [this, keyEvent] {
+ if(this->prevEvent.time == keyEvent.time && this->prevEvent.response_type == keyEvent.response_type && this->prevEvent.detail == keyEvent.detail){
+ this->releaseShortcut({keyEvent.detail, keyEvent.state & QHotkeyPrivateX11::validModsMask});
+ }
+ });
+ this->prevHandledEvent = keyEvent;
+ }
+
+ return false;
+}
+
+QString QHotkeyPrivateX11::getX11String(Qt::Key keycode)
+{
+ switch(keycode){
+
+ case Qt::Key_MediaLast :
+ case Qt::Key_MediaPrevious :
+ return QStringLiteral("XF86AudioPrev");
+ case Qt::Key_MediaNext :
+ return QStringLiteral("XF86AudioNext");
+ case Qt::Key_MediaPause :
+ case Qt::Key_MediaPlay :
+ case Qt::Key_MediaTogglePlayPause :
+ return QStringLiteral("XF86AudioPlay");
+ case Qt::Key_MediaRecord :
+ return QStringLiteral("XF86AudioRecord");
+ case Qt::Key_MediaStop :
+ return QStringLiteral("XF86AudioStop");
+ default :
+ return QKeySequence(keycode).toString(QKeySequence::NativeText);
+ }
+}
+
+quint32 QHotkeyPrivateX11::nativeKeycode(Qt::Key keycode, bool &ok)
+{
+ QString keyString = getX11String(keycode);
+
+ KeySym keysym = XStringToKeysym(keyString.toLatin1().constData());
+ if (keysym == NoSymbol) {
+ //not found -> just use the key
+ if(keycode <= 0xFFFF)
+ keysym = keycode;
+ else
+ return 0;
+ }
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
+ const QNativeInterface::QX11Application *x11Interface = qGuiApp->nativeInterface();
+ Display *display = x11Interface->display();
+#else
+ const bool x11Interface = QX11Info::isPlatformX11();
+ Display *display = QX11Info::display();
+#endif
+
+ if(x11Interface) {
+ auto res = XKeysymToKeycode(display, keysym);
+ if(res != 0)
+ ok = true;
+ return res;
+ }
+ return 0;
+}
+
+quint32 QHotkeyPrivateX11::nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok)
+{
+ quint32 nMods = 0;
+ if (modifiers & Qt::ShiftModifier)
+ nMods |= ShiftMask;
+ if (modifiers & Qt::ControlModifier)
+ nMods |= ControlMask;
+ if (modifiers & Qt::AltModifier)
+ nMods |= Mod1Mask;
+ if (modifiers & Qt::MetaModifier)
+ nMods |= Mod4Mask;
+ ok = true;
+ return nMods;
+}
+
+bool QHotkeyPrivateX11::registerShortcut(QHotkey::NativeShortcut shortcut)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
+ const QNativeInterface::QX11Application *x11Interface = qGuiApp->nativeInterface();
+ Display *display = x11Interface->display();
+#else
+ const bool x11Interface = QX11Info::isPlatformX11();
+ Display *display = QX11Info::display();
+#endif
+
+ if(!display || !x11Interface)
+ return false;
+
+ HotkeyErrorHandler errorHandler;
+ for(quint32 specialMod : QHotkeyPrivateX11::specialModifiers) {
+ XGrabKey(display,
+ shortcut.key,
+ shortcut.modifier | specialMod,
+ DefaultRootWindow(display),
+ True,
+ GrabModeAsync,
+ GrabModeAsync);
+ }
+ XSync(display, False);
+
+ if(errorHandler.hasError) {
+ error = errorHandler.errorString;
+ this->unregisterShortcut(shortcut);
+ return false;
+ }
+ return true;
+}
+
+bool QHotkeyPrivateX11::unregisterShortcut(QHotkey::NativeShortcut shortcut)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
+ Display *display = qGuiApp->nativeInterface()->display();
+#else
+ Display *display = QX11Info::display();
+#endif
+
+ if(!display)
+ return false;
+
+ HotkeyErrorHandler errorHandler;
+ for(quint32 specialMod : QHotkeyPrivateX11::specialModifiers) {
+ XUngrabKey(display,
+ shortcut.key,
+ shortcut.modifier | specialMod,
+ XDefaultRootWindow(display));
+ }
+ XSync(display, False);
+
+ if(HotkeyErrorHandler::hasError) {
+ error = HotkeyErrorHandler::errorString;
+ return false;
+ }
+ return true;
+}
+
+QString QHotkeyPrivateX11::formatX11Error(Display *display, int errorCode)
+{
+ char errStr[256];
+ XGetErrorText(display, errorCode, errStr, 256);
+ return QString::fromLatin1(errStr);
+}
+
+
+
+// ---------- QHotkeyPrivateX11::HotkeyErrorHandler implementation ----------
+
+bool QHotkeyPrivateX11::HotkeyErrorHandler::hasError = false;
+QString QHotkeyPrivateX11::HotkeyErrorHandler::errorString;
+
+QHotkeyPrivateX11::HotkeyErrorHandler::HotkeyErrorHandler()
+{
+ prevHandler = XSetErrorHandler(&HotkeyErrorHandler::handleError);
+}
+
+QHotkeyPrivateX11::HotkeyErrorHandler::~HotkeyErrorHandler()
+{
+ XSetErrorHandler(prevHandler);
+ hasError = false;
+ errorString.clear();
+}
+
+int QHotkeyPrivateX11::HotkeyErrorHandler::handleError(Display *display, XErrorEvent *error)
+{
+ switch (error->error_code) {
+ case BadAccess:
+ case BadValue:
+ case BadWindow:
+ if (error->request_code == 33 || //grab key
+ error->request_code == 34) {// ungrab key
+ hasError = true;
+ errorString = QHotkeyPrivateX11::formatX11Error(display, error->error_code);
+ return 1;
+ }
+ Q_FALLTHROUGH();
+ // fall through
+ default:
+ return 0;
+ }
+}
diff --git a/qml/AppView.qml b/qml/AppView.qml
index 5c6b4cc..6a286ef 100644
--- a/qml/AppView.qml
+++ b/qml/AppView.qml
@@ -9,6 +9,8 @@ import "."
Item {
id:root
+ property bool pinned: false
+ property alias inputText: inputArea.text
signal settingClicked;
function startTrans(){
if(inputArea.text.length > 0 && transBtn.visible)
@@ -153,6 +155,7 @@ Item {
onClicked: {
mainWindow.flags = mainWindow.flags & (0xFFFFFFFF ^ Qt.WindowStaysOnTopHint)
tItem.state = "no"
+ pinned = false
}
}
@@ -167,6 +170,7 @@ Item {
onClicked: {
mainWindow.flags = mainWindow.flags |Qt.WindowStaysOnTopHint
tItem.state = "yes"
+ pinned = true
}
}
diff --git a/qml/GPopWindow.qml b/qml/GPopWindow.qml
new file mode 100644
index 0000000..a2d1d48
--- /dev/null
+++ b/qml/GPopWindow.qml
@@ -0,0 +1,82 @@
+import QtQuick
+import QtQuick.Controls
+
+import QtLocation
+import Qt5Compat.GraphicalEffects
+Window {
+ flags:Qt.Window | Qt.FramelessWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
+
+ id:popWindow
+ height:20
+ width:100
+ maximumHeight: height
+ maximumWidth: width
+ minimumHeight: height
+ minimumWidth: width
+ color: "transparent"
+
+
+
+ Rectangle{
+ id:_rect
+ anchors.fill: parent
+ radius:5
+ clip:true
+
+ Image {
+ id: img
+ source:"qrc:/res/icon.png"
+
+ anchors.left:parent.left
+ anchors.top: parent.top
+ height:parent.height
+ width:height
+ fillMode: Image.PreserveAspectCrop
+ layer.enabled: true
+ layer.effect: OpacityMask {
+ maskSource: mask
+ }
+ MouseArea{
+ anchors.fill: parent
+ property variant clickPos: "1,1"
+ onPressed: {
+ clickPos = Qt.point(mouseX ,mouseY)
+ }
+
+ onPositionChanged: {
+ var delta = Qt.point(mouseX -clickPos.x, mouseY-clickPos.y)
+ popWindow.x += delta.x;
+ popWindow.y += delta.y;
+ }
+ }
+ }
+
+ Rectangle {
+ id: mask
+ width: popWindow.width
+ height: width
+ radius: 5
+ visible: false
+ }
+
+ }
+
+
+
+// Column{
+// anchors.fill: parent
+// Button{
+// width:parent.width
+// height:parent.height/2
+
+// }
+// Button{
+// width:parent.width
+// height:parent.height/2
+
+// }
+// }
+
+
+
+}
diff --git a/qml/SettingView.qml b/qml/SettingView.qml
index f7a4782..cde8f0c 100644
--- a/qml/SettingView.qml
+++ b/qml/SettingView.qml
@@ -6,16 +6,46 @@ import Controller
Item {
signal backClicked;
+
+ property bool lock:false
+
+
function reload(){
setting.loadConfig()
+ lock = true
keyInput.text = setting.apiKey
serverInput.text = setting.apiServer
+ shortcutText.text = setting.shortCut
if(setting.model == "gpt-3.5-turbo")
modelSelector.currentIndex = 0
else if(setting.model == "gpt-4")
modelSelector.currentIndex = 1
- saveBtn.visible = false
+ lock = false
+
+ }
+
+ function saveConfig(){
+ if(lock){
+ return
+ }
+ setting.apiServer = serverInput.text.trim()
+ setting.apiKey = keyInput.text
+ setting.shortCut = shortcutText.text
+ if(modelSelector.currentIndex == 0)
+ setting.model = "gpt-3.5-turbo"
+ else
+ setting.model = "gpt-4"
+ setting.updateConfig()
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ shortcutRect.focus = false;
+ if(shortcutText.text.length > 0){
+ hotkey.setShortcut(shortcutText.text)
+ }
+ }
}
@@ -42,30 +72,6 @@ Item {
}
}
-
-
- IconButton{
- id:saveBtn
- width: 17.5
- height:20
- visible:false
- anchors.right: parent.right
- anchors.top: parent.top
- normalUrl:"qrc:///res/save1.png"
- hoveredUrl:"qrc:///res/save1.png"
- pressedUrl:"qrc:///res/save2.png"
- onClicked: {
- setting.apiServer = serverInput.text.trim()
- setting.apiKey = keyInput.text
- if(modelSelector.currentIndex == 0)
- setting.model = "gpt-3.5-turbo"
- else
- setting.model = "gpt-4"
- setting.updateConfig()
- visible = false
- }
- }
-
}
Text{
id:serverText
@@ -96,7 +102,7 @@ Item {
padding:7
text: "https://api.openai.com"
onTextChanged: {
- saveBtn.visible = true
+ saveConfig()
}
}
}
@@ -133,7 +139,7 @@ Item {
y:20
wrapMode: Text.WrapAnywhere
onTextChanged:{
- saveBtn.visible = true
+ saveConfig()
}
background: Rectangle{
color: "#E6E7E7"
@@ -161,17 +167,153 @@ Item {
currentIndex: 0
model:["GPT-3.5", "GPT-4"]
onCurrentIndexChanged:{
- saveBtn.visible = true
+ saveConfig()
}
height:40
}
+ Text{
+ id:shortCutText
+ anchors.left: header.left
+ anchors.top:modelSelector.bottom
+ anchors.topMargin: 20
+ text:"Shortcut"
+ font.bold: true
+ color:"green"
+ }
+
+ Item {
+ id:shortcutItem
+ anchors.left: header.left
+ anchors.top:shortCutText.bottom
+ anchors.topMargin: 10
+ width:100
+ height:30
+ Rectangle {
+ id:shortcutRect
+ color: "#E6E7E7"
+ anchors.fill: parent
+ radius: 5
+ border.width:1
+ border.color: color
+ onActiveFocusChanged: {
+ }
+
+ onFocusChanged: {
+ if(focus){
+ border.color = "green"
+ shortcutRect.forceActiveFocus()
+ shortcutText.text = ""
+ hotkey.setShortcut("")
+ }else{
+ border.color = color
+ }
+
+ }
+
+ Text{
+ id:shortcutText
+ anchors.centerIn: parent
+ text:""
+ onTextChanged: {
+ saveConfig()
+ }
+ }
+
+ Keys.onPressed:(event)=> {
+ if(!shortcutRect.focus){
+ return
+ }
+
+ shortcutText.text = ""
+ var vaild = false
+ var haveCtrl = false
+
+
+ if(Qt.platform.os === "macos" || Qt.platform.os === "osx"){
+ if (event.modifiers & Qt.ControlModifier) {
+ shortcutText.text = "Ctrl+"
+ haveCtrl = true
+ }
+ if (event.modifiers & Qt.MetaModifier) {
+ shortcutText.text = "Meta+"
+ haveCtrl = true
+ }
+ }else{
+ if (event.modifiers & Qt.ControlModifier) {
+ shortcutText.text = "Ctrl+"
+ haveCtrl = true
+ }
+ }
+
+ if (event.modifiers & Qt.AltModifier) {
+ shortcutText.text = "Alt+"
+ haveCtrl = true
+ }
+ if (event.modifiers & Qt.ShiftModifier) {
+ shortcutText.text = "Shift+"
+ haveCtrl = true
+ }
+
+ if(shortCutText.text.length > 0){
+ switch(event.key){
+ case Qt.Key_F1: shortcutText.text = "F1"; vaild = true; break;
+ case Qt.Key_F2: shortcutText.text = "F2";vaild = true; break;
+ case Qt.Key_F3: shortcutText.text = "F3";vaild = true; break;
+ case Qt.Key_F4: shortcutText.text = "F4"; vaild = true; break;
+ case Qt.Key_F5: shortcutText.text = "F5"; vaild = true; break;
+ case Qt.Key_F6: shortcutText.text = "F6";vaild = true; break;
+ case Qt.Key_F7: shortcutText.text = "F7"; vaild = true; break;
+ case Qt.Key_F8: shortcutText.text = "F8"; vaild = true; break;
+ case Qt.Key_F9: shortcutText.text = "F9"; vaild = true; break;
+ case Qt.Key_F10: shortcutText.text = "F10"; vaild = true; break;
+ case Qt.Key_F11: shortcutText.text = "F11"; vaild = true; break;
+ case Qt.Key_F12: shortcutText.text = "F12"; vaild = true; break;
+ }
+ if(event.key >= Qt.Key_0 && event.key <= Qt.Key_9 ){
+ if(haveCtrl){
+ shortcutText.text += String.fromCharCode(event.key)
+ vaild = true
+ }
+ }else if(event.key >= Qt.Key_A && event.key <= Qt.Key_Z ){
+ if(haveCtrl){
+ shortcutText.text += String.fromCharCode(event.key)
+ vaild = true
+ }
+ }
+ }
+
+ if(vaild){
+ shortcutRect.focus = false;
+ if(shortcutText.text.length > 0){
+ if(hotkey.setShortcut(shortcutText.text) == false){
+ shortcutRect.focus = false;
+ shortcutText.text = ""
+ }
+ }
+ }
+
+
+ }
+
+ }
+
+ MouseArea{
+ anchors.fill: parent
+ onClicked: {
+ shortcutRect.focus = true
+ }
+ }
+
+
+
+ }
Text{
id:about
anchors.left: header.left
- anchors.top:modelSelector.bottom
+ anchors.top:shortcutItem.bottom
anchors.topMargin: 20
text:"About"
font.bold: true
diff --git a/qml/main.qml b/qml/main.qml
index 0e29f68..bd28dc0 100644
--- a/qml/main.qml
+++ b/qml/main.qml
@@ -1,11 +1,13 @@
import QtQuick
import QtQuick.Controls
-
+import QtQuick.Window
import QtQuick.Layouts
import Qt.labs.platform
import "."
+import Controller
+
Window {
id: mainWindow
@@ -16,11 +18,66 @@ Window {
minimumWidth:400
title: qsTr("GPT Translator")
+ property Component popComponent: null
+ property QtObject popW: null
+
+
// flags:Qt.Window | Qt.FramelessWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
+ function popWindow(t, pos){
+
+ if(popComponent !== null){
+ popW.close()
+ popComponent.destroy()
+ }
+
+ popComponent = Qt.createComponent("GPopWindow.qml")
+ if (popComponent.status === Component.Ready) {
+ popW = popComponent.createObject(mainWindow)
+ if (popW) {
+ popW.x = pos.x
+ popW.y = pos.y
+ popW.visible = true
+ popW.show()
+ popW.raise()
+ popW.requestActivate()
+ mainWindow.visible = false
+ } else {
+ console.error("Error creating new window:", popComponent.errorString())
+ }
+ } else {
+ console.error("Error loading DynamicWindow component:", popComponent.errorString())
+ }
+
+ }
+ Hotkey{
+ id:hotkey
+ onSelectedTextChanged: {
+// popWindow(selectedText, mousePos)
+ appView.inputText = selectedText
+ appView.startTrans()
+ mainWindow.show()
+ mainWindow.raise()
+ mainWindow.requestActivate()
+
+ }
+
+ }
+
+
+
+ Component.onCompleted: {
+ hotkey.binding(app)
+ hotkey.setShortcut(setting.shortCut)
+ }
+
MouseArea{
+ id:mouseArea
anchors.fill: parent
property variant clickPos: "1,1"
+ onClicked: {
+
+ }
onPressed: {
clickPos = Qt.point(mouseX ,mouseY)
@@ -32,15 +89,29 @@ Window {
mainWindow.y += delta.y;
}
}
+
+ onActiveChanged: {
+// if(active){
+// mainWindow.visible = true
+// }else{
+// if(!appView.pinned)
+// mainWindow.visible = false
+// }
+ }
+
+
+
+
+
Item{
anchors.fill: parent
focus: true
Keys.onPressed:(event)=> {
- if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_R ||
- (event.modifiers & Qt.MetaModifier) && event.key === Qt.Key_R) {
- // Command+R or Ctrl+R pressed
- appView.startTrans()
- }
+ if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_R ||
+ (event.modifiers & Qt.MetaModifier) && event.key === Qt.Key_R) {
+ // Command+R or Ctrl+R pressed
+ appView.startTrans()
+ }
}
SystemTrayIcon {
@@ -63,7 +134,13 @@ Window {
mainWindow.show()
mainWindow.raise()
mainWindow.requestActivate()
+
+// mainWindow.x = trayIcon.geometry.x - mainWindow.width/2
+// mainWindow.y = trayIcon.geometry.y + 50
+// mainWindow.visible = true
}
+
+
}
SwipeView {
@@ -90,4 +167,5 @@ Window {
}
+
}
diff --git a/res/icon.png b/res/icon.png
new file mode 100644
index 0000000..9d4de2a
Binary files /dev/null and b/res/icon.png differ
diff --git a/src/controller.cpp b/src/controller.cpp
index b2fa848..dc9a06d 100644
--- a/src/controller.cpp
+++ b/src/controller.cpp
@@ -7,6 +7,7 @@ Setting::Setting(QObject * parent): QObject{parent}
_apiServer = "";
_apiKey = "";
_model = "";
+ _shortCut = "";
//win C:\Users\xxxx\AppData\Local\GPT_Translator
//macos /Users/xxx/Library/Preferences/GPT_Translator/
_configPath = QStandardPaths::locate(QStandardPaths::AppConfigLocation, "config.json", QStandardPaths::LocateFile);
@@ -51,6 +52,7 @@ bool Setting::loadConfig()
_apiKey = obj.value("apiKey").toString();
_model = obj.value("model").toString();
_apiServer = obj.value("apiServer").toString();
+ _shortCut = obj.value("shortCut").toString();
if(_apiServer.trimmed().length() == 0){
_apiServer = "https://api.openai.com";
}
@@ -62,7 +64,7 @@ bool Setting::loadConfig()
void Setting::updateConfig()
{
- QString s = "{\"apiKey\":\"" + _apiKey + "\",\"model\":\"" + _model + "\", \"apiServer\":\"" + _apiServer + "\"}";
+ QString s = "{\"apiKey\":\"" + _apiKey + "\",\"model\":\"" + _model + "\", \"apiServer\":\"" + _apiServer + "\", \"shortCut\":\"" + _shortCut + "\"}";
QFile file(_configPath);
if(file.open(QIODevice::WriteOnly)){
QTextStream out(&file);
@@ -158,7 +160,7 @@ void Controller::sendMessage(QString str, int mode)
QUrl apiUrl(_apiServer + "/v1/chat/completions");
QNetworkRequest request(apiUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- request.setRawHeader("Authorization", QString::fromStdString("Bearer %1").arg(_apiKey).toUtf8());
+ request.setRawHeader("Authorization", QString::fromStdString("Bearer %1").arg(_apiKey.trimmed()).toUtf8());
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); // Events shouldn't be cached
QJsonObject requestData;
diff --git a/src/controller.h b/src/controller.h
index b026d84..cb4e8c4 100644
--- a/src/controller.h
+++ b/src/controller.h
@@ -1,7 +1,7 @@
/*
* @Date: 2023-04-07 15:38:00
* @LastEditors: JessGuo
- * @LastEditTime: 2023-04-07 15:54:06
+ * @LastEditTime: 2023-05-04 23:49:11
* @FilePath: /GPT_Translator/src/controller.h
*/
#ifndef CONTROLLER_H
@@ -31,6 +31,7 @@ class Setting : public QObject
Q_PROPERTY_AUTO(QString,apiServer);
Q_PROPERTY_AUTO(QString,apiKey);
Q_PROPERTY_AUTO(QString,model);
+ Q_PROPERTY_AUTO(QString,shortCut);
public:
explicit Setting(QObject *parent = nullptr);
Q_INVOKABLE bool loadConfig();
diff --git a/src/hotkey.cpp b/src/hotkey.cpp
new file mode 100644
index 0000000..ddaae7b
--- /dev/null
+++ b/src/hotkey.cpp
@@ -0,0 +1,126 @@
+/*
+ * @Date: 2023-05-02 23:52:58
+ * @LastEditors: JessGuo
+ * @LastEditTime: 2023-05-05 22:34:50
+ * @FilePath: /GPT_Translator/src/hotkey.cpp
+ */
+#include "hotkey.h"
+
+#ifdef Q_OS_MAC
+#include
+#endif
+
+#ifdef Q_OS_WIN
+#include
+#endif
+
+#ifdef Q_OS_LINUX
+#include
+#include
+#include
+#endif
+
+
+Hotkey::Hotkey(QObject *parent)
+ : QObject{parent}
+{
+
+}
+
+void Hotkey::binding(QObject *obj)
+{
+ _hotkey = new QHotkey(QKeySequence(""), true, obj); //The hotkey will be automatically registered
+ qDebug() << "Is segistered:" << _hotkey->isRegistered();
+
+
+ QObject::connect(_hotkey, &QHotkey::activated, obj, [&,this](){
+ qDebug() << "Hotkey Activated ";
+
+#ifdef Q_OS_MAC
+ CGEventRef push = CGEventCreateKeyboardEvent(NULL, 0x08, true);//0x08=='c'
+ CGEventSetFlags(push, kCGEventFlagMaskCommand);
+ CGEventPost(kCGHIDEventTap, push);
+
+ push = CGEventCreateKeyboardEvent(NULL, 0x08, false);//0x08=='c'
+ CGEventSetFlags(push, kCGEventFlagMaskCommand);
+ CGEventPost(kCGHIDEventTap, push);
+
+ CGPoint mouseLocation;
+ mouseLocation = CGEventGetLocation(CGEventCreate(NULL));
+ qDebug() << "x:" << mouseLocation.x;
+ qDebug() << "y:" << mouseLocation.y;
+ _mousePos.setX(mouseLocation.x);
+ _mousePos.setY(mouseLocation.y);
+#endif
+
+#ifdef Q_OS_WIN
+ // 为按下和释放 Ctrl+C 准备输入事件
+ INPUT inputs[4] = { 0 };
+ inputs[0].type = inputs[1].type = inputs[2].type = inputs[3].type = INPUT_KEYBOARD;
+ inputs[0].ki.wVk = inputs[2].ki.wVk = VK_CONTROL;
+ inputs[1].ki.wVk = inputs[3].ki.wVk = 'C';
+ inputs[2].ki.dwFlags = inputs[3].ki.dwFlags = KEYEVENTF_KEYUP;
+
+ // 发送输入事件
+ UINT numEventsSent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
+ if (numEventsSent != ARRAYSIZE(inputs)) {
+ qDebug() << "SendInput failed: " << GetLastError();
+ }
+#endif
+
+#ifdef Q_OS_LINUX
+ Display *display = XOpenDisplay(NULL);
+ if (display == NULL) {
+ qDebug() << "Unable to open X display.";
+ return;
+ }
+
+ Window root = DefaultRootWindow(display);
+ KeyCode ctrl_key = XKeysymToKeycode(display, XK_Control_L);
+ KeyCode c_key = XKeysymToKeycode(display, XK_c);
+
+ XEvent event;
+ memset(&event, 0, sizeof(event));
+ event.xkey.type = KeyPress;
+ event.xkey.root = root;
+ event.xkey.window = root;
+ event.xkey.same_screen = True;
+
+ // 按下 Ctrl 键
+ event.xkey.keycode = ctrl_key;
+ XSendEvent(display, root, True, KeyPressMask, &event);
+ XFlush(display);
+ usleep(10000);
+
+ // 按下 C 键
+ event.xkey.keycode = c_key;
+ XSendEvent(display, root, True, KeyPressMask, &event);
+ XFlush(display);
+ usleep(10000);
+
+ // 释放 C 键
+ event.xkey.type = KeyRelease;
+ event.xkey.keycode = c_key;
+ XSendEvent(display, root, True, KeyReleaseMask, &event);
+ XFlush(display);
+ usleep(10000);
+
+ // 释放 Ctrl 键
+ event.xkey.keycode = ctrl_key;
+ XSendEvent(display, root, True, KeyReleaseMask, &event);
+ XFlush(display);
+ usleep(10000);
+
+ XCloseDisplay(display);
+#endif
+ // Use a timer to wait for the copy operation to complete
+ QTimer::singleShot(200, [this] {
+ QClipboard *clipboard = QGuiApplication::clipboard();
+ QString copiedText = clipboard->text();
+ qDebug() << "Copied text:" << copiedText;
+ this->_selectedText = copiedText;
+ this->selectedTextChanged();
+ });
+
+ });
+}
diff --git a/src/hotkey.h b/src/hotkey.h
new file mode 100644
index 0000000..d8f1f38
--- /dev/null
+++ b/src/hotkey.h
@@ -0,0 +1,38 @@
+#ifndef HOTKEY_H
+#define HOTKEY_H
+
+#include
+
+#include "stdafx.h"
+
+#include
+#include
+#include
+#include
+#include
+
+
+
+class Hotkey : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY_AUTO(QString,selectedText);
+ Q_PROPERTY_AUTO(QPoint,mousePos);
+
+public:
+ explicit Hotkey(QObject *parent = nullptr);
+
+ Q_INVOKABLE void binding(QObject * obj);
+
+ Q_INVOKABLE bool setShortcut(QString str){
+ return _hotkey->setShortcut(QKeySequence(str), true);
+ }
+
+
+private:
+ QHotkey *_hotkey;
+signals:
+
+};
+
+#endif // HOTKEY_H
diff --git a/src/main.cpp b/src/main.cpp
index ab7ea31..4b3b058 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -14,17 +14,31 @@
#include "updater.h"
#include
-#include
+#include "hotkey.h"
+
int main(int argc, char *argv[])
{
- QString type = QSysInfo::productType();
- if((type != "macos") && (type != "windows")){
+
+ #ifdef Q_OS_WIN
+ qDebug() << "Current OS: Windows";
+ #endif
+
+ #ifdef Q_OS_MAC
+ qDebug() << "Current OS: macOS";
+ #endif
+
+ #ifdef Q_OS_LINUX
+ qDebug() << "Current OS: Linux";
+ #endif
+
+ #ifdef Q_OS_LINUX
qputenv("QT_QUICK_BACKEND","software");//Failed to build graphics pipeline state under linux, need to be software
- }
+ #endif
QGuiApplication app(argc, argv);
app.setWindowIcon(QIcon("qrc:///res/logo/logo.ico"));
+
QTranslator translator;
const QStringList uiLanguages = QLocale::system().uiLanguages();
for (const QString &locale : uiLanguages) {
@@ -42,15 +56,19 @@ int main(int argc, char *argv[])
#endif
Setting * setting = new Setting();
-
+// Hotkey *key = new Hotkey();
+// key->binding(&app);
qmlRegisterType("Controller",1,0,"APIController");
qmlRegisterType("Updater",1,0,"APIUpdater");
+ qmlRegisterType("Controller",1,0,"Hotkey");
QQmlApplicationEngine engine;
const QUrl url(u"qrc:///qml/main.qml"_qs);
engine.rootContext()->setContextProperty("setting", setting);
+ engine.rootContext()->setContextProperty("app", &app);
+
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)