KPeople

mergedialog.cpp
1 /*
2  KPeople - Duplicates
3  SPDX-FileCopyrightText: 2013 Franck Arrecot <[email protected]>
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 
28 using namespace KPeople;
29 
30 class MergeDialogPrivate
31 {
32 public:
33  PersonsModel *personsModel;
34  QListView *view;
35  MergeDelegate *delegate;
36 
37  QStandardItemModel *model;
38  DuplicatesFinder *duplicatesFinder;
39  KPixmapSequenceWidget *sequence;
40 };
41 
42 MergeDialog::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 
82 MergeDialog::~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 
97 void 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 
109 void MergeDialog::onMergeButtonClicked()
110 {
111  Q_D(MergeDialog);
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 
130 void MergeDialog::searchForDuplicatesFinished(KJob *)
131 {
132  Q_D(MergeDialog);
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 
147 void MergeDialog::feedDuplicateModelFromMatches(const QList<Match> &matches)
148 {
149  Q_D(MergeDialog);
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 
181 QStandardItem *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.type() == (QVariant::Icon)) {
209  icon = deco.value<QIcon>();
210  } else if (deco.type() == (QVariant::Pixmap)) {
211  icon = QIcon(deco.value<QPixmap>());
212  } else if (deco.type() == (QVariant::Image)) {
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 }
const T value(const Key &key) const const
void sortChildren(int column, Qt::SortOrder order)
This class creates a model of all known contacts from all sources Contacts are represented as a tree ...
Definition: personsmodel.h:33
DisplayRole
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
Qt::CheckState checkState() const const
T value() const const
void setPersonsModel(PersonsModel *model)
Specifies which PersonsModel will be used to look for duplicates.
Definition: mergedialog.cpp:87
QHash::iterator find(const Key &key)
void setText(const QString &text)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QStandardItem * child(int row, int column) const const
int rowCount() const const
QString i18n(const char *text, const TYPE &arg...)
QVariant::Type type() const const
The merge dialog will be used to provide a GUI to attempt to figure out what contacts should be merge...
Definition: mergedialog.h:33
QHash::const_iterator constBegin() const const
QHash::const_iterator constEnd() const const
virtual void accept()
void addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role)
QStandardItemModel * model() const const
int result() const const
void setIcon(const QIcon &icon)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
virtual QVariant data(int role) const const
QStandardItem * invisibleRootItem() const const
void appendRow(const QList< QStandardItem * > &items)
virtual void setData(const QVariant &value, int role)
void setCheckable(bool checkable)
QStandardItem * item(int row, int column) const const
void setCheckState(Qt::CheckState state)
const char * typeName() const const
QObject * parent() const const
void finished(int result)
void setSizeHint(const QSize &size)
Q_D(Todo)
QHash::iterator end()
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sun Aug 14 2022 04:09:08 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.