diff --git a/src/conf/rootfoldersconfigurationpage.cpp b/src/conf/rootfoldersconfigurationpage.cpp index 13a1576..3d57acc 100644 --- a/src/conf/rootfoldersconfigurationpage.cpp +++ b/src/conf/rootfoldersconfigurationpage.cpp @@ -1,274 +1,275 @@ // SPDX-FileCopyrightText: 2024 g10 Code GmbH // SPDX-FileContributor: Carl Schwan <carl@carlschwan.eu> // SPDX-License-Identifier: GPL-2.0-or-later #include "rootfoldersconfigurationpage.h" #include "models/rootfoldersmodel.h" #include "rootfolderconfig.h" #include "rootfoldersmanager.h" #include "ui_rootfoldersconfigurationpage.h" #include <QAction> #include <QCheckBox> #include <QDialogButtonBox> #include <QFileDialog> #include <QFileInfo> #include <QLabel> #include <QMessageBox> #include <QPainter> #include <QPushButton> #include <QTimer> #include <QToolButton> #include <KMessageBox> #include <KSelectionProxyModel> #include <KUrlRequester> #include <KWidgetItemDelegate> #include <Libkleo/DefaultKeyFilter> #include <Libkleo/KeySelectionCombo> #include "kwidgetsaddons_version.h" using namespace GpgPass::Config; class DummyButton : public QToolButton { public: int buttonSize(QAbstractItemView *view) { constexpr int iconSize = 16; QStyleOptionToolButton styleOption; initStyleOption(&styleOption); const auto size = view->style()->sizeFromContents(QStyle::CT_ToolButton, &styleOption, QSize(iconSize, iconSize)); return qMax(size.width(), size.height()); } }; class RootFolderItemDelegate : public KWidgetItemDelegate { Q_OBJECT public: explicit RootFolderItemDelegate(QAbstractItemView *view) : KWidgetItemDelegate(view) { mMargin = 4; mSpacing = 4; DummyButton dummyButton; mButtonSize = dummyButton.buttonSize(view); } protected: QList<QWidget *> createItemWidgets(const QModelIndex &index) const override { if (index.column() != 0 && qobject_cast<KSelectionProxyModel const *>(index.model()) != nullptr) { return {}; } auto removeButton = new QToolButton; removeButton->setAccessibleName(i18nc("@action:button", "Delete Folder")); removeButton->setAutoRaise(true); setBlockedEventTypes(removeButton, {QEvent::MouseButtonPress, QEvent::MouseButtonRelease, QEvent::MouseButtonDblClick}); removeButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); connect(removeButton, &QToolButton::clicked, this, &RootFolderItemDelegate::slotRemoveButtonClicked); return {removeButton}; } #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) void updateItemWidgets(const QList<QWidget *> &widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const override #else void updateItemWidgets(const QList<QWidget *> widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const override #endif { if (widgets.count() == 0) { return; } auto removeButton = static_cast<QToolButton *>(widgets[0]); QSize buttonSize(mButtonSize, option.rect.height() - 2 * mMargin); removeButton->resize(buttonSize); removeButton->move(option.rect.width() - mButtonSize - mMargin, mMargin); + removeButton->setVisible(itemView()->model()->rowCount() > 1); } void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { if (!index.isValid()) { return; } const bool selected = option.state & QStyle::State_Selected; itemView()->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, nullptr); QRect textRect = option.rect; textRect.setLeft(textRect.left() + mMargin * 2); textRect.setWidth(textRect.width() - mButtonSize - mMargin - mSpacing); painter->setPen(option.palette.color(QPalette::Normal, selected ? QPalette::HighlightedText : QPalette::Text)); painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, index.data().toString()); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override { const int width = option.fontMetrics.boundingRect(index.data().toString()).width(); const int height = qMax(mButtonSize, option.fontMetrics.height()); return QSize(width + 2 * mMargin, height + 2 * mMargin); } Q_SIGNALS: void removeFolder(const QModelIndex &index); private: void slotRemoveButtonClicked() { const QModelIndex index = focusedIndex(); if (!index.isValid()) { qWarning() << "!index.isValid()"; return; } Q_EMIT removeFolder(index); } int mMargin; int mSpacing; int mButtonSize; }; RootFoldersConfigurationPage::RootFoldersConfigurationPage(RootFoldersManager *rootFoldersManager, QAction *addStoreAction, QWidget *parent) : GpgPassConfigModule(parent) , ui(new Ui::RootFoldersConfigurationPage) , m_rootFoldersManager(rootFoldersManager) , m_rootFoldersModel(new RootFoldersModel(m_rootFoldersManager, this)) , m_addStoreAction(addStoreAction) { ui->setupUi(this); ui->rootFoldersView->setModel(m_rootFoldersModel); auto delegate = new RootFolderItemDelegate(ui->rootFoldersView); ui->rootFoldersView->setItemDelegate(delegate); ui->splitter->setStretchFactor(1, 3); connect(ui->rootFoldersView->selectionModel(), &QItemSelectionModel::currentChanged, this, &RootFoldersConfigurationPage::selectTreeItem); connect(m_addStoreAction, &QAction::triggered, this, [this]() { const auto idx = m_rootFoldersModel->addNewFolder(); ui->rootFoldersView->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect); }); // Name m_nameInput = Kleo::FormTextInput<QLineEdit>::create(this); m_nameInput->setLabelText(i18nc("@label", "Name:")); m_nameInput->setValueRequiredErrorMessage(i18n("Enter a valid path.")); ui->folderInfoLayout->addWidget(m_nameInput->label()); ui->folderInfoLayout->addWidget(m_nameInput->hintLabel()); ui->folderInfoLayout->addWidget(m_nameInput->errorLabel()); ui->folderInfoLayout->addWidget(m_nameInput->widget()); ui->folderInfoLayout->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing)); // Location m_locationInput = Kleo::FormTextInput<KUrlRequester>::create(this); m_locationInput->setLabelText(i18nc("@label", "Path:")); m_locationInput->setValueRequiredErrorMessage(i18n("Enter a valid path.")); m_locationInput->setInvalidEntryErrorMessage(i18n("Enter a valid path.")); m_locationInput->widget()->setMode(KFile::Directory); ui->folderInfoLayout->addWidget(m_locationInput->label()); ui->folderInfoLayout->addWidget(m_locationInput->hintLabel()); ui->folderInfoLayout->addWidget(m_locationInput->errorLabel()); ui->folderInfoLayout->addWidget(m_locationInput->widget()); ui->folderInfoLayout->addStretch(); #if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) auto button = new QToolButton(this); button->setDefaultAction(addStoreAction); button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); ui->folderInfoLayout->addWidget(button); #endif ui->folderInfoLayout->setContentsMargins(style()->pixelMetric(QStyle::PM_LayoutLeftMargin), style()->pixelMetric(QStyle::PM_LayoutTopMargin), style()->pixelMetric(QStyle::PM_LayoutRightMargin), style()->pixelMetric(QStyle::PM_LayoutBottomMargin)); connect(m_locationInput->widget(), &KUrlRequester::textChanged, this, &RootFoldersConfigurationPage::slotLocationChanged); connect(m_nameInput->widget(), &QLineEdit::textEdited, this, &RootFoldersConfigurationPage::slotNameChanged); connect(delegate, &RootFolderItemDelegate::removeFolder, m_rootFoldersModel, &RootFoldersModel::slotRemoveFolder); connect(delegate, &RootFolderItemDelegate::removeFolder, this, &RootFoldersConfigurationPage::changed); } void RootFoldersConfigurationPage::save() { m_rootFoldersManager->save(); } void RootFoldersConfigurationPage::load() { m_rootFoldersModel->load(); if (m_rootFoldersModel->rowCount({}) > 0) { const auto idx = m_rootFoldersModel->index(0, 0); ui->rootFoldersView->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect); } } void RootFoldersConfigurationPage::defaults() { m_rootFoldersManager->load(); } void RootFoldersConfigurationPage::selectTreeItem(const QModelIndex &) { const auto rootFolder = fromCurrentIndex(); if (!rootFolder) { return; } m_nameInput->widget()->setText(rootFolder->name()); m_locationInput->widget()->setUrl(QUrl::fromLocalFile(rootFolder->path())); for (int i = 0, count = ui->folderInfoLayout->count(); i < count; i++) { const auto item = ui->folderInfoLayout->itemAt(i); const auto spacer = item->spacerItem(); if (spacer) { spacer->changeSize(0, style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing), QSizePolicy::Minimum, QSizePolicy::Fixed); break; } } ui->folderInfoLayout->invalidate(); } RootFolderConfig *RootFoldersConfigurationPage::fromCurrentIndex() { const auto index = ui->rootFoldersView->currentIndex(); if (!index.isValid()) { return {}; } return index.data(Qt::UserRole).value<RootFolderConfig *>(); } void RootFoldersConfigurationPage::slotNameChanged(const QString &text) { auto rootFolder = fromCurrentIndex(); rootFolder->setName(text); rootFolder->save(); Q_EMIT changed(); } void RootFoldersConfigurationPage::slotLocationChanged(const QString &text) { auto rootFolder = fromCurrentIndex(); const auto oldPath = rootFolder->path(); if (oldPath == text) { return; } rootFolder->setPath(m_locationInput->widget()->url().toLocalFile()); rootFolder->save(); Q_EMIT changed(); } #include "rootfoldersconfigurationpage.moc"