Akonadi Contacts

contactgroupeditordelegate.cpp
1 /*
2  This file is part of Akonadi Contact.
3 
4  SPDX-FileCopyrightText: 2009 Tobias Koenig <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "contactgroupeditordelegate_p.h"
10 
11 #include "contactcompletionmodel_p.h"
12 #include "contactgroupmodel_p.h"
13 
14 #include <KComboBox>
15 #include <entitytreemodel.h>
16 
17 #include <QAbstractItemView>
18 #include <QCompleter>
19 #include <QIcon>
20 #include <QMouseEvent>
21 #include <QSortFilterProxyModel>
22 #include <QTimer>
23 
24 using namespace Akonadi;
25 
26 /**
27  * @short Model that filters out all contacts without email address.
28  */
29 class ContactsWithEmailFilterModel : public QSortFilterProxyModel
30 {
31 public:
32  ContactsWithEmailFilterModel(QObject *parent)
33  : QSortFilterProxyModel(parent)
34  {
35  // contact names should be sorted correctly
36  setSortLocaleAware(true);
37  }
38 
39 protected:
40  bool filterAcceptsRow(int row, const QModelIndex &parent) const override
41  {
42  const QModelIndex index = sourceModel()->index(row, Akonadi::ContactCompletionModel::EmailColumn, parent);
43  if (!index.isValid()) {
44  return false;
45  }
46 
47  return !index.data().toString().isEmpty();
48  }
49 };
50 
51 ContactLineEdit::ContactLineEdit(bool isReference, ContactCompletionModel::Columns column, QWidget *parent)
52  : QLineEdit(parent)
53  , mIsReference(isReference)
54 {
55  setFrame(false);
56 
57  auto filter = new ContactsWithEmailFilterModel(this);
58  filter->setSourceModel(Akonadi::ContactCompletionModel::self());
59 
60  auto completer = new QCompleter(filter, this);
61  completer->setCompletionColumn(column);
62  completer->setCaseSensitivity(Qt::CaseInsensitive);
63  connect(completer, QOverload<const QModelIndex &>::of(&QCompleter::activated), this, QOverload<const QModelIndex &>::of(&ContactLineEdit::completed));
64 
65  setCompleter(completer);
66 
67  connect(this, &QLineEdit::textEdited, this, &ContactLineEdit::slotTextEdited);
68 }
69 
70 bool ContactLineEdit::isReference() const
71 {
72  return mIsReference;
73 }
74 
75 Akonadi::Item ContactLineEdit::completedItem() const
76 {
77  return mItem;
78 }
79 
80 void ContactLineEdit::completed(const QModelIndex &index)
81 {
82  if (index.isValid()) {
84  mIsReference = true;
85  } else {
86  mItem = Item();
87  mIsReference = false;
88  }
89 
90  Q_EMIT completed(this);
91 }
92 
93 void ContactLineEdit::slotTextEdited()
94 {
95  // if the user has edited the text, we break up the reference
96  mIsReference = false;
97 }
98 
99 class Q_DECL_HIDDEN ContactGroupEditorDelegate::Private
100 {
101 public:
102  Private()
103  : mButtonSize(16, 16)
104  , mIcon(QIcon::fromTheme(QStringLiteral("list-remove")))
105  {
106  }
107 
108  QSize mButtonSize;
109  const QIcon mIcon;
110  QAbstractItemView *mItemView = nullptr;
111 };
112 
113 ContactGroupEditorDelegate::ContactGroupEditorDelegate(QAbstractItemView *view, QObject *parent)
114  : QStyledItemDelegate(parent)
115  , d(new Private)
116 {
117  d->mItemView = view;
118 }
119 
120 ContactGroupEditorDelegate::~ContactGroupEditorDelegate()
121 {
122  delete d;
123 }
124 
125 QWidget *ContactGroupEditorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
126 {
127  const bool isReference = index.data(ContactGroupModel::IsReferenceRole).toBool();
128  Q_UNUSED(option)
129  if (index.column() == 0) {
130  auto edit = new ContactLineEdit(isReference, ContactCompletionModel::EmailColumn, parent);
131  connect(edit, QOverload<QWidget *>::of(&ContactLineEdit::completed), this, &ContactGroupEditorDelegate::completed);
132 
133  return edit;
134  } else {
135  if (index.data(ContactGroupModel::IsReferenceRole).toBool()) {
136  auto comboBox = new KComboBox(parent);
137  comboBox->setFrame(false);
138  comboBox->setAutoFillBackground(true);
139  return comboBox;
140  } else {
141  auto edit = new ContactLineEdit(isReference, ContactCompletionModel::EmailColumn, parent);
142  connect(edit, QOverload<QWidget *>::of(&ContactLineEdit::completed), this, &ContactGroupEditorDelegate::completed);
143  return edit;
144  }
145  }
146 }
147 
148 void ContactGroupEditorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
149 {
150  if (index.data(ContactGroupModel::IsReferenceRole).toBool()) {
151  if (index.column() == 0) {
152  auto lineEdit = qobject_cast<QLineEdit *>(editor);
153  if (!lineEdit) {
154  return;
155  }
156 
157  lineEdit->setText(index.data(Qt::EditRole).toString());
158  } else {
159  auto comboBox = qobject_cast<KComboBox *>(editor);
160  if (!comboBox) {
161  return;
162  }
163 
164  const QStringList emails = index.data(ContactGroupModel::AllEmailsRole).toStringList();
165  comboBox->clear();
166  comboBox->addItems(emails);
167  comboBox->setCurrentIndex(comboBox->findText(index.data(Qt::EditRole).toString()));
168  }
169  } else {
170  auto lineEdit = qobject_cast<QLineEdit *>(editor);
171  if (!lineEdit) {
172  return;
173  }
174 
175  lineEdit->setText(index.data(Qt::EditRole).toString());
176  }
177 }
178 
179 void ContactGroupEditorDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
180 {
181  if (index.data(ContactGroupModel::IsReferenceRole).toBool()) {
182  if (index.column() == 0) {
183  auto lineEdit = static_cast<ContactLineEdit *>(editor);
184 
185  const bool isReference = lineEdit->isReference();
186  const Item item = lineEdit->completedItem();
187  model->setData(index, isReference, ContactGroupModel::IsReferenceRole);
188  if (isReference) {
189  if (item.isValid()) {
190  model->setData(index, item.id(), Qt::EditRole);
191  }
192  } else {
193  model->setData(index, lineEdit->text(), Qt::EditRole);
194  }
195  }
196 
197  if (index.column() == 1) {
198  auto comboBox = qobject_cast<KComboBox *>(editor);
199  if (!comboBox) {
200  return;
201  }
202 
203  model->setData(index, comboBox->currentText(), Qt::EditRole);
204  }
205  } else {
206  auto lineEdit = static_cast<ContactLineEdit *>(editor);
207 
208  const bool isReference = lineEdit->isReference();
209  const Item item = lineEdit->completedItem();
210  model->setData(index, isReference, ContactGroupModel::IsReferenceRole);
211  if (isReference) {
212  if (item.isValid()) {
213  model->setData(index.sibling(index.row(), 0), item.id(), Qt::EditRole);
214  }
215  } else {
216  model->setData(index, lineEdit->text(), Qt::EditRole);
217  }
218  }
219 }
220 
221 static bool isLastRow(const QModelIndex &index)
222 {
223  return index.row() == (index.model()->rowCount() - 1);
224 }
225 
226 void ContactGroupEditorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
227 {
228  QStyledItemDelegate::paint(painter, option, index);
229 
230  if (index.column() == 1 && !isLastRow(index)) {
231  d->mIcon.paint(painter, option.rect, Qt::AlignRight);
232  }
233 }
234 
235 QSize ContactGroupEditorDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
236 {
237  Q_UNUSED(option)
238 
239  QSize hint = QStyledItemDelegate::sizeHint(option, index);
240  hint.setHeight(qMax(hint.height(), d->mButtonSize.height()));
241 
242  if (index.column() == 1) {
243  hint.setWidth(hint.width() + d->mButtonSize.width());
244  }
245 
246  return hint;
247 }
248 
249 bool ContactGroupEditorDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
250 {
251  if (index.column() == 1 && !isLastRow(index)) {
252  if (event->type() == QEvent::MouseButtonRelease) {
253  const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
254  QRect buttonRect = d->mItemView->visualRect(index);
255  buttonRect.setLeft(buttonRect.right() - d->mButtonSize.width());
256 
257  if (buttonRect.contains(mouseEvent->pos())) {
258  model->removeRows(index.row(), 1);
259  QTimer::singleShot(0, this, &ContactGroupEditorDelegate::setFirstColumnAsCurrent);
260  return true;
261  }
262  }
263  }
264  return QStyledItemDelegate::editorEvent(event, model, option, index);
265 }
266 
267 void ContactGroupEditorDelegate::completed(QWidget *widget)
268 {
269  Q_EMIT commitData(widget);
270  Q_EMIT closeEditor(widget);
271 }
272 
273 void ContactGroupEditorDelegate::setFirstColumnAsCurrent()
274 {
275  d->mItemView->setCurrentIndex(d->mItemView->model()->index(d->mItemView->currentIndex().row(), 0));
276 }
277 
278 #include "moc_contactgroupeditordelegate_p.cpp"
void clear()
virtual int rowCount(const QModelIndex &parent) const const =0
MouseButtonRelease
bool isValid() const
QEvent::Type type() const const
int right() const const
void setText(const QString &)
void activated(const QString &text)
T value() const const
AlignRight
Id id() const
bool isValid() const const
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const const override
CaseInsensitive
EditRole
bool isEmpty() const const
int row() const const
QAction * hint(const QObject *recvr, const char *slot, QObject *parent)
QFuture< void > filter(Sequence &sequence, KeepFunctor filterFunction)
void textEdited(const QString &text)
virtual bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override
virtual QSize sizeHint() const const
virtual bool removeRows(int row, int count, const QModelIndex &parent)
bool contains(const QRect &rectangle, bool proper) const const
const QAbstractItemModel * model() const const
QStringList toStringList() const const
QVariant data(int role) const const
QModelIndex sibling(int row, int column) const const
int column() const const
bool toBool() const const
virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
QPoint pos() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
QString toString() const const
virtual bool event(QEvent *event) override
void setLeft(int x)
int height() const const
Q_EMITQ_EMIT
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Jun 22 2021 23:08:52 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.