KPeople

mergedialog.cpp
1/*
2 KPeople - Duplicates
3 SPDX-FileCopyrightText: 2013 Franck Arrecot <franck.arrecot@gmail.com>
4
5 SPDX-License-Identifier: LGPL-2.1-or-later
6*/
7
8#include "mergedialog.h"
9#include "duplicatesfinder_p.h"
10#include "matchessolver_p.h"
11#include "mergedelegate.h"
12#include "persondata.h"
13#include "personsmodel.h"
14
15#include "kpeople_widgets_debug.h"
16
17#include <QDialogButtonBox>
18#include <QLabel>
19#include <QLayout>
20#include <QListView>
21#include <QPushButton>
22#include <QStandardItemModel>
23
24#include <KLocalizedString>
25#include <KPixmapSequence>
26#include <KPixmapSequenceWidget>
27
28using namespace KPeople;
29
30class MergeDialogPrivate
31{
32public:
33 PersonsModel *personsModel;
34 QListView *view;
35 MergeDelegate *delegate;
36
37 QStandardItemModel *model;
38 DuplicatesFinder *duplicatesFinder;
39 KPixmapSequenceWidget *sequence;
40};
41
42MergeDialog::MergeDialog(QWidget *parent)
43 : QDialog(parent)
44 , d_ptr(new MergeDialogPrivate)
45{
47
48 d_ptr->personsModel = nullptr;
49 d_ptr->delegate = nullptr;
50 d_ptr->duplicatesFinder = nullptr;
51
52 setWindowTitle(i18n("Duplicates Manager"));
53
54 auto *layout = new QVBoxLayout(this);
55
56 setMinimumSize(450, 350);
57
58 d->model = new QStandardItemModel(this);
59 d->view = new QListView(this);
60 d->view->setModel(d->model);
61 d->view->setEditTriggers(QAbstractItemView::NoEditTriggers);
62
63 QLabel *topLabel = new QLabel(i18n("Select contacts to be merged"));
64
65 QDialogButtonBox *buttons = new QDialogButtonBox(this);
68 connect(buttons, SIGNAL(accepted()), SLOT(onMergeButtonClicked()));
69 connect(buttons, SIGNAL(rejected()), SLOT(reject()));
70
71 d->sequence = new KPixmapSequenceWidget(this);
72 d->sequence->setSequence(KPixmapSequence(QStringLiteral("process-working"), 22));
73 d->sequence->setInterval(100);
74 d->sequence->setVisible(false);
75
76 layout->addWidget(topLabel);
77 layout->addWidget(d->view);
78 layout->addWidget(d->sequence);
79 layout->addWidget(buttons);
80}
81
82MergeDialog::~MergeDialog()
83{
84 delete d_ptr;
85}
86
88{
90 d->personsModel = model;
91 if (d->personsModel) {
92 searchForDuplicates();
93 connect(d->personsModel, SIGNAL(modelInitialized(bool)), SLOT(searchForDuplicates()));
94 }
95}
96
97void MergeDialog::searchForDuplicates()
98{
100 if (!d->personsModel || !d->personsModel->rowCount() || d->duplicatesFinder) {
101 qCWarning(KPEOPLE_WIDGETS_LOG) << "MergeDialog failed to launch the duplicates research";
102 return;
103 }
104 d->duplicatesFinder = new DuplicatesFinder(d->personsModel);
105 connect(d->duplicatesFinder, SIGNAL(result(KJob *)), SLOT(searchForDuplicatesFinished(KJob *)));
106 d->duplicatesFinder->start();
107}
108
109void MergeDialog::onMergeButtonClicked()
110{
112 QList<Match> matches;
113 for (int i = 0, rows = d->model->rowCount(); i < rows; i++) {
114 QStandardItem *item = d->model->item(i, 0);
115 if (item->checkState() == Qt::Checked) {
116 for (int j = 0, contactsCount = item->rowCount(); j < contactsCount; ++j) {
117 QStandardItem *matchItem = item->child(j);
118 matches << matchItem->data(MergeDialog::MergeReasonRole).value<Match>();
119 }
120 }
121 }
122
123 MatchesSolver *solverJob = new MatchesSolver(matches, d->personsModel, this);
124 solverJob->start();
125 d->sequence->setVisible(true);
126 d->view->setEnabled(false);
127 connect(solverJob, SIGNAL(finished(KJob *)), this, SLOT(accept()));
128}
129
130void MergeDialog::searchForDuplicatesFinished(KJob *)
131{
133 feedDuplicateModelFromMatches(d->duplicatesFinder->results());
134
135 d->delegate = new MergeDelegate(d->view);
136 d->view->setItemDelegate(d->delegate);
137
138 // To extend the selected item
139 connect(d->view->selectionModel(),
140 SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
141 d->delegate,
142 SLOT(onSelectedContactsChanged(QItemSelection, QItemSelection)));
143 // To contract an already selected item
144 connect(d->view, SIGNAL(doubleClicked(QModelIndex)), d->delegate, SLOT(onClickContactParent(QModelIndex)));
145}
146
147void MergeDialog::feedDuplicateModelFromMatches(const QList<Match> &matches)
148{
152
153 for (const Match &match : matches) {
154 QPersistentModelIndex destination = doneIndexes.value(match.indexA, match.indexA);
155 QHash<QPersistentModelIndex, QList<Match>>::iterator currentValue = compareTable.find(destination);
156
157 if (currentValue == compareTable.end()) { // new parent, create it
158 compareTable[match.indexA] = QList<Match>() << match;
159 } else { // know parent, add child
160 currentValue->append(match);
161 }
162 doneIndexes[match.indexB] = destination;
163 }
164 // now build the model : 1st dimension = person candidate, 2nd dimension = match
165 QStandardItem *rootItem = d->model->invisibleRootItem();
167
168 for (i = compareTable.constBegin(); i != compareTable.constEnd(); ++i) {
169 // Build the merge Contact in the model
170 QStandardItem *parent = itemMergeContactFromMatch(true, i->first());
171 rootItem->appendRow(parent);
172
173 for (const Match &matchChild : std::as_const(*i)) {
174 parent->appendRow(itemMergeContactFromMatch(false, matchChild));
175 }
176 }
177
178 rootItem->sortChildren(0);
179}
180
181QStandardItem *MergeDialog::itemMergeContactFromMatch(bool isParent, const Match &match)
182{
183 QStandardItem *item = new QStandardItem;
184
185 item->setCheckable(true);
187 item->setSizeHint(MergeDelegate::pictureSize());
188 item->setData(true, KExtendableItemDelegate::ShowExtensionIndicatorRole);
189
190 QVariant deco;
191 if (!isParent) { // child
192 QString uri = match.indexB.data(PersonsModel::PersonUriRole).toString();
193 item->setData(uri, UriRole);
194
195 item->setData(QVariant::fromValue<Match>(match), MergeReasonRole);
196 item->setText(match.indexB.data(Qt::DisplayRole).toString());
197 deco = match.indexB.data(Qt::DecorationRole);
198
199 } else { // parent
200 QString uri = match.indexA.data(PersonsModel::PersonUriRole).toString();
201 item->setData(uri, UriRole);
202
203 item->setText(match.indexA.data(Qt::DisplayRole).toString());
204 deco = match.indexA.data(Qt::DecorationRole);
205 }
206
207 QIcon icon;
208 if (deco.userType() == QMetaType::QIcon) {
209 icon = deco.value<QIcon>();
210 } else if (deco.userType() == QMetaType::QPixmap) {
211 icon = QIcon(deco.value<QPixmap>());
212 } else if (deco.userType() == QMetaType::QImage) {
213 icon = QIcon(QPixmap::fromImage(deco.value<QImage>()));
214 } else {
215 qCWarning(KPEOPLE_WIDGETS_LOG) << "unknown decoration type" << deco.typeName();
216 }
217 item->setIcon(icon);
218 return item;
219}
220
221#include "moc_mergedialog.cpp"
The merge dialog will be used to provide a GUI to attempt to figure out what contacts should be merge...
Definition mergedialog.h:34
void setPersonsModel(PersonsModel *model)
Specifies which PersonsModel will be used to look for duplicates.
This class creates a model of all known contacts from all sources Contacts are represented as a tree ...
QString i18n(const char *text, const TYPE &arg...)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
virtual void accept()
void finished(int result)
int result() const const
QPushButton * addButton(StandardButton button)
const_iterator constBegin() const const
const_iterator constEnd() const const
iterator end()
iterator find(const Key &key)
T value(const Key &key) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
void appendRow(QStandardItem *item)
Qt::CheckState checkState() const const
QStandardItem * child(int row, int column) const const
virtual QVariant data(int role) const const
QStandardItemModel * model() const const
int rowCount() const const
void setCheckState(Qt::CheckState state)
void setCheckable(bool checkable)
virtual void setData(const QVariant &value, int role)
void setIcon(const QIcon &icon)
void setSizeHint(const QSize &size)
void setText(const QString &text)
void sortChildren(int column, Qt::SortOrder order)
QStandardItem * invisibleRootItem() const const
QStandardItem * item(int row, int column) const const
DisplayRole
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
const char * typeName() const const
int userType() const const
T value() const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:17:46 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.