File indexing completed on 2024-11-24 04:39:31
0001 /* 0002 This file is part of Contact Editor. 0003 0004 SPDX-FileCopyrightText: 2009 Tobias Koenig <tokoe@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "displaynameeditwidget.h" 0010 0011 #include <QAbstractItemView> 0012 #include <QEvent> 0013 #include <QHBoxLayout> 0014 #include <QPainter> 0015 #include <QStyledItemDelegate> 0016 0017 #include <KComboBox> 0018 #include <KLocalizedString> 0019 using namespace Akonadi; 0020 // Tries to guess the display type that is used for the passed contact 0021 static DisplayNameEditWidget::DisplayType guessedDisplayType(const KContacts::Addressee &contact) 0022 { 0023 if (contact.formattedName() == (contact.givenName() + QLatin1Char(' ') + contact.familyName())) { 0024 return DisplayNameEditWidget::SimpleName; 0025 } else if (contact.formattedName() == contact.assembledName()) { 0026 return DisplayNameEditWidget::FullName; 0027 } else if (contact.formattedName() == (contact.familyName() + QLatin1StringView(", ") + contact.givenName())) { 0028 return DisplayNameEditWidget::ReverseNameWithComma; 0029 } else if (contact.formattedName() == (contact.familyName() + QLatin1Char(' ') + contact.givenName())) { 0030 return DisplayNameEditWidget::ReverseName; 0031 } else if (contact.formattedName() == contact.organization()) { 0032 return DisplayNameEditWidget::Organization; 0033 } else { 0034 return DisplayNameEditWidget::CustomName; 0035 } 0036 } 0037 0038 class DisplayNameDelegate : public QStyledItemDelegate 0039 { 0040 Q_OBJECT 0041 public: 0042 DisplayNameDelegate(QAbstractItemView *view, QObject *parent = nullptr) 0043 : QStyledItemDelegate(parent) 0044 { 0045 mDescriptions.append(i18n("Short Name")); 0046 mDescriptions.append(i18n("Full Name")); 0047 mDescriptions.append(i18n("Reverse Name with Comma")); 0048 mDescriptions.append(i18n("Reverse Name")); 0049 mDescriptions.append(i18n("Organization")); 0050 mDescriptions.append(i18nc("@item:inlistbox A custom name format", "Custom")); 0051 0052 QFont font = view->font(); 0053 font.setStyle(QFont::StyleItalic); 0054 QFontMetrics metrics(font); 0055 for (const QString &description : std::as_const(mDescriptions)) { 0056 mMaxDescriptionWidth = qMax(mMaxDescriptionWidth, metrics.boundingRect(description).width()); 0057 } 0058 0059 mMaxDescriptionWidth += 2; 0060 } 0061 0062 int maximumDescriptionWidth() const 0063 { 0064 return mMaxDescriptionWidth; 0065 } 0066 0067 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override 0068 { 0069 QStyledItemDelegate::paint(painter, option, index); 0070 const QRect rect(option.rect.width() - mMaxDescriptionWidth, option.rect.y(), mMaxDescriptionWidth, option.rect.height()); 0071 painter->save(); 0072 QFont font(painter->font()); 0073 font.setStyle(QFont::StyleItalic); 0074 painter->setFont(font); 0075 if (option.state & QStyle::State_Selected) { 0076 painter->setPen(option.palette.color(QPalette::Normal, QPalette::BrightText)); 0077 } else { 0078 painter->setPen(option.palette.color(QPalette::Disabled, QPalette::Text)); 0079 } 0080 painter->drawText(rect, Qt::AlignLeft, mDescriptions.at(index.row())); 0081 painter->restore(); 0082 } 0083 0084 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override 0085 { 0086 QSize size = QStyledItemDelegate::sizeHint(option, index); 0087 size.setWidth(size.width() + mMaxDescriptionWidth); 0088 0089 return size; 0090 } 0091 0092 private: 0093 QStringList mDescriptions; 0094 int mMaxDescriptionWidth = 0; 0095 }; 0096 0097 DisplayNameEditWidget::DisplayNameEditWidget(QWidget *parent) 0098 : QWidget(parent) 0099 , mDisplayType(FullName) 0100 { 0101 auto layout = new QHBoxLayout(this); 0102 layout->setContentsMargins({}); 0103 0104 mView = new KComboBox(this); 0105 mView->addItems(QStringList() << QString() << QString() << QString() << QString() << QString() << QString()); 0106 0107 layout->addWidget(mView); 0108 setFocusProxy(mView); 0109 setFocusPolicy(Qt::StrongFocus); 0110 connect(mView, &KComboBox::activated, this, &DisplayNameEditWidget::displayTypeChanged); 0111 0112 auto delegate = new DisplayNameDelegate(mView->view(), this); 0113 mView->view()->setItemDelegate(delegate); 0114 0115 mAdditionalPopupWidth = delegate->maximumDescriptionWidth(); 0116 0117 mViewport = mView->view()->viewport(); 0118 mViewport->installEventFilter(this); 0119 } 0120 0121 DisplayNameEditWidget::~DisplayNameEditWidget() = default; 0122 0123 void DisplayNameEditWidget::setReadOnly(bool readOnly) 0124 { 0125 mView->setEnabled(!readOnly); 0126 } 0127 0128 void DisplayNameEditWidget::setDisplayType(DisplayType type) 0129 { 0130 if ((int)type == -1) { 0131 // guess the used display type 0132 mDisplayType = guessedDisplayType(mContact); 0133 } else { 0134 mDisplayType = type; 0135 } 0136 0137 updateView(); 0138 } 0139 0140 DisplayNameEditWidget::DisplayType DisplayNameEditWidget::displayType() const 0141 { 0142 return mDisplayType; 0143 } 0144 0145 void DisplayNameEditWidget::loadContact(const KContacts::Addressee &contact) 0146 { 0147 mContact = contact; 0148 0149 mDisplayType = guessedDisplayType(mContact); 0150 0151 updateView(); 0152 } 0153 0154 void DisplayNameEditWidget::storeContact(KContacts::Addressee &contact) const 0155 { 0156 contact.setFormattedName(mView->currentText()); 0157 } 0158 0159 void DisplayNameEditWidget::changeName(const KContacts::Addressee &contact) 0160 { 0161 const QString organization = mContact.organization(); 0162 mContact = contact; 0163 mContact.setOrganization(organization); 0164 if (mDisplayType == CustomName) { 0165 mContact.setFormattedName(mView->currentText()); 0166 } 0167 0168 updateView(); 0169 } 0170 0171 void DisplayNameEditWidget::changeOrganization(const QString &organization) 0172 { 0173 mContact.setOrganization(organization); 0174 0175 updateView(); 0176 } 0177 0178 void DisplayNameEditWidget::displayTypeChanged(int type) 0179 { 0180 mDisplayType = (DisplayType)type; 0181 0182 updateView(); 0183 } 0184 0185 bool DisplayNameEditWidget::eventFilter(QObject *object, QEvent *event) 0186 { 0187 if (object == mViewport) { 0188 if (event->type() == QEvent::Show) { 0189 // retrieve the widget that contains the popup view 0190 QWidget *parentWidget = mViewport->parentWidget()->parentWidget(); 0191 0192 int maxWidth = 0; 0193 QFontMetrics metrics(mView->font()); 0194 const int viewCount(mView->count()); 0195 for (int i = 0; i < viewCount; ++i) { 0196 maxWidth = qMax(maxWidth, metrics.boundingRect(mView->itemText(i)).width()); 0197 } 0198 0199 // resize it to show the complete content 0200 parentWidget->resize(maxWidth + mAdditionalPopupWidth + 20, parentWidget->height()); 0201 } 0202 return false; 0203 } 0204 0205 return QWidget::eventFilter(object, event); 0206 } 0207 0208 void DisplayNameEditWidget::updateView() 0209 { 0210 // SimpleName: 0211 mView->setItemText(0, mContact.givenName() + QLatin1Char(' ') + mContact.familyName()); 0212 0213 // FullName: 0214 mView->setItemText(1, mContact.assembledName()); 0215 0216 // ReverseNameWithComma: 0217 mView->setItemText(2, mContact.familyName() + QStringLiteral(", ") + mContact.givenName()); 0218 0219 // ReverseName: 0220 mView->setItemText(3, mContact.familyName() + QLatin1Char(' ') + mContact.givenName()); 0221 0222 // Organization: 0223 mView->setItemText(4, mContact.organization()); 0224 0225 // CustomName: 0226 mView->setItemText(5, mContact.formattedName()); 0227 0228 // delay the state change here, since we might have been called from mView via a signal 0229 QMetaObject::invokeMethod( 0230 this, 0231 [this]() { 0232 setComboBoxEditable(mDisplayType == CustomName); 0233 }, 0234 Qt::QueuedConnection); 0235 0236 mView->setCurrentIndex((int)mDisplayType); 0237 } 0238 0239 void DisplayNameEditWidget::setComboBoxEditable(bool value) 0240 { 0241 mView->setEditable(value); 0242 } 0243 0244 #include "displaynameeditwidget.moc" 0245 0246 #include "moc_displaynameeditwidget.cpp"