Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 100 additions & 11 deletions src/gui/passkeys/PasskeyImportDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,55 @@
#include "browser/BrowserService.h"
#include "core/Metadata.h"
#include "gui/MainWindow.h"
#include "gui/group/GroupModel.h"
#include <QCloseEvent>
#include <QFileInfo>
#include <QSortFilterProxyModel>

// ---------------------------------------------------------------------------
// GroupModelNoRecycle
// Thin QSortFilterProxyModel that hides the recycle bin group (same pattern
// as DatabaseSettingsWidgetFdoSecrets::GroupModelNoRecycle).
// ---------------------------------------------------------------------------
class PasskeyImportDialog::GroupModelNoRecycle : public QSortFilterProxyModel
{
Q_OBJECT

Database* m_db;

public:
explicit GroupModelNoRecycle(Database* db, QObject* parent = nullptr)
: QSortFilterProxyModel(parent)
, m_db(db)
{
Q_ASSERT(db);
setSourceModel(new GroupModel(m_db, this));
}

Group* groupFromIndex(const QModelIndex& index) const
{
auto* groupModel = qobject_cast<GroupModel*>(sourceModel());
Q_ASSERT(groupModel);
return groupModel->groupFromIndex(mapToSource(index));
}

protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override
{
const auto sourceIdx = sourceModel()->index(sourceRow, 0, sourceParent);
if (!sourceIdx.isValid()) {
return false;
}
auto* groupModel = qobject_cast<GroupModel*>(sourceModel());
Q_ASSERT(groupModel);
const auto* group = groupModel->groupFromIndex(sourceIdx);
const auto* recycleBin = m_db->metadata()->recycleBin();
return group && !group->isRecycled()
&& (!recycleBin || group->uuid() != recycleBin->uuid());
}
};

// ---------------------------------------------------------------------------

PasskeyImportDialog::PasskeyImportDialog(QWidget* parent)
: QDialog(parent)
Expand All @@ -38,7 +85,26 @@ PasskeyImportDialog::PasskeyImportDialog(QWidget* parent)
connect(m_ui->cancelButton, SIGNAL(clicked()), SLOT(reject()));
connect(m_ui->selectDatabaseCombobBox, SIGNAL(currentIndexChanged(int)), SLOT(changeDatabase(int)));
connect(m_ui->selectEntryComboBox, SIGNAL(currentIndexChanged(int)), SLOT(changeEntry(int)));
connect(m_ui->selectGroupComboBox, SIGNAL(currentIndexChanged(int)), SLOT(changeGroup(int)));

// When the user toggles between "default group" and "select group",
// re-evaluate which group UUID is current.
connect(m_ui->useDefaultGroupRadio, &QRadioButton::toggled,
this, &PasskeyImportDialog::onGroupSelectionChanged);

// When "Select group" becomes checked and nothing is selected yet,
// auto-select the root item so the user always has a valid destination.
connect(m_ui->selectCustomGroupRadio, &QRadioButton::toggled, this, [this](bool checked) {
if (checked && m_groupModel
&& !m_ui->selectGroupTreeView->selectionModel()->hasSelection()) {
const auto rootIdx = m_groupModel->index(0, 0);
if (rootIdx.isValid()) {
m_ui->selectGroupTreeView->selectionModel()->select(
rootIdx, QItemSelectionModel::SelectCurrent);
m_ui->selectGroupTreeView->setCurrentIndex(rootIdx);
}
}
onGroupSelectionChanged();
});
}

PasskeyImportDialog::~PasskeyImportDialog()
Expand Down Expand Up @@ -169,16 +235,29 @@ void PasskeyImportDialog::addGroups()
return;
}

m_ui->selectGroupComboBox->clear();
m_ui->selectGroupComboBox->addItem(tr("Default passkeys group (Imported Passkeys)"), {});

