diff --git a/share/linux/org.keepassxc.KeePassXC.desktop b/share/linux/org.keepassxc.KeePassXC.desktop index 8a0169800..6ee0f7411 100644 --- a/share/linux/org.keepassxc.KeePassXC.desktop +++ b/share/linux/org.keepassxc.KeePassXC.desktop @@ -10,6 +10,7 @@ Exec=keepassxc %f TryExec=keepassxc Icon=keepassxc StartupWMClass=keepassxc +StartupNotify=true Terminal=false Type=Application Version=1.0 diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 0ccd31062..834d76aca 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -55,6 +55,7 @@ BrowserService::BrowserService(DatabaseTabWidget* parent) , m_dialogActive(false) , m_bringToFrontRequested(false) , m_wasMinimized(false) + , m_wasHidden(false) , m_keepassBrowserUUID(QUuid::fromRfc4122(QByteArray::fromHex("de887cc3036343b8974b5911b8816224"))) { // Don't connect the signals when used from DatabaseSettingsWidgetBrowser (parent is nullptr) @@ -96,7 +97,6 @@ bool BrowserService::openDatabase(bool triggerUnlock) if (triggerUnlock) { m_bringToFrontRequested = true; - m_wasMinimized = getMainWindow()->isMinimized(); raiseWindow(true); } @@ -933,7 +933,11 @@ void BrowserService::hideWindow() const getMainWindow()->showMinimized(); } else { #ifdef Q_OS_MACOS - macUtils()->raiseLastActiveWindow(); + if (m_wasHidden) { + macUtils()->hideOwnWindow(); + } else { + macUtils()->raiseLastActiveWindow(); + } #else getMainWindow()->lower(); #endif @@ -944,6 +948,7 @@ void BrowserService::raiseWindow(const bool force) { m_wasMinimized = getMainWindow()->isMinimized(); #ifdef Q_OS_MACOS + m_wasHidden = macUtils()->isHidden(); macUtils()->raiseOwnWindow(); Tools::wait(500); #else diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 74febe4f0..8e1c45f4a 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -125,6 +125,7 @@ private: bool m_dialogActive; bool m_bringToFrontRequested; bool m_wasMinimized; + bool m_wasHidden; QUuid m_keepassBrowserUUID; }; diff --git a/src/cli/Add.cpp b/src/cli/Add.cpp index 3eb3b1bfc..dd9d0b50c 100644 --- a/src/cli/Add.cpp +++ b/src/cli/Add.cpp @@ -83,7 +83,7 @@ int Add::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli add"); + errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli add"); return EXIT_FAILURE; } diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 4c52e6b7e..e59a911a5 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -83,6 +83,13 @@ if(APPLE AND WITH_APP_BUNDLE) "@executable_path/../Frameworks/libykpers-1.1.dylib" keepassxc-cli COMMENT "Changing linking of keepassxc-cli") + + # Copy app to staging directory for pre-install testing + set(CLI_APP_DIR "${CMAKE_BINARY_DIR}/src/${CLI_INSTALL_DIR}") + add_custom_command(TARGET keepassxc-cli + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy keepassxc-cli ${CLI_APP_DIR}/keepassxc-cli + COMMENT "Copying keepassxc-cli inside the application") endif() if(APPLE OR UNIX) diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp index 0cec5b3ce..25aea06e1 100644 --- a/src/cli/Clip.cpp +++ b/src/cli/Clip.cpp @@ -42,7 +42,7 @@ Clip::~Clip() int Clip::execute(const QStringList& arguments) { - TextStream out(Utils::STDOUT); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -62,7 +62,7 @@ int Clip::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2 && args.size() != 3) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli clip"); + errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli clip"); return EXIT_FAILURE; } @@ -83,11 +83,11 @@ int Clip::clipEntry(QSharedPointer database, bool clipTotp, bool silent) { - TextStream err(Utils::STDERR); + TextStream errorTextStream(Utils::STDERR); int timeoutSeconds = 0; if (!timeout.isEmpty() && !timeout.toInt()) { - err << QObject::tr("Invalid timeout value %1.").arg(timeout) << endl; + errorTextStream << QObject::tr("Invalid timeout value %1.").arg(timeout) << endl; return EXIT_FAILURE; } else if (!timeout.isEmpty()) { timeoutSeconds = timeout.toInt(); @@ -96,14 +96,14 @@ int Clip::clipEntry(QSharedPointer database, TextStream outputTextStream(silent ? Utils::DEVNULL : Utils::STDOUT, QIODevice::WriteOnly); Entry* entry = database->rootGroup()->findEntryByPath(entryPath); if (!entry) { - err << QObject::tr("Entry %1 not found.").arg(entryPath) << endl; + errorTextStream << QObject::tr("Entry %1 not found.").arg(entryPath) << endl; return EXIT_FAILURE; } QString value; if (clipTotp) { if (!entry->hasTotp()) { - err << QObject::tr("Entry with path %1 has no TOTP set up.").arg(entryPath) << endl; + errorTextStream << QObject::tr("Entry with path %1 has no TOTP set up.").arg(entryPath) << endl; return EXIT_FAILURE; } diff --git a/src/cli/Diceware.cpp b/src/cli/Diceware.cpp index be1ac084f..f11347344 100644 --- a/src/cli/Diceware.cpp +++ b/src/cli/Diceware.cpp @@ -38,8 +38,8 @@ Diceware::~Diceware() int Diceware::execute(const QStringList& arguments) { - TextStream in(Utils::STDIN, QIODevice::ReadOnly); - TextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -58,7 +58,7 @@ int Diceware::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (!args.isEmpty()) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware"); + errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware"); return EXIT_FAILURE; } @@ -78,12 +78,12 @@ int Diceware::execute(const QStringList& arguments) } if (!dicewareGenerator.isValid()) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware"); + outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware"); return EXIT_FAILURE; } QString password = dicewareGenerator.generatePassphrase(); - out << password << endl; + outputTextStream << password << endl; return EXIT_SUCCESS; } diff --git a/src/cli/Edit.cpp b/src/cli/Edit.cpp index 48ea5d739..7954648ce 100644 --- a/src/cli/Edit.cpp +++ b/src/cli/Edit.cpp @@ -41,9 +41,8 @@ Edit::~Edit() int Edit::execute(const QStringList& arguments) { - TextStream in(Utils::STDIN, QIODevice::ReadOnly); - TextStream out(Utils::STDOUT, QIODevice::WriteOnly); - TextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -88,7 +87,7 @@ int Edit::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli edit"); + errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli edit"); return EXIT_FAILURE; } @@ -105,19 +104,19 @@ int Edit::execute(const QStringList& arguments) QString passwordLength = parser.value(length); if (!passwordLength.isEmpty() && !passwordLength.toInt()) { - err << QObject::tr("Invalid value for password length: %1").arg(passwordLength) << endl; + errorTextStream << QObject::tr("Invalid value for password length: %1").arg(passwordLength) << endl; return EXIT_FAILURE; } Entry* entry = db->rootGroup()->findEntryByPath(entryPath); if (!entry) { - err << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl; + errorTextStream << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl; return EXIT_FAILURE; } if (parser.value("username").isEmpty() && parser.value("url").isEmpty() && parser.value("title").isEmpty() && !parser.isSet(prompt) && !parser.isSet(generate)) { - err << QObject::tr("Not changing any field for entry %1.").arg(entryPath) << endl; + errorTextStream << QObject::tr("Not changing any field for entry %1.").arg(entryPath) << endl; return EXIT_FAILURE; } @@ -137,7 +136,7 @@ int Edit::execute(const QStringList& arguments) if (parser.isSet(prompt)) { if (!parser.isSet(Command::QuietOption)) { - out << QObject::tr("Enter new password for entry: ") << flush; + outputTextStream << QObject::tr("Enter new password for entry: ") << flush; } QString password = Utils::getPassword(parser.isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT); entry->setPassword(password); @@ -160,12 +159,12 @@ int Edit::execute(const QStringList& arguments) QString errorMessage; if (!db->save(databasePath, &errorMessage, true, false)) { - err << QObject::tr("Writing the database failed: %1").arg(errorMessage) << endl; + errorTextStream << QObject::tr("Writing the database failed: %1").arg(errorMessage) << endl; return EXIT_FAILURE; } if (!parser.isSet(Command::QuietOption)) { - out << QObject::tr("Successfully edited entry %1.").arg(entry->title()) << endl; + outputTextStream << QObject::tr("Successfully edited entry %1.").arg(entry->title()) << endl; } return EXIT_SUCCESS; } diff --git a/src/cli/Estimate.cpp b/src/cli/Estimate.cpp index 556ff4435..7064963f4 100644 --- a/src/cli/Estimate.cpp +++ b/src/cli/Estimate.cpp @@ -156,8 +156,8 @@ static void estimate(const char* pwd, bool advanced) int Estimate::execute(const QStringList& arguments) { - TextStream in(Utils::STDIN, QIODevice::ReadOnly); - TextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream inputTextStream(Utils::STDIN, QIODevice::ReadOnly); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -171,7 +171,7 @@ int Estimate::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() > 1) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli estimate"); + errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli estimate"); return EXIT_FAILURE; } @@ -179,7 +179,7 @@ int Estimate::execute(const QStringList& arguments) if (args.size() == 1) { password = args.at(0); } else { - password = in.readLine(); + password = inputTextStream.readLine(); } estimate(password.toLatin1(), parser.isSet(advancedOption)); diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index 0b20b9528..d7099cd52 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -43,8 +43,8 @@ Extract::~Extract() int Extract::execute(const QStringList& arguments) { - TextStream out(Utils::STDOUT, QIODevice::WriteOnly); - TextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -56,12 +56,12 @@ int Extract::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 1) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli extract"); + errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli extract"); return EXIT_FAILURE; } if (!parser.isSet(Command::QuietOption)) { - out << QObject::tr("Insert password to unlock %1: ").arg(args.at(0)) << flush; + outputTextStream << QObject::tr("Insert password to unlock %1: ").arg(args.at(0)) << flush; } auto compositeKey = QSharedPointer::create(); @@ -77,12 +77,12 @@ int Extract::execute(const QStringList& arguments) auto fileKey = QSharedPointer::create(); QString errorMsg; if (!fileKey->load(keyFilePath, &errorMsg)) { - err << QObject::tr("Failed to load key file %1: %2").arg(keyFilePath, errorMsg) << endl; + errorTextStream << QObject::tr("Failed to load key file %1: %2").arg(keyFilePath, errorMsg) << endl; return EXIT_FAILURE; } if (fileKey->type() != FileKey::Hashed) { - err << QObject::tr("WARNING: You are using a legacy key file format which may become\n" + errorTextStream << QObject::tr("WARNING: You are using a legacy key file format which may become\n" "unsupported in the future.\n\n" "Please consider generating a new key file.") << endl; @@ -95,11 +95,11 @@ int Extract::execute(const QStringList& arguments) const QString& databaseFilename = args.at(0); QFile dbFile(databaseFilename); if (!dbFile.exists()) { - err << QObject::tr("File %1 does not exist.").arg(databaseFilename) << endl; + errorTextStream << QObject::tr("File %1 does not exist.").arg(databaseFilename) << endl; return EXIT_FAILURE; } if (!dbFile.open(QIODevice::ReadOnly)) { - err << QObject::tr("Unable to open file %1.").arg(databaseFilename) << endl; + errorTextStream << QObject::tr("Unable to open file %1.").arg(databaseFilename) << endl; return EXIT_FAILURE; } @@ -112,14 +112,14 @@ int Extract::execute(const QStringList& arguments) if (reader.hasError()) { if (xmlData.isEmpty()) { - err << QObject::tr("Error while reading the database:\n%1").arg(reader.errorString()) << endl; + errorTextStream << QObject::tr("Error while reading the database:\n%1").arg(reader.errorString()) << endl; } else { - err << QObject::tr("Error while parsing the database:\n%1").arg(reader.errorString()) << endl; + errorTextStream << QObject::tr("Error while parsing the database:\n%1").arg(reader.errorString()) << endl; } return EXIT_FAILURE; } - out << xmlData.constData() << endl; + outputTextStream << xmlData.constData() << endl; return EXIT_SUCCESS; } diff --git a/src/cli/Generate.cpp b/src/cli/Generate.cpp index adbfc0cc0..5f0ad98ac 100644 --- a/src/cli/Generate.cpp +++ b/src/cli/Generate.cpp @@ -38,8 +38,8 @@ Generate::~Generate() int Generate::execute(const QStringList& arguments) { - TextStream in(Utils::STDIN, QIODevice::ReadOnly); - TextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -84,7 +84,7 @@ int Generate::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (!args.isEmpty()) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate"); + errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate"); return EXIT_FAILURE; } @@ -128,12 +128,12 @@ int Generate::execute(const QStringList& arguments) passwordGenerator.setExcludedChars(parser.value(exclude)); if (!passwordGenerator.isValid()) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate"); + errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate"); return EXIT_FAILURE; } QString password = passwordGenerator.generatePassword(); - out << password << endl; + outputTextStream << password << endl; return EXIT_SUCCESS; } diff --git a/src/cli/List.cpp b/src/cli/List.cpp index 9c4f733ef..11650d405 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -40,7 +40,7 @@ List::~List() int List::execute(const QStringList& arguments) { - TextStream out(Utils::STDOUT); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -58,7 +58,7 @@ int List::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 1 && args.size() != 2) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli ls"); + errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli ls"); return EXIT_FAILURE; } @@ -80,20 +80,20 @@ int List::execute(const QStringList& arguments) int List::listGroup(Database* database, bool recursive, const QString& groupPath) { - TextStream out(Utils::STDOUT, QIODevice::WriteOnly); - TextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); if (groupPath.isEmpty()) { - out << database->rootGroup()->print(recursive) << flush; + outputTextStream << database->rootGroup()->print(recursive) << flush; return EXIT_SUCCESS; } Group* group = database->rootGroup()->findGroupByPath(groupPath); if (!group) { - err << QObject::tr("Cannot find group %1.").arg(groupPath) << endl; + errorTextStream << QObject::tr("Cannot find group %1.").arg(groupPath) << endl; return EXIT_FAILURE; } - out << group->print(recursive) << flush; + outputTextStream << group->print(recursive) << flush; return EXIT_SUCCESS; } diff --git a/src/cli/Locate.cpp b/src/cli/Locate.cpp index e348df117..f25ce79af 100644 --- a/src/cli/Locate.cpp +++ b/src/cli/Locate.cpp @@ -42,7 +42,7 @@ Locate::~Locate() int Locate::execute(const QStringList& arguments) { - TextStream out(Utils::STDOUT, QIODevice::WriteOnly); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -55,7 +55,7 @@ int Locate::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli locate"); + errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli locate"); return EXIT_FAILURE; } @@ -72,17 +72,17 @@ int Locate::execute(const QStringList& arguments) int Locate::locateEntry(Database* database, const QString& searchTerm) { - TextStream out(Utils::STDOUT, QIODevice::WriteOnly); - TextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QStringList results = database->rootGroup()->locate(searchTerm); if (results.isEmpty()) { - err << "No results for that search term." << endl; + errorTextStream << "No results for that search term." << endl; return EXIT_FAILURE; } for (const QString& result : asConst(results)) { - out << result << endl; + outputTextStream << result << endl; } return EXIT_SUCCESS; } diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index c4759fe40..39dcf21fa 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -38,8 +38,8 @@ Merge::~Merge() int Merge::execute(const QStringList& arguments) { - TextStream out(Utils::STDOUT, QIODevice::WriteOnly); - TextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -64,7 +64,7 @@ int Merge::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli merge"); + errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli merge"); return EXIT_FAILURE; } @@ -83,7 +83,7 @@ int Merge::execute(const QStringList& arguments) db2 = QSharedPointer::create(); QString errorMessage; if (!db2->open(args.at(1), db1->key(), &errorMessage, false)) { - err << QObject::tr("Error reading merge file:\n%1").arg(errorMessage); + errorTextStream << QObject::tr("Error reading merge file:\n%1").arg(errorMessage); return EXIT_FAILURE; } } @@ -94,14 +94,14 @@ int Merge::execute(const QStringList& arguments) if (databaseChanged) { QString errorMessage; if (!db1->save(args.at(0), &errorMessage, true, false)) { - err << QObject::tr("Unable to save database to file : %1").arg(errorMessage) << endl; + errorTextStream << QObject::tr("Unable to save database to file : %1").arg(errorMessage) << endl; return EXIT_FAILURE; } if (!parser.isSet(Command::QuietOption)) { - out << "Successfully merged the database files." << endl; + outputTextStream << "Successfully merged the database files." << endl; } } else if (!parser.isSet(Command::QuietOption)) { - out << "Database was not modified by merge operation." << endl; + outputTextStream << "Database was not modified by merge operation." << endl; } return EXIT_SUCCESS; diff --git a/src/cli/Remove.cpp b/src/cli/Remove.cpp index cd6275b77..a1cda2e2d 100644 --- a/src/cli/Remove.cpp +++ b/src/cli/Remove.cpp @@ -44,7 +44,7 @@ Remove::~Remove() int Remove::execute(const QStringList& arguments) { - TextStream out(Utils::STDERR, QIODevice::WriteOnly); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::tr("main", "Remove an entry from the database.")); @@ -57,7 +57,7 @@ int Remove::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli rm"); + errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli rm"); return EXIT_FAILURE; } @@ -74,12 +74,12 @@ int Remove::execute(const QStringList& arguments) int Remove::removeEntry(Database* database, const QString& databasePath, const QString& entryPath, bool quiet) { - TextStream out(quiet ? Utils::DEVNULL : Utils::STDOUT, QIODevice::WriteOnly); - TextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream outputTextStream(quiet ? Utils::DEVNULL : Utils::STDOUT, QIODevice::WriteOnly); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QPointer entry = database->rootGroup()->findEntryByPath(entryPath); if (!entry) { - err << QObject::tr("Entry %1 not found.").arg(entryPath) << endl; + errorTextStream << QObject::tr("Entry %1 not found.").arg(entryPath) << endl; return EXIT_FAILURE; } @@ -95,14 +95,14 @@ int Remove::removeEntry(Database* database, const QString& databasePath, const Q QString errorMessage; if (!database->save(databasePath, &errorMessage, true, false)) { - err << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl; + errorTextStream << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl; return EXIT_FAILURE; } if (recycled) { - out << QObject::tr("Successfully recycled entry %1.").arg(entryTitle) << endl; + outputTextStream << QObject::tr("Successfully recycled entry %1.").arg(entryTitle) << endl; } else { - out << QObject::tr("Successfully deleted entry %1.").arg(entryTitle) << endl; + outputTextStream << QObject::tr("Successfully deleted entry %1.").arg(entryTitle) << endl; } return EXIT_SUCCESS; diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index d8cf5a4fe..9ae3f4d0f 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -41,7 +41,7 @@ Show::~Show() int Show::execute(const QStringList& arguments) { - TextStream out(Utils::STDOUT); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); QCommandLineParser parser; parser.setApplicationDescription(description); @@ -67,7 +67,7 @@ int Show::execute(const QStringList& arguments) const QStringList args = parser.positionalArguments(); if (args.size() != 2) { - out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli show"); + errorTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli show"); return EXIT_FAILURE; } @@ -84,18 +84,17 @@ int Show::execute(const QStringList& arguments) int Show::showEntry(Database* database, QStringList attributes, bool showTotp, const QString& entryPath) { - TextStream in(Utils::STDIN, QIODevice::ReadOnly); - TextStream out(Utils::STDOUT, QIODevice::WriteOnly); - TextStream err(Utils::STDERR, QIODevice::WriteOnly); + TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly); + TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly); Entry* entry = database->rootGroup()->findEntryByPath(entryPath); if (!entry) { - err << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl; + errorTextStream << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl; return EXIT_FAILURE; } if (showTotp && !entry->hasTotp()) { - err << QObject::tr("Entry with path %1 has no TOTP set up.").arg(entryPath) << endl; + errorTextStream << QObject::tr("Entry with path %1 has no TOTP set up.").arg(entryPath) << endl; return EXIT_FAILURE; } @@ -110,20 +109,20 @@ int Show::showEntry(Database* database, QStringList attributes, bool showTotp, c for (const QString& attribute : asConst(attributes)) { if (!entry->attributes()->contains(attribute)) { sawUnknownAttribute = true; - err << QObject::tr("ERROR: unknown attribute %1.").arg(attribute) << endl; + errorTextStream << QObject::tr("ERROR: unknown attribute %1.").arg(attribute) << endl; continue; } if (showAttributeNames) { - out << attribute << ": "; + outputTextStream << attribute << ": "; } - out << entry->resolveMultiplePlaceholders(entry->attributes()->value(attribute)) << endl; + outputTextStream << entry->resolveMultiplePlaceholders(entry->attributes()->value(attribute)) << endl; } if (showTotp) { if (showAttributeNames) { - out << "TOTP: "; + outputTextStream << "TOTP: "; } - out << entry->totp() << endl; + outputTextStream << entry->totp() << endl; } return sawUnknownAttribute ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 735f1ece4..8d4d3c01a 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -196,6 +196,8 @@ void Config::init(const QString& fileName) m_defaults.insert("security/resettouchidscreenlock", true); m_defaults.insert("GUI/Language", "system"); m_defaults.insert("GUI/HideToolbar", false); + m_defaults.insert("GUI/MovableToolbar", false); + m_defaults.insert("GUI/ToolButtonStyle", Qt::ToolButtonIconOnly); m_defaults.insert("GUI/ShowTrayIcon", false); m_defaults.insert("GUI/DarkTrayIcon", false); m_defaults.insert("GUI/MinimizeToTray", false); diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp index 1b4641b21..d69e3a5ce 100644 --- a/src/gui/ApplicationSettingsWidget.cpp +++ b/src/gui/ApplicationSettingsWidget.cpp @@ -81,6 +81,7 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent) // clang-format off connect(m_generalUi->autoSaveAfterEveryChangeCheckBox, SIGNAL(toggled(bool)), SLOT(enableAutoSaveOnExit(bool))); connect(m_generalUi->systrayShowCheckBox, SIGNAL(toggled(bool)), this, SLOT(enableSystray(bool))); + connect(m_generalUi->toolbarHideCheckBox, SIGNAL(toggled(bool)), this, SLOT(enableToolbarSettings(bool))); connect(m_secUi->clearClipboardCheckBox, SIGNAL(toggled(bool)), m_secUi->clearClipboardSpinBox, SLOT(setEnabled(bool))); @@ -155,6 +156,19 @@ void ApplicationSettingsWidget::loadSettings() m_generalUi->previewHideCheckBox->setChecked(config()->get("GUI/HidePreviewPanel").toBool()); m_generalUi->toolbarHideCheckBox->setChecked(config()->get("GUI/HideToolbar").toBool()); + m_generalUi->toolbarMovableCheckBox->setChecked(config()->get("GUI/MovableToolbar").toBool()); + + m_generalUi->toolButtonStyleComboBox->clear(); + m_generalUi->toolButtonStyleComboBox->addItem(tr("Icon only"), Qt::ToolButtonIconOnly); + m_generalUi->toolButtonStyleComboBox->addItem(tr("Text only"), Qt::ToolButtonTextOnly); + m_generalUi->toolButtonStyleComboBox->addItem(tr("Text beside icon"), Qt::ToolButtonTextBesideIcon); + m_generalUi->toolButtonStyleComboBox->addItem(tr("Text under icon"), Qt::ToolButtonTextUnderIcon); + m_generalUi->toolButtonStyleComboBox->addItem(tr("Follow style"), Qt::ToolButtonFollowStyle); + int toolButtonStyleIndex = m_generalUi->toolButtonStyleComboBox->findData(config()->get("GUI/ToolButtonStyle")); + if (toolButtonStyleIndex > 0) { + m_generalUi->toolButtonStyleComboBox->setCurrentIndex(toolButtonStyleIndex); + } + m_generalUi->systrayShowCheckBox->setChecked(config()->get("GUI/ShowTrayIcon").toBool()); m_generalUi->systrayDarkIconCheckBox->setChecked(config()->get("GUI/DarkTrayIcon").toBool()); m_generalUi->systrayMinimizeToTrayCheckBox->setChecked(config()->get("GUI/MinimizeToTray").toBool()); @@ -231,6 +245,11 @@ void ApplicationSettingsWidget::saveSettings() config()->set("GUI/HidePreviewPanel", m_generalUi->previewHideCheckBox->isChecked()); config()->set("GUI/HideToolbar", m_generalUi->toolbarHideCheckBox->isChecked()); + config()->set("GUI/MovableToolbar", m_generalUi->toolbarMovableCheckBox->isChecked()); + + int currentToolButtonStyleIndex = m_generalUi->toolButtonStyleComboBox->currentIndex(); + config()->set("GUI/ToolButtonStyle", m_generalUi->toolButtonStyleComboBox->itemData(currentToolButtonStyleIndex).toString()); + config()->set("GUI/ShowTrayIcon", m_generalUi->systrayShowCheckBox->isChecked()); config()->set("GUI/DarkTrayIcon", m_generalUi->systrayDarkIconCheckBox->isChecked()); config()->set("GUI/MinimizeToTray", m_generalUi->systrayMinimizeToTrayCheckBox->isChecked()); @@ -299,3 +318,11 @@ void ApplicationSettingsWidget::enableSystray(bool checked) m_generalUi->systrayDarkIconCheckBox->setEnabled(checked); m_generalUi->systrayMinimizeToTrayCheckBox->setEnabled(checked); } + +void ApplicationSettingsWidget::enableToolbarSettings(bool checked) +{ + m_generalUi->toolbarMovableCheckBox->setEnabled(!checked); + m_generalUi->toolButtonStyleComboBox->setEnabled(!checked); + m_generalUi->toolButtonStyleLabel->setEnabled(!checked); +} + diff --git a/src/gui/ApplicationSettingsWidget.h b/src/gui/ApplicationSettingsWidget.h index f596b52c5..fb6208707 100644 --- a/src/gui/ApplicationSettingsWidget.h +++ b/src/gui/ApplicationSettingsWidget.h @@ -55,6 +55,7 @@ private slots: void reject(); void enableAutoSaveOnExit(bool checked); void enableSystray(bool checked); + void enableToolbarSettings(bool checked); private: QWidget* const m_secWidget; diff --git a/src/gui/ApplicationSettingsWidgetGeneral.ui b/src/gui/ApplicationSettingsWidgetGeneral.ui index f8e304cca..4826e09bf 100644 --- a/src/gui/ApplicationSettingsWidgetGeneral.ui +++ b/src/gui/ApplicationSettingsWidgetGeneral.ui @@ -7,7 +7,7 @@ 0 0 684 - 842 + 881 @@ -186,6 +186,100 @@ + + + + 0 + + + QLayout::SetMaximumSize + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + true + + + + 0 + 0 + + + + Movable toolbar + + + + + + + + + 15 + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + true + + + + 0 + 0 + + + + Button style + + + + + + + true + + + + 0 + 0 + + + + + + diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 2ecaa31ce..7147808d7 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -151,7 +151,7 @@ MainWindow::MainWindow() m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size(); restoreGeometry(config()->get("GUI/MainWindowGeometry").toByteArray()); - + restoreState(config()->get("GUI/MainWindowState").toByteArray()); #ifdef WITH_XC_BROWSER m_ui->settingsWidget->addSettingsPage(new BrowserPlugin(m_ui->tabWidget)); #endif @@ -842,6 +842,7 @@ void MainWindow::saveWindowInformation() { if (isVisible()) { config()->set("GUI/MainWindowGeometry", saveGeometry()); + config()->set("GUI/MainWindowState", saveState()); } } @@ -964,6 +965,13 @@ void MainWindow::applySettingsChanges() #endif m_ui->toolBar->setHidden(config()->get("GUI/HideToolbar").toBool()); + m_ui->toolBar->setMovable(config()->get("GUI/MovableToolbar").toBool()); + + bool isOk = false; + const auto toolButtonStyle = static_cast(config()->get("GUI/ToolButtonStyle").toInt(&isOk)); + if (isOk) { + m_ui->toolBar->setToolButtonStyle(toolButtonStyle); + } updateTrayIcon(); } diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 28bc2cc4e..828784675 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -1002,7 +1002,9 @@ bool EditEntryWidget::hasBeenModified() const void EditEntryWidget::togglePasswordGeneratorButton(bool checked) { - m_mainUi->passwordGenerator->regeneratePassword(); + if (checked) { + m_mainUi->passwordGenerator->regeneratePassword(); + } m_mainUi->passwordGenerator->setVisible(checked); } diff --git a/src/gui/macutils/AppKit.h b/src/gui/macutils/AppKit.h index f1eced5bb..ecd11c421 100644 --- a/src/gui/macutils/AppKit.h +++ b/src/gui/macutils/AppKit.h @@ -33,6 +33,8 @@ public: pid_t activeProcessId(); pid_t ownProcessId(); bool activateProcess(pid_t pid); + bool hideProcess(pid_t pid); + bool isHidden(pid_t pid); private: void *self; diff --git a/src/gui/macutils/AppKitImpl.h b/src/gui/macutils/AppKitImpl.h index f370096fc..97673b071 100644 --- a/src/gui/macutils/AppKitImpl.h +++ b/src/gui/macutils/AppKitImpl.h @@ -28,5 +28,7 @@ - (pid_t) activeProcessId; - (pid_t) ownProcessId; - (bool) activateProcess:(pid_t) pid; +- (bool) hideProcess:(pid_t) pid; +- (bool) isHidden:(pid_t) pid; @end diff --git a/src/gui/macutils/AppKitImpl.mm b/src/gui/macutils/AppKitImpl.mm index 457044389..ca4e9f10e 100644 --- a/src/gui/macutils/AppKitImpl.mm +++ b/src/gui/macutils/AppKitImpl.mm @@ -72,10 +72,27 @@ AppKit::~AppKit() - (bool) activateProcess:(pid_t) pid { NSRunningApplication *app = [NSRunningApplication runningApplicationWithProcessIdentifier:pid]; - return [app activateWithOptions:NSApplicationActivateIgnoringOtherApps]; } +// +// Hide application by process id +// +- (bool) hideProcess:(pid_t) pid +{ + NSRunningApplication *app = [NSRunningApplication runningApplicationWithProcessIdentifier:pid]; + return [app hide]; +} + +// +// Get application hidden state by process id +// +- (bool) isHidden:(pid_t) pid +{ + NSRunningApplication *app = [NSRunningApplication runningApplicationWithProcessIdentifier:pid]; + return [app isHidden]; +} + // // ------------------------- C++ Trampolines ------------------------- // @@ -100,4 +117,14 @@ bool AppKit::activateProcess(pid_t pid) return [static_cast(self) activateProcess:pid]; } +bool AppKit::hideProcess(pid_t pid) +{ + return [static_cast(self) hideProcess:pid]; +} + +bool AppKit::isHidden(pid_t pid) +{ + return [static_cast(self) isHidden:pid]; +} + @end diff --git a/src/gui/macutils/MacUtils.cpp b/src/gui/macutils/MacUtils.cpp index a76c561be..602c1958f 100644 --- a/src/gui/macutils/MacUtils.cpp +++ b/src/gui/macutils/MacUtils.cpp @@ -60,3 +60,13 @@ bool MacUtils::raiseLastActiveWindow() { return m_appkit->activateProcess(m_appkit->lastActiveProcessId()); } + +bool MacUtils::hideOwnWindow() +{ + return m_appkit->hideProcess(m_appkit->ownProcessId()); +} + +bool MacUtils::isHidden() +{ + return m_appkit->isHidden(m_appkit->ownProcessId()); +} diff --git a/src/gui/macutils/MacUtils.h b/src/gui/macutils/MacUtils.h index 6d8bb3fe2..67c53dd22 100644 --- a/src/gui/macutils/MacUtils.h +++ b/src/gui/macutils/MacUtils.h @@ -35,6 +35,8 @@ public: bool raiseWindow(WId pid); bool raiseLastActiveWindow(); bool raiseOwnWindow(); + bool hideOwnWindow(); + bool isHidden(); private: explicit MacUtils(QObject* parent = nullptr); diff --git a/src/proxy/CMakeLists.txt b/src/proxy/CMakeLists.txt index 6839c9a22..ff645dadb 100755 --- a/src/proxy/CMakeLists.txt +++ b/src/proxy/CMakeLists.txt @@ -45,6 +45,13 @@ if(WITH_XC_BROWSER) "@executable_path/../Frameworks/QtNetwork.framework/Versions/5/QtNetwork" keepassxc-proxy COMMENT "Changing linking of keepassxc-proxy") + + # Copy app to staging directory for pre-install testing + set(PROXY_APP_DIR "${CMAKE_BINARY_DIR}/src/${PROXY_INSTALL_DIR}") + add_custom_command(TARGET keepassxc-proxy + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy keepassxc-proxy ${PROXY_APP_DIR}/keepassxc-proxy + COMMENT "Copying keepassxc-proxy inside the application") endif() if(MINGW) target_link_libraries(keepassxc-proxy Wtsapi32.lib Ws2_32.lib)