diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f8e7b2269..4d4f84982 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -174,7 +174,9 @@ if(APPLE) set(keepassx_SOURCES ${keepassx_SOURCES} core/ScreenLockListenerMac.cpp - core/MacPasteboard.cpp) + core/MacPasteboard.cpp + gui/macutils/MacUtils.cpp + gui/macutils/AppKitImpl.mm) endif() if(UNIX AND NOT APPLE) set(keepassx_SOURCES @@ -272,7 +274,7 @@ target_link_libraries(keepassx_core ${ZXCVBN_LIBRARIES}) if(APPLE) - target_link_libraries(keepassx_core "-framework Foundation") + target_link_libraries(keepassx_core "-framework Foundation -framework AppKit") if(Qt5MacExtras_FOUND) target_link_libraries(keepassx_core Qt5::MacExtras) endif() diff --git a/src/autotype/mac/AutoTypeMac.cpp b/src/autotype/mac/AutoTypeMac.cpp index d8fcf6d7f..60cec1144 100644 --- a/src/autotype/mac/AutoTypeMac.cpp +++ b/src/autotype/mac/AutoTypeMac.cpp @@ -17,6 +17,7 @@ */ #include "AutoTypeMac.h" +#include "gui/macutils/MacUtils.h" #include @@ -25,8 +26,7 @@ #define INVALID_KEYCODE 0xFFFF AutoTypePlatformMac::AutoTypePlatformMac() - : m_appkit(new AppKit()) - , m_hotkeyRef(nullptr) + : m_hotkeyRef(nullptr) , m_hotkeyId({ 'kpx2', HOTKEY_ID }) { EventTypeSpec eventSpec; @@ -79,7 +79,7 @@ QStringList AutoTypePlatformMac::windowTitles() // WId AutoTypePlatformMac::activeWindow() { - return m_appkit->activeProcessId(); + return macUtils()->activeWindow(); } // @@ -159,7 +159,7 @@ AutoTypeExecutor* AutoTypePlatformMac::createExecutor() // bool AutoTypePlatformMac::raiseWindow(WId pid) { - return m_appkit->activateProcess(pid); + return macUtils()->raiseWindow(pid); } // @@ -167,7 +167,7 @@ bool AutoTypePlatformMac::raiseWindow(WId pid) // bool AutoTypePlatformMac::raiseLastActiveWindow() { - return m_appkit->activateProcess(m_appkit->lastActiveProcessId()); + return macUtils()->raiseLastActiveWindow(); } // @@ -175,7 +175,7 @@ bool AutoTypePlatformMac::raiseLastActiveWindow() // bool AutoTypePlatformMac::raiseOwnWindow() { - return m_appkit->activateProcess(m_appkit->ownProcessId()); + return macUtils()->raiseOwnWindow(); } // diff --git a/src/autotype/mac/AutoTypeMac.h b/src/autotype/mac/AutoTypeMac.h index d2c224784..875c21764 100644 --- a/src/autotype/mac/AutoTypeMac.h +++ b/src/autotype/mac/AutoTypeMac.h @@ -23,7 +23,6 @@ #include #include -#include "AppKit.h" #include "autotype/AutoTypePlatformPlugin.h" #include "autotype/AutoTypeAction.h" @@ -55,7 +54,6 @@ signals: void globalShortcutTriggered(); private: - std::unique_ptr m_appkit; EventHotKeyRef m_hotkeyRef; EventHotKeyID m_hotkeyId; diff --git a/src/autotype/mac/CMakeLists.txt b/src/autotype/mac/CMakeLists.txt index f2915bc0a..f1c5387f3 100644 --- a/src/autotype/mac/CMakeLists.txt +++ b/src/autotype/mac/CMakeLists.txt @@ -1,6 +1,8 @@ set(autotype_mac_SOURCES AutoTypeMac.cpp) -set(autotype_mac_mm_SOURCES AppKitImpl.mm) +set(autotype_mac_mm_SOURCES + ${CMAKE_SOURCE_DIR}/src/gui/macutils/AppKitImpl.mm + ${CMAKE_SOURCE_DIR}/src/gui/macutils/MacUtils.cpp) 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") diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index bf42eab31..0ccd31062 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -33,8 +33,12 @@ #include "core/Group.h" #include "core/Metadata.h" #include "core/PasswordGenerator.h" +#include "core/Tools.h" #include "gui/MainWindow.h" #include "gui/MessageBox.h" +#ifdef Q_OS_MACOS +#include "gui/macutils/MacUtils.h" +#endif const char BrowserService::KEEPASSXCBROWSER_NAME[] = "KeePassXC-Browser Settings"; const char BrowserService::KEEPASSXCBROWSER_OLD_NAME[] = "keepassxc-browser Settings"; @@ -50,6 +54,7 @@ BrowserService::BrowserService(DatabaseTabWidget* parent) : m_dbTabWidget(parent) , m_dialogActive(false) , m_bringToFrontRequested(false) + , m_wasMinimized(false) , m_keepassBrowserUUID(QUuid::fromRfc4122(QByteArray::fromHex("de887cc3036343b8974b5911b8816224"))) { // Don't connect the signals when used from DatabaseSettingsWidgetBrowser (parent is nullptr) @@ -90,8 +95,9 @@ bool BrowserService::openDatabase(bool triggerUnlock) } if (triggerUnlock) { - getMainWindow()->bringToFront(); m_bringToFrontRequested = true; + m_wasMinimized = getMainWindow()->isMinimized(); + raiseWindow(true); } return false; @@ -168,6 +174,7 @@ QString BrowserService::storeKey(const QString& key) "give it a unique name to identify and accept it.")); keyDialog.setOkButtonText(tr("Save and allow access")); keyDialog.setWindowFlags(keyDialog.windowFlags() | Qt::WindowStaysOnTopHint); + raiseWindow(); keyDialog.show(); keyDialog.activateWindow(); keyDialog.raise(); @@ -176,6 +183,7 @@ QString BrowserService::storeKey(const QString& key) id = keyDialog.textValue(); if (ok != QDialog::Accepted || id.isEmpty()) { + hideWindow(); return {}; } @@ -191,6 +199,7 @@ QString BrowserService::storeKey(const QString& key) } } while (contains && dialogResult == MessageBox::Cancel); + hideWindow(); db->metadata()->customData()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + id, key); return id; } @@ -371,12 +380,13 @@ void BrowserService::updateEntry(const QString& id, || entry->password().compare(password, Qt::CaseSensitive) != 0) { MessageBox::Button dialogResult = MessageBox::No; if (!browserSettings()->alwaysAllowUpdate()) { + raiseWindow(); dialogResult = MessageBox::question(nullptr, tr("KeePassXC: Update Entry"), tr("Do you want to update the information in %1 - %2?") .arg(QUrl(url).host(), username), MessageBox::Save | MessageBox::Cancel, - MessageBox::Cancel); + MessageBox::Cancel, MessageBox::Raise); } if (browserSettings()->alwaysAllowUpdate() || dialogResult == MessageBox::Save) { @@ -385,6 +395,8 @@ void BrowserService::updateEntry(const QString& id, entry->setPassword(password); entry->endUpdate(); } + + hideWindow(); } } @@ -591,6 +603,11 @@ bool BrowserService::confirmEntries(QList& pwEntriesToConfirm, accessControlDialog.setUrl(url); accessControlDialog.setItems(pwEntriesToConfirm); + raiseWindow(); + accessControlDialog.show(); + accessControlDialog.activateWindow(); + accessControlDialog.raise(); + int res = accessControlDialog.exec(); if (accessControlDialog.remember()) { for (Entry* entry : pwEntriesToConfirm) { @@ -614,6 +631,7 @@ bool BrowserService::confirmEntries(QList& pwEntriesToConfirm, } m_dialogActive = false; + hideWindow(); if (res == QDialog::Accepted) { return true; } @@ -909,6 +927,32 @@ bool BrowserService::checkLegacySettings() return dialogResult == MessageBox::Yes; } +void BrowserService::hideWindow() const +{ + if (m_wasMinimized) { + getMainWindow()->showMinimized(); + } else { +#ifdef Q_OS_MACOS + macUtils()->raiseLastActiveWindow(); +#else + getMainWindow()->lower(); +#endif + } +} + +void BrowserService::raiseWindow(const bool force) +{ + m_wasMinimized = getMainWindow()->isMinimized(); +#ifdef Q_OS_MACOS + macUtils()->raiseOwnWindow(); + Tools::wait(500); +#else + if (force) { + getMainWindow()->bringToFront(); + } +#endif +} + void BrowserService::databaseLocked(DatabaseWidget* dbWidget) { if (dbWidget) { @@ -920,7 +964,7 @@ void BrowserService::databaseUnlocked(DatabaseWidget* dbWidget) { if (dbWidget) { if (m_bringToFrontRequested) { - getMainWindow()->lower(); + hideWindow(); m_bringToFrontRequested = false; } emit databaseUnlocked(); diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 335f2a13b..74febe4f0 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -117,11 +117,14 @@ private: bool moveSettingsToCustomData(Entry* entry, const QString& name) const; int moveKeysToCustomData(Entry* entry, QSharedPointer db) const; bool checkLegacySettings(); + void hideWindow() const; + void raiseWindow(const bool force = false); private: DatabaseTabWidget* const m_dbTabWidget; bool m_dialogActive; bool m_bringToFrontRequested; + bool m_wasMinimized; QUuid m_keepassBrowserUUID; }; diff --git a/src/gui/DatabaseOpenDialog.cpp b/src/gui/DatabaseOpenDialog.cpp index 1b8b018e2..ff7d46f22 100644 --- a/src/gui/DatabaseOpenDialog.cpp +++ b/src/gui/DatabaseOpenDialog.cpp @@ -25,7 +25,11 @@ DatabaseOpenDialog::DatabaseOpenDialog(QWidget* parent) , m_view(new DatabaseOpenWidget(this)) { setWindowTitle(tr("Unlock Database - KeePassXC")); +#ifdef Q_OS_MACOS + setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); +#else setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint | Qt::ForeignWindow); +#endif connect(m_view, SIGNAL(dialogFinished(bool)), this, SLOT(complete(bool))); } diff --git a/src/gui/DatabaseOpenDialog.h b/src/gui/DatabaseOpenDialog.h index 236d2e0c2..f2860fccb 100644 --- a/src/gui/DatabaseOpenDialog.h +++ b/src/gui/DatabaseOpenDialog.h @@ -15,8 +15,8 @@ * along with this program. If not, see . */ -#ifndef KEEPASSX_AUTOTYPEUNLOCKDIALOG_H -#define KEEPASSX_AUTOTYPEUNLOCKDIALOG_H +#ifndef KEEPASSX_UNLOCKDATABASEDIALOG_H +#define KEEPASSX_UNLOCKDATABASEDIALOG_H #include "core/Global.h" @@ -37,7 +37,8 @@ public: { None, AutoType, - Merge + Merge, + Browser }; explicit DatabaseOpenDialog(QWidget* parent = nullptr); @@ -61,4 +62,4 @@ private: Intent m_intent = Intent::None; }; -#endif // KEEPASSX_AUTOTYPEUNLOCKDIALOG_H +#endif // KEEPASSX_UNLOCKDATABASEDIALOG_H diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 21c90cfa0..7e41035e3 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -39,6 +39,9 @@ #include "gui/DatabaseOpenDialog.h" #include "gui/entry/EntryView.h" #include "gui/group/GroupView.h" +#ifdef Q_OS_MACOS +#include "gui/macutils/MacUtils.h" +#endif #include "gui/wizard/NewDatabaseWizard.h" DatabaseTabWidget::DatabaseTabWidget(QWidget* parent) @@ -544,8 +547,8 @@ void DatabaseTabWidget::unlockDatabaseInDialog(DatabaseWidget* dbWidget, Databas m_databaseOpenDialog->setFilePath(filePath); #ifdef Q_OS_MACOS - if (intent == DatabaseOpenDialog::Intent::AutoType) { - autoType()->raiseWindow(); + if (intent == DatabaseOpenDialog::Intent::AutoType || intent == DatabaseOpenDialog::Intent::Browser) { + macUtils()->raiseOwnWindow(); Tools::wait(500); } #endif diff --git a/src/gui/MessageBox.cpp b/src/gui/MessageBox.cpp index 1460a6c78..e3edd2dbf 100644 --- a/src/gui/MessageBox.cpp +++ b/src/gui/MessageBox.cpp @@ -76,7 +76,8 @@ MessageBox::Button MessageBox::messageBox(QWidget* parent, const QString& title, const QString& text, MessageBox::Buttons buttons, - MessageBox::Button defaultButton) + MessageBox::Button defaultButton, + MessageBox::Action action) { if (m_nextAnswer == MessageBox::NoButton) { QMessageBox msgBox(parent); @@ -101,6 +102,11 @@ MessageBox::Button MessageBox::messageBox(QWidget* parent, } } + if (action == MessageBox::Raise) { + msgBox.setWindowFlags(Qt::WindowStaysOnTopHint); + msgBox.activateWindow(); + msgBox.raise(); + } msgBox.exec(); Button returnButton = m_addedButtonLookup[msgBox.clickedButton()]; @@ -118,36 +124,40 @@ MessageBox::Button MessageBox::critical(QWidget* parent, const QString& title, const QString& text, MessageBox::Buttons buttons, - MessageBox::Button defaultButton) + MessageBox::Button defaultButton, + MessageBox::Action action) { - return messageBox(parent, QMessageBox::Critical, title, text, buttons, defaultButton); + return messageBox(parent, QMessageBox::Critical, title, text, buttons, defaultButton, action); } MessageBox::Button MessageBox::information(QWidget* parent, const QString& title, const QString& text, MessageBox::Buttons buttons, - MessageBox::Button defaultButton) + MessageBox::Button defaultButton, + MessageBox::Action action) { - return messageBox(parent, QMessageBox::Information, title, text, buttons, defaultButton); + return messageBox(parent, QMessageBox::Information, title, text, buttons, defaultButton, action); } MessageBox::Button MessageBox::question(QWidget* parent, const QString& title, const QString& text, MessageBox::Buttons buttons, - MessageBox::Button defaultButton) + MessageBox::Button defaultButton, + MessageBox::Action action) { - return messageBox(parent, QMessageBox::Question, title, text, buttons, defaultButton); + return messageBox(parent, QMessageBox::Question, title, text, buttons, defaultButton, action); } MessageBox::Button MessageBox::warning(QWidget* parent, const QString& title, const QString& text, MessageBox::Buttons buttons, - MessageBox::Button defaultButton) + MessageBox::Button defaultButton, + MessageBox::Action action) { - return messageBox(parent, QMessageBox::Warning, title, text, buttons, defaultButton); + return messageBox(parent, QMessageBox::Warning, title, text, buttons, defaultButton, action); } void MessageBox::setNextAnswer(MessageBox::Button button) diff --git a/src/gui/MessageBox.h b/src/gui/MessageBox.h index 696b28844..816d5c865 100644 --- a/src/gui/MessageBox.h +++ b/src/gui/MessageBox.h @@ -63,6 +63,11 @@ public: Last = Merge, }; + enum Action { + None = 0, + Raise = 1, + }; + typedef uint64_t Buttons; static void initializeButtonDefs(); @@ -72,22 +77,26 @@ public: const QString& title, const QString& text, Buttons buttons = MessageBox::Ok, - Button defaultButton = MessageBox::NoButton); + Button defaultButton = MessageBox::NoButton, + Action action = MessageBox::None); static Button information(QWidget* parent, const QString& title, const QString& text, Buttons buttons = MessageBox::Ok, - Button defaultButton = MessageBox::NoButton); + Button defaultButton = MessageBox::NoButton, + Action action = MessageBox::None); static Button question(QWidget* parent, const QString& title, const QString& text, Buttons buttons = MessageBox::Ok, - Button defaultButton = MessageBox::NoButton); + Button defaultButton = MessageBox::NoButton, + Action action = MessageBox::None); static Button warning(QWidget* parent, const QString& title, const QString& text, Buttons buttons = MessageBox::Ok, - Button defaultButton = MessageBox::NoButton); + Button defaultButton = MessageBox::NoButton, + Action action = MessageBox::None); private: static Button m_nextAnswer; @@ -99,7 +108,8 @@ private: const QString& title, const QString& text, Buttons buttons = MessageBox::Ok, - Button defaultButton = MessageBox::NoButton); + Button defaultButton = MessageBox::NoButton, + Action action = MessageBox::None); static QString stdButtonText(QMessageBox::StandardButton button); diff --git a/src/autotype/mac/AppKit.h b/src/gui/macutils/AppKit.h similarity index 100% rename from src/autotype/mac/AppKit.h rename to src/gui/macutils/AppKit.h diff --git a/src/autotype/mac/AppKitImpl.h b/src/gui/macutils/AppKitImpl.h similarity index 100% rename from src/autotype/mac/AppKitImpl.h rename to src/gui/macutils/AppKitImpl.h diff --git a/src/autotype/mac/AppKitImpl.mm b/src/gui/macutils/AppKitImpl.mm similarity index 100% rename from src/autotype/mac/AppKitImpl.mm rename to src/gui/macutils/AppKitImpl.mm diff --git a/src/gui/macutils/MacUtils.cpp b/src/gui/macutils/MacUtils.cpp new file mode 100644 index 000000000..a76c561be --- /dev/null +++ b/src/gui/macutils/MacUtils.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Felix Geyer + * Copyright (C) 2018 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 . + */ + +#include "MacUtils.h" +#include + +MacUtils* MacUtils::m_instance = nullptr; + +MacUtils::MacUtils(QObject* parent) : QObject(parent) + , m_appkit(new AppKit()) +{ + +} + +MacUtils::~MacUtils() +{ + +} + +MacUtils* MacUtils::instance() +{ + if (!m_instance) { + m_instance = new MacUtils(qApp); + } + + return m_instance; +} + +WId MacUtils::activeWindow() +{ + return m_appkit->activeProcessId(); +} + +bool MacUtils::raiseWindow(WId pid) +{ + return m_appkit->activateProcess(pid); +} + +bool MacUtils::raiseOwnWindow() +{ + return m_appkit->activateProcess(m_appkit->ownProcessId()); +} + +bool MacUtils::raiseLastActiveWindow() +{ + return m_appkit->activateProcess(m_appkit->lastActiveProcessId()); +} diff --git a/src/gui/macutils/MacUtils.h b/src/gui/macutils/MacUtils.h new file mode 100644 index 000000000..6d8bb3fe2 --- /dev/null +++ b/src/gui/macutils/MacUtils.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 Felix Geyer + * Copyright (C) 2017 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 . + */ + +#ifndef KEEPASSXC_MACUTILS_H +#define KEEPASSXC_MACUTILS_H + +#include +#include +#include "AppKit.h" + +class MacUtils : public QObject +{ + Q_OBJECT + +public: + static MacUtils* instance(); + static void createTestInstance(); + + WId activeWindow(); + bool raiseWindow(WId pid); + bool raiseLastActiveWindow(); + bool raiseOwnWindow(); + +private: + explicit MacUtils(QObject* parent = nullptr); + ~MacUtils(); + +private: + std::unique_ptr m_appkit; + static MacUtils* m_instance; + void* self; + + Q_DISABLE_COPY(MacUtils) +}; + +inline MacUtils* macUtils() +{ + return MacUtils::instance(); +} + +#endif // KEEPASSXC_MACUTILS_H