Akonadi Contacts

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

KDE's Doxygen guidelines are available online.