diff --git a/.gitignore b/.gitignore index 61dfb90d9..c96688e9f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ release*/ *.swp .DS_Store +.version \ No newline at end of file diff --git a/AppImage-Recipe.sh b/AppImage-Recipe.sh index 2187fde1f..96e7b4099 100755 --- a/AppImage-Recipe.sh +++ b/AppImage-Recipe.sh @@ -66,14 +66,18 @@ cp "$QXCB_PLUGIN" ".${QT_PLUGIN_PATH}/platforms/" get_apprun copy_deps + +# protect our libgpg-error from being deleted +mv ./opt/gpg-error-127/lib/x86_64-linux-gnu/libgpg-error.so.0 ./protected.so delete_blacklisted +mv ./protected.so ./opt/gpg-error-127/lib/x86_64-linux-gnu/libgpg-error.so.0 get_desktop get_icon cat << EOF > ./usr/bin/keepassxc_env #!/usr/bin/env bash -export LD_LIBRARY_PATH="/opt/libgcrypt20-18/lib/x86_64-linux-gnu:\${LD_LIBRARY_PATH}" -export LD_LIBRARY_PATH="/opt/gpg-error-127/lib/x86_64-linux-gnu:\${LD_LIBRARY_PATH}" +export LD_LIBRARY_PATH="../opt/libgcrypt20-18/lib/x86_64-linux-gnu:\${LD_LIBRARY_PATH}" +export LD_LIBRARY_PATH="../opt/gpg-error-127/lib/x86_64-linux-gnu:\${LD_LIBRARY_PATH}" export LD_LIBRARY_PATH="..$(dirname ${QT_PLUGIN_PATH})/lib:\${LD_LIBRARY_PATH}" export QT_PLUGIN_PATH="..${QT_PLUGIN_PATH}:\${KPXC_QT_PLUGIN_PATH}" @@ -84,6 +88,11 @@ unset XDG_DATA_DIRS if [ "\${1}" == "cli" ]; then shift exec keepassxc-cli "\$@" +elif [ "\${1}" == "proxy" ]; then + shift + exec keepassxc-proxy "\$@" +elif [ -v CHROME_WRAPPER ] || [ -v MOZ_LAUNCHED_CHILD ]; then + exec keepassxc-proxy "\$@" else exec keepassxc "\$@" fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fb00a023..7a1b0245a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,19 +80,22 @@ set_property(CACHE KEEPASSXC_BUILD_TYPE PROPERTY STRINGS Snapshot Release PreRel execute_process(COMMAND git tag --points-at HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE GIT_TAG) -if(GIT_TAG) - string(REGEX REPLACE "\r?\n$" "" GIT_TAG "${GIT_TAG}") +if(NOT GIT_TAG AND EXISTS ${CMAKE_SOURCE_DIR}/.version) + file(READ ${CMAKE_SOURCE_DIR}/.version OVERRIDE_VERSION) +endif() - if(GIT_TAG MATCHES "^[\\.0-9]+-(alpha|beta)[0-9]+$") +string(REGEX REPLACE "(\r?\n)+" "" OVERRIDE_VERSION "${OVERRIDE_VERSION}") +if(OVERRIDE_VERSION) + if(OVERRIDE_VERSION MATCHES "^[\\.0-9]+-(alpha|beta)[0-9]+$") set(KEEPASSXC_BUILD_TYPE PreRelease) - set(KEEPASSXC_VERSION ${GIT_TAG}) - elseif(GIT_TAG MATCHES "^[\\.0-9]+$") + set(KEEPASSXC_VERSION ${OVERRIDE_VERSION}) + elseif(OVERRIDE_VERSION MATCHES "^[\\.0-9]+$") set(KEEPASSXC_BUILD_TYPE Release) - set(KEEPASSXC_VERSION ${GIT_TAG}) + set(KEEPASSXC_VERSION ${OVERRIDE_VERSION}) endif() endif() -if(KEEPASSXC_BUILD_TYPE STREQUAL "PreRelease" AND NOT GIT_TAG) +if(KEEPASSXC_BUILD_TYPE STREQUAL "PreRelease" AND NOT OVERRIDE_VERSION) set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION}-preview") elseif(KEEPASSXC_BUILD_TYPE STREQUAL "Snapshot") set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION}-snapshot") @@ -295,6 +298,8 @@ if(Qt5Core_VERSION VERSION_LESS "5.2.0") message(FATAL_ERROR "Qt version 5.2.0 or higher is required") endif() +get_filename_component(Qt5_PREFIX ${Qt5_DIR}/../../.. REALPATH) + set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) diff --git a/Dockerfile b/Dockerfile index 5bcef9ab9..31d8cf6ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ FROM ubuntu:14.04 -ENV REBUILD_COUNTER=5 +ENV REBUILD_COUNTER=6 ENV QT5_VERSION=59 ENV QT5_PPA_VERSION=${QT5_VERSION}4 diff --git a/release-tool b/release-tool index 0df84e34d..c0739a0b4 100755 --- a/release-tool +++ b/release-tool @@ -614,12 +614,24 @@ build() { if ${BUILD_SOURCE_TARBALL}; then logInfo "Creating source tarball..." local app_name_lower="$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]')" - TARBALL_NAME="${app_name_lower}-${RELEASE_NAME}-src.tar.xz" - git archive --format=tar "$TAG_NAME" --prefix="${app_name_lower}-${RELEASE_NAME}/" \ - | xz -6 > "${OUTPUT_DIR}/${TARBALL_NAME}" + local prefix="${app_name_lower}-${RELEASE_NAME}" + local tarball_name="${prefix}-src.tar" + + git archive --format=tar "$TAG_NAME" --prefix="${prefix}/" --output="${OUTPUT_DIR}/${tarball_name}" + + if ! ${BUILD_SNAPSHOT}; then + # add .version file to tar + mkdir "${prefix}" + echo -n ${RELEASE_NAME} > "${prefix}/.version" + tar --append --file="${OUTPUT_DIR}/${tarball_name}" "${prefix}/.version" + rm "${prefix}/.version" + rmdir "${prefix}" 2> /dev/null + fi + + xz -6 "${OUTPUT_DIR}/${tarball_name}" fi - if [ -e "${OUTPUT_DIR}/build-release" ]; then + if ! ${BUILD_SNAPSHOT} && [ -e "${OUTPUT_DIR}/build-release" ]; then logInfo "Cleaning existing build directory..." rm -r "${OUTPUT_DIR}/build-release" 2> /dev/null if [ $? -ne 0 ]; then diff --git a/share/icons/application/16x16/actions/paperclip.png b/share/icons/application/16x16/actions/paperclip.png new file mode 100644 index 000000000..cd61805ef Binary files /dev/null and b/share/icons/application/16x16/actions/paperclip.png differ diff --git a/share/icons/application/22x22/actions/paperclip.png b/share/icons/application/22x22/actions/paperclip.png new file mode 100644 index 000000000..99d0eb821 Binary files /dev/null and b/share/icons/application/22x22/actions/paperclip.png differ diff --git a/share/icons/application/32x32/actions/paperclip.png b/share/icons/application/32x32/actions/paperclip.png new file mode 100644 index 000000000..cb57d1378 Binary files /dev/null and b/share/icons/application/32x32/actions/paperclip.png differ diff --git a/share/icons/svg/paperclip.svgz b/share/icons/svg/paperclip.svgz new file mode 100644 index 000000000..f82480bca Binary files /dev/null and b/share/icons/svg/paperclip.svgz differ diff --git a/share/translations/CMakeLists.txt b/share/translations/CMakeLists.txt index 78ff0bcc4..5ca739695 100644 --- a/share/translations/CMakeLists.txt +++ b/share/translations/CMakeLists.txt @@ -23,12 +23,12 @@ message(STATUS "Including translations...\n") qt5_add_translation(QM_FILES ${TRANSLATION_FILES}) if(MINGW) - file(GLOB QTBASE_TRANSLATIONS ${Qt5_DIR}/../../../share/qt5/translations/qtbase_*.qm) + file(GLOB QTBASE_TRANSLATIONS ${Qt5_PREFIX}/share/qt5/translations/qtbase_*.qm) elseif(APPLE OR KEEPASSXC_DIST_APPIMAGE) file(GLOB QTBASE_TRANSLATIONS /usr/share/qt/translations/qtbase_*.qm /usr/share/qt5/translations/qtbase_*.qm - ${Qt5_DIR}/../../../translations/qtbase_*.qm) + ${Qt5_PREFIX}/translations/qtbase_*.qm) endif() set(QM_FILES ${QM_FILES} ${QTBASE_TRANSLATIONS}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f9e562dff..10a06e0fc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -382,7 +382,7 @@ if(MINGW) include(DeployQt4) install_qt4_executable(${PROGNAME}.exe) add_custom_command(TARGET ${PROGNAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${Qt5_DIR}/../../../share/qt5/plugins/platforms/qwindows$<$:d>.dll + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${Qt5_PREFIX}/share/qt5/plugins/platforms/qwindows$<$:d>.dll $) install(FILES $/qwindows$<$:d>.dll DESTINATION "platforms") endif() diff --git a/src/browser/BrowserOptionDialog.cpp b/src/browser/BrowserOptionDialog.cpp index f6d634a82..693e62d26 100755 --- a/src/browser/BrowserOptionDialog.cpp +++ b/src/browser/BrowserOptionDialog.cpp @@ -19,6 +19,7 @@ #include "BrowserOptionDialog.h" #include "ui_BrowserOptionDialog.h" +#include "config-keepassx.h" #include "BrowserSettings.h" #include "core/FilePath.h" @@ -45,6 +46,8 @@ BrowserOptionDialog::BrowserOptionDialog(QWidget* parent) : connect(m_ui->useCustomProxy, SIGNAL(toggled(bool)), m_ui->customProxyLocation, SLOT(setEnabled(bool))); connect(m_ui->useCustomProxy, SIGNAL(toggled(bool)), m_ui->customProxyLocationBrowseButton, SLOT(setEnabled(bool))); connect(m_ui->customProxyLocationBrowseButton, SIGNAL(clicked()), this, SLOT(showProxyLocationFileDialog())); + + m_ui->browserGlobalWarningWidget->setVisible(false); } BrowserOptionDialog::~BrowserOptionDialog() @@ -84,6 +87,18 @@ void BrowserOptionDialog::loadSettings() m_ui->chromiumSupport->setChecked(settings.chromiumSupport()); m_ui->firefoxSupport->setChecked(settings.firefoxSupport()); m_ui->vivaldiSupport->setChecked(settings.vivaldiSupport()); + +#if defined(KEEPASSXC_DIST_APPIMAGE) + m_ui->supportBrowserProxy->setChecked(true); + m_ui->supportBrowserProxy->setEnabled(false); +#elif defined(KEEPASSXC_DIST_SNAP) + m_ui->enableBrowserSupport->setChecked(false); + m_ui->enableBrowserSupport->setEnabled(false); + m_ui->browserGlobalWarningWidget->showMessage( + tr("We're sorry, but KeePassXC-Browser is not supported for Snap releases at the moment."), MessageWidget::Warning); + m_ui->browserGlobalWarningWidget->setCloseButtonVisible(false); + m_ui->browserGlobalWarningWidget->setAutoHideTimeout(-1); +#endif } void BrowserOptionDialog::saveSettings() diff --git a/src/browser/BrowserOptionDialog.ui b/src/browser/BrowserOptionDialog.ui index 9bb0250b3..e82379452 100755 --- a/src/browser/BrowserOptionDialog.ui +++ b/src/browser/BrowserOptionDialog.ui @@ -26,6 +26,9 @@ 0 + + + diff --git a/src/browser/HostInstaller.cpp b/src/browser/HostInstaller.cpp index 9271da69e..9b27ab1cf 100644 --- a/src/browser/HostInstaller.cpp +++ b/src/browser/HostInstaller.cpp @@ -17,12 +17,14 @@ */ #include "HostInstaller.h" +#include "config-keepassx.h" #include #include #include #include #include #include +#include #include const QString HostInstaller::HOST_NAME = "org.keepassxc.keepassxc_browser"; @@ -161,6 +163,13 @@ QString HostInstaller::getInstallDir(SupportedBrowsers browser) const QJsonObject HostInstaller::constructFile(SupportedBrowsers browser, const bool& proxy, const QString& location) { QString path; +#ifdef KEEPASSXC_DIST_APPIMAGE + if (proxy && !location.isEmpty()) { + path = location; + } else { + path = QProcessEnvironment::systemEnvironment().value("APPIMAGE"); + } +#else if (proxy) { if (!location.isEmpty()) { path = location; @@ -178,6 +187,8 @@ QJsonObject HostInstaller::constructFile(SupportedBrowsers browser, const bool& path.replace("/","\\"); #endif +#endif // #ifdef KEEPASSXC_DIST_APPIMAGE + QJsonObject script; script["name"] = HostInstaller::HOST_NAME; script["description"] = "KeePassXC integration with native messaging support"; diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index fda7586d7..ff9d15fc0 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -76,7 +76,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_messageWidget = new MessageWidget(this); m_messageWidget->setHidden(true); - QVBoxLayout* mainLayout = new QVBoxLayout(); + auto* mainLayout = new QVBoxLayout(); QLayout* layout = new QHBoxLayout(); mainLayout->addWidget(m_messageWidget); mainLayout->addLayout(layout); @@ -119,7 +119,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) connect(m_detailsView, SIGNAL(errorOccurred(QString)), this, SLOT(showErrorMessage(QString))); - QVBoxLayout* vLayout = new QVBoxLayout(rightHandSideWidget); + auto* vLayout = new QVBoxLayout(rightHandSideWidget); vLayout->setMargin(0); vLayout->addWidget(m_searchingLabel); vLayout->addWidget(m_detailSplitter); @@ -127,8 +127,8 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_detailSplitter->addWidget(m_entryView); m_detailSplitter->addWidget(m_detailsView); - m_detailSplitter->setStretchFactor(0, 80); - m_detailSplitter->setStretchFactor(1, 20); + m_detailSplitter->setStretchFactor(0, 100); + m_detailSplitter->setStretchFactor(1, 0); m_searchingLabel->setVisible(false); @@ -795,10 +795,6 @@ void DatabaseWidget::switchToView(bool accepted) m_newParent = nullptr; } - if (accepted) { - showMessage(tr("Entry updated successfully."), MessageWidget::Positive, false, 2000); - } - setCurrentWidget(m_mainWidget); } diff --git a/src/gui/DetailsWidget.cpp b/src/gui/DetailsWidget.cpp index 1ac20c9d8..77de24666 100644 --- a/src/gui/DetailsWidget.cpp +++ b/src/gui/DetailsWidget.cpp @@ -134,7 +134,7 @@ void DetailsWidget::updateEntryHeaderLine() { Q_ASSERT(m_currentEntry); const QString title = m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->title()); - m_ui->entryTitleLabel->setText(hierarchy(m_currentEntry->group(), title)); + m_ui->entryTitleLabel->setRawText(hierarchy(m_currentEntry->group(), title)); m_ui->entryIcon->setPixmap(preparePixmap(m_currentEntry->iconPixmap(), 16)); } @@ -250,7 +250,7 @@ void DetailsWidget::updateEntryAutotypeTab() void DetailsWidget::updateGroupHeaderLine() { Q_ASSERT(m_currentGroup); - m_ui->groupTitleLabel->setText(hierarchy(m_currentGroup, {})); + m_ui->groupTitleLabel->setRawText(hierarchy(m_currentGroup, {})); m_ui->groupIcon->setPixmap(preparePixmap(m_currentGroup->iconPixmap(), 32)); } diff --git a/src/gui/DetailsWidget.ui b/src/gui/DetailsWidget.ui index 5446f6510..53787d713 100644 --- a/src/gui/DetailsWidget.ui +++ b/src/gui/DetailsWidget.ui @@ -25,11 +25,26 @@ + + 0 + + + 0 + + + 0 + + + 0 + - + QLayout::SetDefaultConstraint + + 9 + @@ -44,9 +59,9 @@ - + - + 0 0 @@ -61,19 +76,6 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -152,22 +154,75 @@ - - - - - Qt::Horizontal + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + - - QSizePolicy::Fixed - - + - 20 - 20 + 100 + 0 - + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + PointingHandCursor + + + + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + @@ -194,21 +249,8 @@ - - - - - 100 - 0 - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - + + 0 @@ -222,20 +264,20 @@ - Password + Expiration Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 100 - 0 - + + + + + 0 + 0 + @@ -261,24 +303,24 @@ - - - + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + - 100 - 0 + 20 + 20 - - PointingHandCursor - - - - - + - - + + 0 @@ -292,25 +334,22 @@ - Expiration + Password Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - + Qt::Vertical - 20 - 10 + 0 + 0 @@ -418,11 +457,26 @@ + + 0 + + + 0 + + + 0 + + + 0 + - + QLayout::SetDefaultConstraint + + 9 + @@ -437,9 +491,9 @@ - + - + 0 0 @@ -454,19 +508,6 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -503,22 +544,50 @@ - - - - - Qt::Horizontal + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + - - QSizePolicy::Fixed + + + + + + + 0 + 0 + - - - 20 - 20 - + + + 75 + true + - + + Expiration + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + @@ -543,7 +612,40 @@ - + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + @@ -567,45 +669,11 @@ - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Expiration - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - + Qt::Vertical - - - 20 - 10 - - diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp index 9e4152e25..56b1b2a38 100644 --- a/src/gui/SettingsWidget.cpp +++ b/src/gui/SettingsWidget.cpp @@ -150,6 +150,7 @@ void SettingsWidget::loadSettings() if (m_globalAutoTypeKey > 0 && m_globalAutoTypeModifiers > 0) { m_generalUi->autoTypeShortcutWidget->setShortcut(m_globalAutoTypeKey, m_globalAutoTypeModifiers); } + m_generalUi->autoTypeShortcutWidget->setAttribute(Qt::WA_MacShowFocusRect, true); m_generalUi->autoTypeDelaySpinBox->setValue(config()->get("AutoTypeDelay").toInt()); } diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index ac9681801..b180c2a01 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -755,6 +755,8 @@ bool EditEntryWidget::commitEntry() } #endif + m_historyModel->setEntries(m_entry->historyItems()); + showMessage(tr("Entry updated successfully."), MessageWidget::Positive); return true; } diff --git a/src/gui/entry/EntryModel.cpp b/src/gui/entry/EntryModel.cpp index 71828c6ed..6f7ebf6af 100644 --- a/src/gui/entry/EntryModel.cpp +++ b/src/gui/entry/EntryModel.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "core/DatabaseIcons.h" #include "core/Entry.h" @@ -35,9 +36,6 @@ const QString EntryModel::HiddenContentDisplay(QString("\u25cf").repeated(6)); // Format used to display dates const Qt::DateFormat EntryModel::DateFormat = Qt::DefaultLocaleShortDate; -// Paperclip symbol -const QString EntryModel::PaperClipDisplay("\U0001f4ce"); - EntryModel::EntryModel(QObject* parent) : QAbstractTableModel(parent) , m_group(nullptr) @@ -205,9 +203,6 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const case Accessed: result = entry->timeInfo().lastAccessTime().toLocalTime().toString(EntryModel::DateFormat); return result; - case Paperclip: - result = entry->attachments()->keys().isEmpty() ? QString() : EntryModel::PaperClipDisplay; - return result; case Attachments: // Display comma-separated list of attachments QList attachments = entry->attachments()->keys(); @@ -238,7 +233,7 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const case Paperclip: // Display entries with attachments above those without when // sorting ascendingly (and vice versa when sorting descendingly) - return entry->attachments()->keys().isEmpty() ? 1 : 0; + return entry->attachments()->isEmpty() ? 1 : 0; default: // For all other columns, simply use data provided by Qt::Display- // Role for sorting @@ -254,9 +249,13 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const case Title: if (entry->isExpired()) { return databaseIcons()->iconPixmap(DatabaseIcons::ExpiredIconIndex); - } else { - return entry->iconScaledPixmap(); } + return entry->iconScaledPixmap(); + case Paperclip: + if (!entry->attachments()->isEmpty()) { + return m_paperClipPixmap; + } + break; } } else if (role == Qt::FontRole) { QFont font; @@ -279,12 +278,6 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const if (index.column() == Paperclip) { return Qt::AlignCenter; } - } else if (role == Qt::SizeHintRole) { - if (index.column() == Paperclip) { - QFont font; - QFontMetrics fm(font); - return fm.width(PaperClipDisplay) / 2; - } } return QVariant(); @@ -316,15 +309,12 @@ QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int ro return tr("Modified"); case Accessed: return tr("Accessed"); - case Paperclip: - return EntryModel::PaperClipDisplay; case Attachments: return tr("Attachments"); } - } else if (role == Qt::TextAlignmentRole) { - switch (section) { - case Paperclip: - return Qt::AlignCenter; + } else if (role == Qt::DecorationRole) { + if (section == Paperclip) { + return m_paperClipPixmap; } } @@ -508,3 +498,8 @@ void EntryModel::togglePasswordsHidden(const bool hide) { setPasswordsHidden(hide); } + +void EntryModel::setPaperClipPixmap(const QPixmap& paperclip) +{ + m_paperClipPixmap = paperclip; +} diff --git a/src/gui/entry/EntryModel.h b/src/gui/entry/EntryModel.h index 7c668a5f6..4fc765044 100644 --- a/src/gui/entry/EntryModel.h +++ b/src/gui/entry/EntryModel.h @@ -19,6 +19,7 @@ #define KEEPASSX_ENTRYMODEL_H #include +#include class Entry; class Group; @@ -65,6 +66,8 @@ public: bool isPasswordsHidden() const; void setPasswordsHidden(const bool hide); + void setPaperClipPixmap(const QPixmap& paperclip); + signals: void switchedToListMode(); void switchedToSearchMode(); @@ -95,9 +98,10 @@ private: bool m_hideUsernames; bool m_hidePasswords; + QPixmap m_paperClipPixmap; + static const QString HiddenContentDisplay; static const Qt::DateFormat DateFormat; - static const QString PaperClipDisplay; }; #endif // KEEPASSX_ENTRYMODEL_H diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 67169d27f..19978a808 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -22,6 +22,7 @@ #include #include +#include "core/FilePath.h" #include "gui/SortFilterHideProxyModel.h" EntryView::EntryView(QWidget* parent) @@ -72,6 +73,10 @@ EntryView::EntryView(QWidget* parent) m_columnActions->setExclusive(false); for (int columnIndex = 1; columnIndex < header()->count(); ++columnIndex) { QString caption = m_model->headerData(columnIndex, Qt::Horizontal, Qt::DisplayRole).toString(); + if (columnIndex == EntryModel::Paperclip) { + caption = tr("Attachments (icon)"); + } + QAction* action = m_headerMenu->addAction(caption); action->setCheckable(true); action->setData(columnIndex); @@ -85,19 +90,18 @@ EntryView::EntryView(QWidget* parent) m_headerMenu->addSeparator(); m_headerMenu->addAction(tr("Reset to defaults"), this, SLOT(resetViewToDefaults())); + header()->setMinimumSectionSize(24); header()->setDefaultSectionSize(100); - // Stretching of last section interferes with fitting columns to window header()->setStretchLastSection(false); header()->setContextMenuPolicy(Qt::CustomContextMenu); + connect(header(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(showHeaderMenu(QPoint))); connect(header(), SIGNAL(sectionCountChanged(int, int)), SIGNAL(viewStateChanged())); connect(header(), SIGNAL(sectionMoved(int, int, int)), SIGNAL(viewStateChanged())); connect(header(), SIGNAL(sectionResized(int, int, int)), SIGNAL(viewStateChanged())); connect(header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), SIGNAL(viewStateChanged())); - // TODO: not working as expected, columns will end up being very small, - // most likely due to the widget not being sized properly at this time - //fitColumnsToWindow(); + resetFixedColumns(); // Configure default search view state and save for later use header()->showSection(EntryModel::ParentGroup); @@ -110,6 +114,8 @@ EntryView::EntryView(QWidget* parent) m_sortModel->sort(EntryModel::Title, Qt::AscendingOrder); sortByColumn(EntryModel::Title, Qt::AscendingOrder); m_defaultListViewState = header()->saveState(); + + m_model->setPaperClipPixmap(filePath()->icon("actions", "paperclip").pixmap(16)); } void EntryView::keyPressEvent(QKeyEvent* event) @@ -292,9 +298,11 @@ QByteArray EntryView::viewState() const /** * Set view state */ -bool EntryView::setViewState(const QByteArray& state) const +bool EntryView::setViewState(const QByteArray& state) { - return header()->restoreState(state); + bool status = header()->restoreState(state); + resetFixedColumns(); + return status; } /** @@ -367,6 +375,8 @@ void EntryView::toggleColumnVisibility(QAction *action) void EntryView::fitColumnsToWindow() { header()->resizeSections(QHeaderView::Stretch); + resetFixedColumns(); + fillRemainingWidth(true); emit viewStateChanged(); } @@ -378,37 +388,8 @@ void EntryView::fitColumnsToContents() { // Resize columns to fit contents header()->resizeSections(QHeaderView::ResizeToContents); - - // Determine total width of currently visible columns. If there is - // still some space available on the header, equally distribute it to - // visible columns and add remaining fraction to last visible column - int width = 0; - for (int columnIndex = 0; columnIndex < header()->count(); ++columnIndex) { - if (!header()->isSectionHidden(columnIndex)) { - width += header()->sectionSize(columnIndex); - } - } - int visible = header()->count() - header()->hiddenSectionCount(); - int avail = header()->width() - width; - if ((visible <= 0) || (avail <= 0)) { - return; - } - int add = avail / visible; - width = 0; - int last = 0; - for (int columnIndex = 0; columnIndex < header()->count(); ++columnIndex) { - if (!header()->isSectionHidden(columnIndex)) { - header()->resizeSection(columnIndex, header()->sectionSize(columnIndex) + add); - width += header()->sectionSize(columnIndex); - if (header()->visualIndex(columnIndex) > last) { - last = header()->visualIndex(columnIndex); - } - } - } - header()->resizeSection(header()->logicalIndex(last), header()->sectionSize(last) + (header()->width() - width)); - - // Shouldn't be necessary due to use of header()->resizeSection, but - // lets just do it anyway for the sake of completeness + resetFixedColumns(); + fillRemainingWidth(false); emit viewStateChanged(); } @@ -428,3 +409,46 @@ void EntryView::resetViewToDefaults() fitColumnsToWindow(); } + +void EntryView::fillRemainingWidth(bool lastColumnOnly) +{ + // Determine total width of currently visible columns + int width = 0; + int lastColumnIndex = 0; + for (int columnIndex = 0; columnIndex < header()->count(); ++columnIndex) { + if (!header()->isSectionHidden(columnIndex)) { + width += header()->sectionSize(columnIndex); + } + if (header()->visualIndex(columnIndex) > lastColumnIndex) { + lastColumnIndex = header()->visualIndex(columnIndex); + } + } + + int numColumns = header()->count() - header()->hiddenSectionCount(); + int availWidth = header()->width() - width; + if ((numColumns <= 0) || (availWidth <= 0)) { + return; + } + + if (!lastColumnOnly) { + // Equally distribute remaining width to visible columns + int add = availWidth / numColumns; + width = 0; + for (int columnIndex = 0; columnIndex < header()->count(); ++columnIndex) { + if (!header()->isSectionHidden(columnIndex)) { + header()->resizeSection(columnIndex, header()->sectionSize(columnIndex) + add); + width += header()->sectionSize(columnIndex); + } + } + } + + // Add remaining width to last column + header()->resizeSection(header()->logicalIndex(lastColumnIndex), header()->sectionSize(lastColumnIndex) + (header()->width() - width)); +} + +void EntryView::resetFixedColumns() +{ + header()->setSectionResizeMode(EntryModel::Paperclip, QHeaderView::Fixed); + header()->resizeSection(EntryModel::Paperclip, header()->minimumSectionSize()); +} + diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index 26672a665..a8422c563 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -48,7 +48,7 @@ public: bool isPasswordsHidden() const; void setPasswordsHidden(const bool hide); QByteArray viewState() const; - bool setViewState(const QByteArray& state) const; + bool setViewState(const QByteArray& state); public slots: void setGroup(Group* group); @@ -74,6 +74,9 @@ private slots: void resetViewToDefaults(); private: + void fillRemainingWidth(bool lastColumnOnly); + void resetFixedColumns(); + EntryModel* const m_model; SortFilterHideProxyModel* const m_sortModel; bool m_inSearchMode; diff --git a/src/proxy/CMakeLists.txt b/src/proxy/CMakeLists.txt index 35e02ea26..f19481c06 100755 --- a/src/proxy/CMakeLists.txt +++ b/src/proxy/CMakeLists.txt @@ -42,13 +42,13 @@ if(WITH_XC_BROWSER) add_custom_command(TARGET keepassxc-proxy POST_BUILD - COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore "@executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore" ${PROXY_APP_DIR} + COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change ${Qt5_PREFIX}/lib/QtCore.framework/Versions/5/QtCore "@executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore" ${PROXY_APP_DIR} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src COMMENT "Changing linking of keepassxc-proxy QtCore") add_custom_command(TARGET keepassxc-proxy POST_BUILD - COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change /usr/local/opt/qt/lib/QtNetwork.framework/Versions/5/QtNetwork "@executable_path/../Frameworks/QtNetwork.framework/Versions/5/QtNetwork" ${PROXY_APP_DIR} + COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change ${Qt5_PREFIX}/lib/QtNetwork.framework/Versions/5/QtNetwork "@executable_path/../Frameworks/QtNetwork.framework/Versions/5/QtNetwork" ${PROXY_APP_DIR} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src COMMENT "Changing linking of keepassxc-proxy QtNetwork") endif()