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"