File indexing completed on 2024-11-17 04:40:41
0001 /* 0002 This file is part of Akonadi Contact. 0003 0004 SPDX-FileCopyrightText: 2009 Tobias Koenig <tokoe@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "contactgroupeditordelegate_p.h" 0010 0011 #include "contactcompletionmodel_p.h" 0012 #include "contactgroupmodel_p.h" 0013 0014 #include <Akonadi/EntityTreeModel> 0015 #include <KComboBox> 0016 0017 #include <QAbstractItemView> 0018 #include <QCompleter> 0019 #include <QIcon> 0020 #include <QMouseEvent> 0021 #include <QSortFilterProxyModel> 0022 #include <QTimer> 0023 0024 using namespace Akonadi; 0025 0026 /** 0027 * @short Model that filters out all contacts without email address. 0028 */ 0029 class ContactsWithEmailFilterModel : public QSortFilterProxyModel 0030 { 0031 public: 0032 ContactsWithEmailFilterModel(QObject *parent) 0033 : QSortFilterProxyModel(parent) 0034 { 0035 // contact names should be sorted correctly 0036 setSortLocaleAware(true); 0037 } 0038 0039 protected: 0040 bool filterAcceptsRow(int row, const QModelIndex &parent) const override 0041 { 0042 const QModelIndex index = sourceModel()->index(row, Akonadi::ContactCompletionModel::EmailColumn, parent); 0043 if (!index.isValid()) { 0044 return false; 0045 } 0046 0047 return !index.data().toString().isEmpty(); 0048 } 0049 }; 0050 0051 ContactLineEdit::ContactLineEdit(bool isReference, Akonadi::ContactCompletionModel::Columns column, QWidget *parent) 0052 : QLineEdit(parent) 0053 , mIsReference(isReference) 0054 { 0055 setFrame(false); 0056 0057 auto filter = new ContactsWithEmailFilterModel(this); 0058 filter->setSourceModel(Akonadi::ContactCompletionModel::self()); 0059 0060 auto completer = new QCompleter(filter, this); 0061 completer->setCompletionColumn(column); 0062 completer->setCaseSensitivity(Qt::CaseInsensitive); 0063 connect(completer, qOverload<const QModelIndex &>(&QCompleter::activated), this, qOverload<const QModelIndex &>(&ContactLineEdit::completed)); 0064 0065 setCompleter(completer); 0066 0067 connect(this, &QLineEdit::textEdited, this, &ContactLineEdit::slotTextEdited); 0068 } 0069 0070 bool ContactLineEdit::isReference() const 0071 { 0072 return mIsReference; 0073 } 0074 0075 Akonadi::Item ContactLineEdit::completedItem() const 0076 { 0077 return mItem; 0078 } 0079 0080 void ContactLineEdit::completed(const QModelIndex &index) 0081 { 0082 if (index.isValid()) { 0083 mItem = index.data(Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>(); 0084 mIsReference = true; 0085 } else { 0086 mItem = Akonadi::Item(); 0087 mIsReference = false; 0088 } 0089 0090 Q_EMIT completed(this); 0091 } 0092 0093 void ContactLineEdit::slotTextEdited() 0094 { 0095 // if the user has edited the text, we break up the reference 0096 mIsReference = false; 0097 } 0098 0099 class Akonadi::ContactGroupEditorDelegatePrivate 0100 { 0101 public: 0102 ContactGroupEditorDelegatePrivate() 0103 : mButtonSize(16, 16) 0104 , mIcon(QIcon::fromTheme(QStringLiteral("list-remove"))) 0105 { 0106 } 0107 0108 const QSize mButtonSize; 0109 const QIcon mIcon; 0110 QAbstractItemView *mItemView = nullptr; 0111 }; 0112 0113 ContactGroupEditorDelegate::ContactGroupEditorDelegate(QAbstractItemView *view, QObject *parent) 0114 : QStyledItemDelegate(parent) 0115 , d(new ContactGroupEditorDelegatePrivate) 0116 { 0117 d->mItemView = view; 0118 } 0119 0120 ContactGroupEditorDelegate::~ContactGroupEditorDelegate() = default; 0121 0122 QWidget *ContactGroupEditorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const 0123 { 0124 const bool isReference = index.data(Akonadi::ContactGroupModel::IsReferenceRole).toBool(); 0125 Q_UNUSED(option) 0126 if (index.column() == 0) { 0127 auto edit = new ContactLineEdit(isReference, Akonadi::ContactCompletionModel::NameAndEmailColumn, parent); 0128 connect(edit, qOverload<QWidget *>(&ContactLineEdit::completed), this, &ContactGroupEditorDelegate::completed); 0129 0130 return edit; 0131 } else { 0132 if (index.data(Akonadi::ContactGroupModel::IsReferenceRole).toBool()) { 0133 auto comboBox = new KComboBox(parent); 0134 comboBox->setFrame(false); 0135 comboBox->setAutoFillBackground(true); 0136 return comboBox; 0137 } else { 0138 auto edit = new ContactLineEdit(isReference, Akonadi::ContactCompletionModel::NameAndEmailColumn, parent); 0139 connect(edit, qOverload<QWidget *>(&ContactLineEdit::completed), this, &ContactGroupEditorDelegate::completed); 0140 return edit; 0141 } 0142 } 0143 } 0144 0145 void ContactGroupEditorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const 0146 { 0147 if (index.data(Akonadi::ContactGroupModel::IsReferenceRole).toBool()) { 0148 if (index.column() == 0) { 0149 auto lineEdit = qobject_cast<QLineEdit *>(editor); 0150 if (!lineEdit) { 0151 return; 0152 } 0153 0154 lineEdit->setText(index.data(Qt::EditRole).toString()); 0155 } else { 0156 auto comboBox = qobject_cast<KComboBox *>(editor); 0157 if (!comboBox) { 0158 return; 0159 } 0160 0161 const QStringList emails = index.data(Akonadi::ContactGroupModel::AllEmailsRole).toStringList(); 0162 comboBox->clear(); 0163 comboBox->addItems(emails); 0164 comboBox->setCurrentIndex(comboBox->findText(index.data(Qt::EditRole).toString())); 0165 } 0166 } else { 0167 auto lineEdit = qobject_cast<QLineEdit *>(editor); 0168 if (!lineEdit) { 0169 return; 0170 } 0171 0172 lineEdit->setText(index.data(Qt::EditRole).toString()); 0173 } 0174 } 0175 0176 void ContactGroupEditorDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const 0177 { 0178 if (index.data(Akonadi::ContactGroupModel::IsReferenceRole).toBool()) { 0179 if (index.column() == 0) { 0180 auto lineEdit = static_cast<ContactLineEdit *>(editor); 0181 0182 const bool isReference = lineEdit->isReference(); 0183 const Akonadi::Item item = lineEdit->completedItem(); 0184 model->setData(index, isReference, Akonadi::ContactGroupModel::IsReferenceRole); 0185 if (isReference) { 0186 if (item.isValid()) { 0187 model->setData(index, item.id(), Qt::EditRole); 0188 } 0189 } else { 0190 model->setData(index, lineEdit->text(), Qt::EditRole); 0191 } 0192 } 0193 0194 if (index.column() == 1) { 0195 auto comboBox = qobject_cast<KComboBox *>(editor); 0196 if (!comboBox) { 0197 return; 0198 } 0199 0200 model->setData(index, comboBox->currentText(), Qt::EditRole); 0201 } 0202 } else { 0203 auto lineEdit = static_cast<ContactLineEdit *>(editor); 0204 0205 const bool isReference = lineEdit->isReference(); 0206 const Akonadi::Item item = lineEdit->completedItem(); 0207 model->setData(index, isReference, Akonadi::ContactGroupModel::IsReferenceRole); 0208 if (isReference) { 0209 if (item.isValid()) { 0210 model->setData(index.sibling(index.row(), 0), item.id(), Qt::EditRole); 0211 } 0212 } else { 0213 model->setData(index, lineEdit->text(), Qt::EditRole); 0214 } 0215 } 0216 } 0217 0218 static bool isLastRow(const QModelIndex &index) 0219 { 0220 return index.row() == (index.model()->rowCount() - 1); 0221 } 0222 0223 void ContactGroupEditorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 0224 { 0225 QStyledItemDelegate::paint(painter, option, index); 0226 0227 if (index.column() == 1 && !isLastRow(index)) { 0228 QRect buttonRect = d->mItemView->visualRect(index); 0229 buttonRect.setLeft(buttonRect.right() - d->mButtonSize.width()); 0230 d->mIcon.paint(painter, buttonRect, Qt::AlignRight); 0231 } 0232 } 0233 0234 QSize ContactGroupEditorDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const 0235 { 0236 Q_UNUSED(option) 0237 0238 QSize hint = QStyledItemDelegate::sizeHint(option, index); 0239 hint.setHeight(qMax(hint.height(), d->mButtonSize.height())); 0240 0241 if (index.column() == 1) { 0242 hint.setWidth(hint.width() + d->mButtonSize.width()); 0243 } 0244 0245 return hint; 0246 } 0247 0248 bool ContactGroupEditorDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) 0249 { 0250 if (index.column() == 1 && !isLastRow(index)) { 0251 if (event->type() == QEvent::MouseButtonRelease) { 0252 const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); 0253 QRect buttonRect = d->mItemView->visualRect(index); 0254 buttonRect.setLeft(buttonRect.right() - d->mButtonSize.width()); 0255 if (buttonRect.contains(mouseEvent->pos())) { 0256 model->removeRows(index.row(), 1); 0257 QTimer::singleShot(0, this, &ContactGroupEditorDelegate::setFirstColumnAsCurrent); 0258 return true; 0259 } 0260 } 0261 } 0262 return QStyledItemDelegate::editorEvent(event, model, option, index); 0263 } 0264 0265 void ContactGroupEditorDelegate::completed(QWidget *widget) 0266 { 0267 Q_EMIT commitData(widget); 0268 Q_EMIT closeEditor(widget); 0269 } 0270 0271 void ContactGroupEditorDelegate::setFirstColumnAsCurrent() 0272 { 0273 d->mItemView->setCurrentIndex(d->mItemView->model()->index(d->mItemView->currentIndex().row(), 0)); 0274 } 0275 0276 #include "moc_contactgroupeditordelegate_p.cpp"