for (const auto& group : m_selectedDatabase->rootGroup()->groupsRecursive(true)) {
if (!group || group->isRecycled() || group == m_selectedDatabase->metadata()->recycleBin()) {
continue;
}
// Disconnect any signal still bound to the old selection model.
if (auto* sm = m_ui->selectGroupTreeView->selectionModel()) {
disconnect(sm, nullptr, this, nullptr);
}

m_ui->selectGroupComboBox->addItem(group->fullPath(), group->uuid());
// Reset to the default group and uncheck any custom selection.
m_selectedGroupUuid = QUuid();
{
QSignalBlocker b(m_ui->useDefaultGroupRadio);
m_ui->useDefaultGroupRadio->setChecked(true);
}

// (Re-)build the tree model, filtering out the recycle bin.
m_groupModel.reset(new GroupModelNoRecycle(m_selectedDatabase.data(), this));
m_ui->selectGroupTreeView->setModel(m_groupModel.data());
m_ui->selectGroupTreeView->expandAll();

connect(m_ui->selectGroupTreeView->selectionModel(),
&QItemSelectionModel::selectionChanged,
this,
&PasskeyImportDialog::onGroupSelectionChanged);

emit updateEntries();
}

void PasskeyImportDialog::changeDatabase(int index)
Expand All @@ -193,8 +272,18 @@ void PasskeyImportDialog::changeEntry(int index)
m_selectedEntryUuid = m_ui->selectEntryComboBox->itemData(index).value<QUuid>();
}

void PasskeyImportDialog::changeGroup(int index)
void PasskeyImportDialog::onGroupSelectionChanged()
{
m_selectedGroupUuid = m_ui->selectGroupComboBox->itemData(index).value<QUuid>();
if (m_ui->useDefaultGroupRadio->isChecked()) {
m_selectedGroupUuid = QUuid();
} else if (m_groupModel) {
const auto indexes = m_ui->selectGroupTreeView->selectionModel()->selectedIndexes();
if (!indexes.isEmpty()) {
const auto* group = m_groupModel->groupFromIndex(indexes.first());
m_selectedGroupUuid = group ? group->uuid() : QUuid();
}
}
emit updateEntries();
}

#include "PasskeyImportDialog.moc"
5 changes: 4 additions & 1 deletion src/gui/passkeys/PasskeyImportDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ class PasskeyImportDialog : public QDialog
private:
void addDatabases();

class GroupModelNoRecycle;

signals:
void updateEntries();
void updateGroups();
Expand All @@ -61,10 +63,11 @@ private slots:
void addGroups();
void changeDatabase(int index);
void changeEntry(int index);
void changeGroup(int index);
void onGroupSelectionChanged();

private:
QScopedPointer<Ui::PasskeyImportDialog> m_ui;
QScopedPointer<GroupModelNoRecycle> m_groupModel;
QSharedPointer<Database> m_selectedDatabase;
QUuid m_selectedDatabaseUuid;
QUuid m_selectedEntryUuid;
Expand Down
80 changes: 76 additions & 4 deletions src/gui/passkeys/PasskeyImportDialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>500</width>
<height>300</height>
<height>500</height>
</rect>
</property>
<property name="sizePolicy">
Expand All @@ -19,7 +19,7 @@
<property name="minimumSize">
<size>
<width>400</width>
<height>300</height>
<height>420</height>
</size>
</property>
<property name="windowTitle">
Expand Down Expand Up @@ -89,10 +89,62 @@
<property name="text">
<string>Group</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="selectGroupComboBox"/>
<layout class="QVBoxLayout" name="groupSelectionLayout">
<property name="spacing">
<number>4</number>
</property>
<item>
<widget class="QRadioButton" name="useDefaultGroupRadio">
<property name="text">
<string>Default passkeys group (Imported Passkeys)</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">groupButtonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="selectCustomGroupRadio">
<property name="text">
<string>Select group:</string>
</property>
<attribute name="buttonGroup">
<string notr="true">groupButtonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QTreeView" name="selectGroupTreeView">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>120</height>
</size>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="entryLabel">
Expand Down Expand Up @@ -170,5 +222,25 @@
</layout>
</widget>
<resources/>
<connections/>
<connections>
<connection>
<sender>selectCustomGroupRadio</sender>
<signal>toggled(bool)</signal>
<receiver>selectGroupTreeView</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>249</x>
<y>150</y>
</hint>
<hint type="destinationlabel">
<x>249</x>
<y>220</y>
</hint>
</hints>
</connection>
</connections>
<buttongroups>
<buttongroup name="groupButtonGroup"/>
</buttongroups>
</ui>