File indexing completed on 2024-05-12 05:07:55

0001 /*
0002     SPDX-FileCopyrightText: 2004-2020 Thomas Baumgart <tbaumgart@kde.org>
0003     SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kmymoneyaccountcombo.h"
0008 
0009 // ----------------------------------------------------------------------------
0010 // QT Includes
0011 
0012 #include <QAction>
0013 #include <QEvent>
0014 #include <QKeyEvent>
0015 #include <QLineEdit>
0016 #include <QList>
0017 #include <QRegularExpression>
0018 #include <QTreeView>
0019 
0020 // ----------------------------------------------------------------------------
0021 // KDE Includes
0022 
0023 #include <KLocalizedString>
0024 
0025 // ----------------------------------------------------------------------------
0026 // Project Includes
0027 
0028 #include "mymoneyfile.h"
0029 #include "mymoneyenums.h"
0030 #include "accountsmodel.h"
0031 #include "icons.h"
0032 
0033 class KMyMoneyAccountCombo::Private
0034 {
0035 public:
0036     Private(KMyMoneyAccountCombo* q)
0037         : m_q(q)
0038         , m_popupView(nullptr)
0039         , m_splitAction(nullptr)
0040         , m_inMakeCompletion(false)
0041     {
0042         m_q->setInsertPolicy(QComboBox::NoInsert);
0043         m_q->setMinimumWidth(m_q->fontMetrics().horizontalAdvance(QLatin1Char('W')) * 15);
0044         m_q->setMaxVisibleItems(15);
0045     }
0046 
0047     KMyMoneyAccountCombo*           m_q;
0048     QTreeView*                      m_popupView;
0049     QAction*                        m_splitAction;
0050     QString                         m_lastSelectedAccount;
0051     QModelIndex                     m_lastSelectedIndex;
0052     bool                            m_inMakeCompletion;
0053 
0054     void selectFirstMatchingItem()
0055     {
0056         if(m_popupView) {
0057             QSignalBlocker blocker(m_popupView);
0058             m_popupView->setCurrentIndex(QModelIndex());
0059             const auto rows = m_q->model()->rowCount();
0060             const auto filterModel = qobject_cast<AccountNamesFilterProxyModel*>(m_q->model());
0061             const auto filterExp = filterModel->filterRegularExpression();
0062             for (auto i = 0; i < rows; ++i) {
0063                 QModelIndex childIndex = filterModel->index(i, 0);
0064                 if (filterModel->hasChildren(childIndex)) {
0065                     // search the first match by walking down the first leaf
0066                     do {
0067                         childIndex = filterModel->index(0, 0, childIndex);
0068                         if (filterModel->flags(childIndex) & Qt::ItemIsSelectable) {
0069                             if (filterExp.match(childIndex.data(eMyMoney::Model::AccountFullHierarchyNameRole).toString()).hasMatch()) {
0070                                 break;
0071                             }
0072                         }
0073                     } while (filterModel->hasChildren(childIndex));
0074 
0075                     // make it the current selection if it's selectable
0076                     if (filterModel->flags(childIndex) & Qt::ItemIsSelectable) {
0077                         m_popupView->setCurrentIndex(childIndex);
0078                     }
0079                     break;
0080                 }
0081             }
0082         }
0083     }
0084 
0085     /**
0086      * Find which item has the @a id and return its index
0087      * into the model of the KMyMoneyAccountCombo. In case
0088      * no item is found, an invalid QModelIndex will be
0089      * returned. If the current selection is pointing to
0090      * one of the favorite accounts, the index for the
0091      * corresponding entry in the hierarchy will be returned.
0092      */
0093     QModelIndex findMatchingItem(const QString& id)
0094     {
0095         const auto startRow =
0096             m_q->model()->index(0, 0).data(eMyMoney::Model::Roles::IdRole).toString() == MyMoneyAccount::stdAccName(eMyMoney::Account::Standard::Favorite) ? 1
0097                                                                                                                                                            : 0;
0098         // Note: Without Qt::MatchWrap we might not get results for credit card
0099         const auto list = m_q->model()->match(m_q->model()->index(startRow, 0),
0100                                               eMyMoney::Model::Roles::IdRole,
0101                                               QVariant(id),
0102                                               1,
0103                                               Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap | Qt::MatchRecursive));
0104         if (!list.isEmpty()) {
0105             return list.first();
0106         }
0107         return {};
0108     }
0109 
0110     /**
0111      * Set the current index based on the QModelIndex @a idx.
0112      * While setting the index, the signals sent out by the
0113      * KMyMoneyAccountCombo are blocked.
0114      */
0115     void setCurrentIndex(const QModelIndex& idx)
0116     {
0117         QSignalBlocker blocker(m_q);
0118         m_q->setRootModelIndex(idx.parent());
0119         m_q->setCurrentIndex(idx.row());
0120         m_q->setRootModelIndex(QModelIndex());
0121     }
0122 
0123     void showSplitAction(bool show)
0124     {
0125         if (show && !m_splitAction) {
0126             m_splitAction = m_q->lineEdit()->addAction(Icons::get(Icons::Icon::Split), QLineEdit::TrailingPosition);
0127             // this for some reason does not work and I had to
0128             // add logic to the eventFilter below to catch this
0129             // key-sequence. Left it here, since it does not hurt either.
0130             m_splitAction->setShortcut(QKeySequence(QStringLiteral("Ctrl+ ")));
0131             m_splitAction->setToolTip(i18nc("@info:tooltip icon in category to open the split editor", "Click this icon to open the split editor"));
0132             m_q->connect(m_splitAction, &QAction::triggered, m_q, &KMyMoneyAccountCombo::splitDialogRequest);
0133 
0134         } else if(!show && m_splitAction) {
0135             m_q->lineEdit()->removeAction(m_splitAction);
0136             m_splitAction->deleteLater();
0137             m_splitAction = nullptr;
0138         }
0139     }
0140 };
0141 
0142 
0143 
0144 
0145 
0146 KMyMoneyAccountCombo::KMyMoneyAccountCombo(QSortFilterProxyModel *model, QWidget *parent)
0147     : KComboBox(parent)
0148     , d(new Private(this))
0149 {
0150     init();
0151     setModel(model);
0152 }
0153 
0154 KMyMoneyAccountCombo::KMyMoneyAccountCombo(QWidget *parent)
0155     : KComboBox(parent)
0156     , d(new Private(this))
0157 {
0158     init();
0159 }
0160 
0161 void KMyMoneyAccountCombo::init()
0162 {
0163     setMaxVisibleItems(15);
0164     setSizeAdjustPolicy(QComboBox::AdjustToContents);
0165 }
0166 
0167 KMyMoneyAccountCombo::~KMyMoneyAccountCombo()
0168 {
0169 }
0170 
0171 void KMyMoneyAccountCombo::setEditable(bool isEditable)
0172 {
0173     KComboBox::setEditable(isEditable);
0174     // don't do the standard behavior
0175     if(lineEdit()) {
0176         lineEdit()->setClearButtonEnabled(true);
0177         connect(lineEdit(), &QLineEdit::textEdited, this, &KMyMoneyAccountCombo::makeCompletion, Qt::UniqueConnection);
0178         installEventFilter(this);
0179         d->showSplitAction(true);
0180     }
0181 }
0182 
0183 void KMyMoneyAccountCombo::setSplitActionVisible(bool show)
0184 {
0185     if (lineEdit()) {
0186         d->showSplitAction(show);
0187     }
0188 }
0189 
0190 void KMyMoneyAccountCombo::wheelEvent(QWheelEvent *ev)
0191 {
0192     Q_UNUSED(ev)
0193     // don't change anything with the help of the wheel, yet (due to the tree model)
0194 }
0195 
0196 void KMyMoneyAccountCombo::expandAll()
0197 {
0198     if (d->m_popupView)
0199         d->m_popupView->expandAll();
0200 }
0201 
0202 void KMyMoneyAccountCombo::collapseAll()
0203 {
0204     if (d->m_popupView)
0205         d->m_popupView->collapseAll();
0206 }
0207 
0208 void KMyMoneyAccountCombo::activated()
0209 {
0210     auto accountId = view()->currentIndex().data(eMyMoney::Model::Roles::IdRole).toString();
0211     if (!accountId.isEmpty() && !(lineEdit() && lineEdit()->text().isEmpty())) {
0212         selectItem(view()->currentIndex());
0213     }
0214 }
0215 
0216 bool KMyMoneyAccountCombo::eventFilter(QObject* o, QEvent* e)
0217 {
0218     if(isEditable()) {
0219         if (o == d->m_popupView) {
0220             // propagate all relevant key press events to the lineEdit widget
0221             if(e->type() == QEvent::KeyPress) {
0222                 QKeyEvent* kev = static_cast<QKeyEvent*>(e);
0223                 bool forLineEdit = (kev->text().length() > 0);
0224                 switch(kev->key()) {
0225                 case Qt::Key_Tab:
0226                 case Qt::Key_Backtab: {
0227                     const auto idx = view()->currentIndex();
0228                     const auto accountId = idx.data(eMyMoney::Model::Roles::IdRole).toString();
0229                     if (!accountId.isEmpty()) {
0230                         d->m_lastSelectedAccount = accountId;
0231                         QSignalBlocker blocker(lineEdit());
0232                         lineEdit()->setText(idx.data(eMyMoney::Model::Roles::AccountFullNameRole).toString());
0233                         d->setCurrentIndex(d->findMatchingItem(accountId));
0234                         Q_EMIT accountSelected(accountId);
0235                     }
0236                     hidePopup();
0237                     break;
0238                 }
0239 
0240                 case Qt::Key_Escape:
0241                 case Qt::Key_Up:
0242                 case Qt::Key_Down:
0243                     forLineEdit = false;
0244                     break;
0245                 default:
0246                     break;
0247                 }
0248                 if(forLineEdit) {
0249                     return lineEdit()->event(e);
0250                 }
0251             } else if(e->type() == QEvent::KeyRelease) {
0252                 QKeyEvent* kev = static_cast<QKeyEvent*>(e);
0253                 switch(kev->key()) {
0254                 case Qt::Key_Escape:
0255                     hidePopup();
0256                     return true;
0257 
0258                 case Qt::Key_Enter:
0259                 case Qt::Key_Return:
0260                     activated();
0261                     hidePopup();
0262                     break;
0263                 default:
0264                     break;
0265                 }
0266 
0267             } else if(e->type() == QEvent::FocusOut) {
0268                 hidePopup();
0269             }
0270 
0271         } else if(o == this) {
0272             if(e->type() == QEvent::KeyPress) {
0273                 const auto kev = static_cast<QKeyEvent*>(e);
0274                 if (kev->modifiers() & Qt::ControlModifier && kev->key() == Qt::Key_Space) {
0275                     Q_EMIT splitDialogRequest();
0276                     return true;
0277                 }
0278             }
0279         }
0280     }
0281     return KComboBox::eventFilter(o, e);
0282 }
0283 
0284 void KMyMoneyAccountCombo::setSelected(const QString& id)
0285 {
0286     if (id.isEmpty()) {
0287         d->m_lastSelectedAccount.clear();
0288         d->m_popupView->selectionModel()->clearSelection();
0289         d->m_popupView->setCurrentIndex(QModelIndex());
0290         setRootModelIndex(QModelIndex());
0291         setCurrentIndex(-1);
0292         Q_EMIT accountSelected(id);
0293         return;
0294     }
0295 
0296     if (id == d->m_lastSelectedAccount) {
0297         // nothing to do
0298         return;
0299     }
0300 
0301     // make sure, we have all items available for search
0302     if(isEditable()) {
0303         lineEdit()->clear();
0304     }
0305 
0306     // reset the filter of the model
0307     auto* filterModel = qobject_cast<QSortFilterProxyModel*>(model());
0308     filterModel->setFilterFixedString(QString());
0309 
0310     const auto idx = d->findMatchingItem(id);
0311     if (idx.isValid()) {
0312         // make sure the popup is closed from here on
0313         hidePopup();
0314         d->m_lastSelectedAccount = id;
0315         d->setCurrentIndex(idx);
0316         Q_EMIT accountSelected(id);
0317     }
0318 }
0319 
0320 const QString& KMyMoneyAccountCombo::getSelected() const
0321 {
0322     return d->m_lastSelectedAccount;
0323 }
0324 
0325 void KMyMoneyAccountCombo::setModel(QSortFilterProxyModel *model)
0326 {
0327     // CAUTION! Assumption is being made that AccountName column number is always 0
0328     if (AccountsModel::Column::AccountName != 0) {
0329         qFatal("AccountsModel::Column::AccountName must be 0 in accountsmodel.h");
0330     }
0331 
0332     // since we create a new popup view, we get rid of an existing one
0333     delete d->m_popupView;
0334 
0335     // call base class implementation
0336     KComboBox::setModel(model);
0337 
0338     // setup filtering criteria
0339     model->setFilterKeyColumn(AccountsModel::Column::AccountName);
0340     model->setFilterRole(eMyMoney::Model::Roles::AccountFullHierarchyNameRole);
0341 
0342     // create popup view, attach model and allow to select a single item
0343     d->m_popupView = new QTreeView(this);
0344     d->m_popupView->setModel(model);
0345     d->m_popupView->setSelectionMode(QAbstractItemView::SingleSelection);
0346     setView(d->m_popupView);
0347 
0348     // setup view parameters
0349     d->m_popupView->setHeaderHidden(true);
0350     d->m_popupView->setRootIsDecorated(true);
0351     d->m_popupView->setAlternatingRowColors(true);
0352     d->m_popupView->setAnimated(true);
0353 
0354     d->m_popupView->expandAll();
0355     connect(d->m_popupView, &QTreeView::activated, this, &KMyMoneyAccountCombo::selectItem);
0356 
0357     d->m_popupView->installEventFilter(this);
0358 
0359     // for some unknown reason, the first selection with the mouse (not with the keyboard)
0360     // after the qlineedit had been cleared using the clear button does not trigger the
0361     // activated() signal of d->m_popupView. This is a workaround to catch this scenario
0362     // and still get valid settings.
0363     connect(this, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [&](int index) {
0364         if (index != -1) {
0365             const auto idx = d->m_popupView->currentIndex();
0366             if (idx.isValid()) {
0367                 if (d->m_lastSelectedAccount.isEmpty() || (idx.data(eMyMoney::Model::IdRole).toString() != d->m_lastSelectedAccount)) {
0368                     selectItem(idx);
0369                 }
0370             }
0371         }
0372     });
0373 
0374     if(isEditable()) {
0375         connect(lineEdit(), &QLineEdit::textEdited, this, &KMyMoneyAccountCombo::makeCompletion, Qt::UniqueConnection);
0376     } else {
0377         connect(this, static_cast<void (KComboBox::*)(int)>(&KMyMoneyAccountCombo::KComboBox::activated), this, &KMyMoneyAccountCombo::activated);
0378     }
0379 
0380     // since the standard QComboBox implementation does not deal with data changes in tree models
0381     // we make sure that a possible account name change gets propagated to our widget
0382     connect(model, &QAbstractItemModel::dataChanged, this, [&](const QModelIndex& topLeft, const QModelIndex& bottomRight) {
0383         const auto currentIdx = currentIndex();
0384         if (currentIdx >= topLeft.row() && currentIdx <= bottomRight.row()) {
0385             setRootModelIndex(topLeft.parent());
0386             const auto accountId = itemData(currentIdx, eMyMoney::Model::IdRole).toString();
0387             const QString text = itemText(currentIdx);
0388             setRootModelIndex(QModelIndex());
0389             // we need to check for the account id because the row is
0390             // ambiguous as it may come from a different sub-tree in the model
0391             if (accountId == d->m_lastSelectedAccount) {
0392                 if (lineEdit()) {
0393                     lineEdit()->setText(text);
0394                     auto dummyResizeEvent = QResizeEvent(QSize(), QSize());
0395                     resizeEvent(&dummyResizeEvent);
0396                 } else {
0397                     Q_EMIT currentTextChanged(text);
0398                 }
0399                 update();
0400             }
0401         }
0402     });
0403 }
0404 
0405 void KMyMoneyAccountCombo::selectItem(const QModelIndex& index)
0406 {
0407     if (d->m_inMakeCompletion)
0408         return;
0409 
0410     if (!index.isValid())
0411         return;
0412 
0413     if (index.model() != model()) {
0414         qDebug() << "KMyMoneyAccountCombo::selectItem called with wrong model" << index;
0415     }
0416     if (index.model()->flags(index) & Qt::ItemIsSelectable) {
0417         // delay the call until the next time in the event loop
0418         QMetaObject::invokeMethod(this, "setSelected", Qt::QueuedConnection, Q_ARG(QString, index.data(eMyMoney::Model::Roles::IdRole).toString()));
0419     }
0420 }
0421 
0422 void KMyMoneyAccountCombo::makeCompletion(const QString& txt)
0423 {
0424     if(!d->m_inMakeCompletion) {
0425         d->m_inMakeCompletion = true;
0426         if (txt.isEmpty()) {
0427             setSelected(QString());
0428         } else {
0429             AccountNamesFilterProxyModel* filterModel = qobject_cast<AccountNamesFilterProxyModel*>(model());
0430 
0431             if(filterModel) {
0432                 const auto completionStr = QStringLiteral(".*");
0433                 // for some reason it helps to avoid internal errors if we
0434                 // clear the filter before setting it to a new value
0435                 filterModel->setFilterFixedString(QString());
0436                 if (txt.contains(MyMoneyFile::AccountSeparator) == 0) {
0437                     const auto filterString = QString::fromLatin1("%1%2%3").arg(completionStr, QRegularExpression::escape(txt), completionStr);
0438                     filterModel->setFilterRegularExpression(QRegularExpression(filterString, QRegularExpression::CaseInsensitiveOption));
0439                 } else {
0440                     QStringList parts = txt.split(MyMoneyFile::AccountSeparator /*, Qt::SkipEmptyParts */);
0441                     QString pattern;
0442                     QStringList::iterator it;
0443                     for (it = parts.begin(); it != parts.end(); ++it) {
0444                         if (pattern.length() > 1)
0445                             pattern += MyMoneyFile::AccountSeparator;
0446                         pattern += QRegularExpression::escape(QString(*it).trimmed()) + completionStr;
0447                     }
0448                     filterModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
0449                     // if we don't have a match, we try it again, but this time
0450                     // we add a wildcard for the top level
0451                     if (filterModel->visibleItems() == 0) {
0452                         // for some reason it helps to avoid internal errors if we
0453                         // clear the filter before setting it to a new value
0454                         filterModel->setFilterFixedString(QString());
0455                         pattern = pattern.prepend(completionStr + MyMoneyFile::AccountSeparator);
0456                         filterModel->setFilterRegularExpression(QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
0457                     }
0458                 }
0459 
0460                 // if nothing is shown, we might as well close the popup
0461                 switch(filterModel->visibleItems()) {
0462                 case 0:
0463                     hidePopup();
0464                     break;
0465                 default:
0466                     expandAll();
0467                     showPopup();
0468                     d->selectFirstMatchingItem();
0469                     break;
0470                 }
0471 
0472                 // we don't have a selection since the user edits the name
0473                 d->m_lastSelectedAccount.clear();
0474 
0475                 // keep current text in edit widget no matter what
0476                 QSignalBlocker blocker(lineEdit());
0477                 lineEdit()->setText(txt);
0478             }
0479         }
0480         d->m_inMakeCompletion = false;
0481     }
0482 }
0483 
0484 void KMyMoneyAccountCombo::showPopup()
0485 {
0486     if(d->m_popupView) {
0487         d->m_popupView->show();
0488     }
0489     KComboBox::showPopup();
0490 }
0491 
0492 void KMyMoneyAccountCombo::hidePopup()
0493 {
0494     if(d->m_popupView) {
0495         d->m_popupView->hide();
0496     }
0497     KComboBox::hidePopup();
0498 }
0499 
0500 QTreeView* KMyMoneyAccountCombo::popup() const
0501 {
0502     return d->m_popupView;
0503 }
0504 
0505 void KMyMoneyAccountCombo::clearSelection()
0506 {
0507     d->m_lastSelectedAccount.clear();
0508     setCurrentIndex(-1);
0509     clearEditText();
0510 }
0511 
0512 class KMyMoneyAccountComboSplitHelperPrivate
0513 {
0514     Q_DISABLE_COPY(KMyMoneyAccountComboSplitHelperPrivate)
0515     Q_DECLARE_PUBLIC(KMyMoneyAccountComboSplitHelper)
0516 
0517 public:
0518     KMyMoneyAccountComboSplitHelperPrivate(KMyMoneyAccountComboSplitHelper* qq)
0519         : q_ptr(qq)
0520         , m_accountCombo(nullptr)
0521         , m_splitModel(nullptr)
0522         , m_norecursive(false)
0523     {
0524     }
0525 
0526     KMyMoneyAccountComboSplitHelper*  q_ptr;
0527     QComboBox*                        m_accountCombo;
0528     QAbstractItemModel*               m_splitModel;
0529     bool                              m_norecursive;
0530 };
0531 
0532 
0533 KMyMoneyAccountComboSplitHelper::KMyMoneyAccountComboSplitHelper(QComboBox* accountCombo, QAbstractItemModel* model)
0534     : QObject(accountCombo)
0535     , d_ptr(new KMyMoneyAccountComboSplitHelperPrivate(this))
0536 {
0537     Q_D(KMyMoneyAccountComboSplitHelper);
0538     d->m_accountCombo = accountCombo;
0539     d->m_splitModel = model;
0540 
0541     connect(model, &QAbstractItemModel::dataChanged, this, &KMyMoneyAccountComboSplitHelper::updateWidget);
0542     connect(model, &QAbstractItemModel::rowsRemoved, this, &KMyMoneyAccountComboSplitHelper::updateWidget, Qt::QueuedConnection);
0543     connect(model, &QAbstractItemModel::modelReset, this, &KMyMoneyAccountComboSplitHelper::updateWidget, Qt::QueuedConnection);
0544     connect(model, &QAbstractItemModel::destroyed, this, &KMyMoneyAccountComboSplitHelper::modelDestroyed);
0545 
0546     accountCombo->installEventFilter(this);
0547     if (accountCombo->lineEdit()) {
0548         accountCombo->lineEdit()->installEventFilter(this);
0549     }
0550     updateWidget();
0551 }
0552 
0553 KMyMoneyAccountComboSplitHelper::~KMyMoneyAccountComboSplitHelper()
0554 {
0555 }
0556 
0557 bool KMyMoneyAccountComboSplitHelper::eventFilter(QObject* watched, QEvent* event)
0558 {
0559     Q_D(KMyMoneyAccountComboSplitHelper);
0560     if (d->m_splitModel && (d->m_splitModel->rowCount() > 1)) {
0561         const auto type = event->type();
0562         if (watched == d->m_accountCombo) {
0563             if (type == QEvent::FocusIn) {
0564                 // select the complete text (which is readonly)
0565                 // to signal focus in the lineedit widget to the user
0566                 const auto lineEdit = d->m_accountCombo->lineEdit();
0567                 if (lineEdit) {
0568                     lineEdit->end(false);
0569                     lineEdit->home(true);
0570                 }
0571             }
0572         }
0573         if ((type == QEvent::MouseButtonPress)
0574                 || (type == QEvent::MouseButtonRelease)
0575                 || (type == QEvent::MouseButtonDblClick)) {
0576             // suppress opening the combo box
0577             // or selecting text in the lineedit
0578             return true;
0579         }
0580         if (type == QEvent::KeyPress) {
0581             auto kev = static_cast<QKeyEvent*>(event);
0582             // swallow all keypress except Ctrl+Space, Return,
0583             // Enter, Tab, BackTab and Esc
0584             switch(kev->key()) {
0585             case Qt::Key_Enter:
0586             case Qt::Key_Return:
0587             case Qt::Key_Escape:
0588             case Qt::Key_Tab:
0589             case Qt::Key_Backtab:
0590                 return false;
0591 
0592             case Qt::Key_Space:
0593                 return !(kev->modifiers() & Qt::ControlModifier);
0594 
0595             default:
0596                 break;
0597             }
0598             return true;
0599         }
0600 
0601     }
0602     return QObject::eventFilter(watched, event);
0603 }
0604 
0605 void KMyMoneyAccountComboSplitHelper::modelDestroyed()
0606 {
0607     Q_D(KMyMoneyAccountComboSplitHelper);
0608     d->m_splitModel = nullptr;
0609 }
0610 
0611 void KMyMoneyAccountComboSplitHelper::updateWidget()
0612 {
0613     Q_D(KMyMoneyAccountComboSplitHelper);
0614     // sanity check
0615     if (!d->m_accountCombo || !d->m_splitModel || d->m_norecursive)
0616         return;
0617 
0618     d->m_norecursive = true;
0619 
0620     QModelIndexList indexes;
0621     bool disabled = false;
0622 
0623     const auto rows = d->m_splitModel->rowCount();
0624     const auto accountCombo = qobject_cast<KMyMoneyAccountCombo*>(d->m_accountCombo);
0625     switch (rows) {
0626     case 0:
0627         d->m_accountCombo->setCurrentIndex(-1);
0628         d->m_accountCombo->setCurrentText(QString());
0629         break;
0630     case 1:
0631         indexes = d->m_accountCombo->model()->match(d->m_accountCombo->model()->index(0,0),
0632                   eMyMoney::Model::IdRole,
0633                   d->m_splitModel->index(0, 0).data(eMyMoney::Model::SplitAccountIdRole).toString(),
0634                   1,
0635                   Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap | Qt::MatchRecursive));
0636         if (indexes.isEmpty()) {
0637             if (accountCombo) {
0638                 accountCombo->clearSelection();
0639             } else {
0640                 d->m_accountCombo->setCurrentIndex(-1);
0641                 d->m_accountCombo->setCurrentText(QString());
0642             }
0643         } else {
0644             const auto idx = indexes.first();
0645             if (accountCombo) {
0646                 const auto accountId = idx.data(eMyMoney::Model::IdRole).toString();
0647                 accountCombo->setSelected(accountId);
0648             } else {
0649                 QSignalBlocker comboBoxBlocker(d->m_accountCombo);
0650                 d->m_accountCombo->setRootModelIndex(idx.parent());
0651                 d->m_accountCombo->setCurrentIndex(idx.row());
0652                 d->m_accountCombo->setRootModelIndex(QModelIndex());
0653             }
0654         }
0655         break;
0656     default:
0657     {
0658         QSignalBlocker lineEditBlocker(d->m_accountCombo->lineEdit());
0659         QString txt, sep;
0660         for (int row = 0; row < rows; ++row) {
0661             const auto idx = d->m_splitModel->index(row, 0);
0662             indexes = d->m_accountCombo->model()->match(d->m_accountCombo->model()->index(0,0),
0663                       eMyMoney::Model::IdRole,
0664                       idx.data(eMyMoney::Model::SplitAccountIdRole).toString(),
0665                       1,
0666                       Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap | Qt::MatchRecursive));
0667             if (!indexes.isEmpty()) {
0668                 txt += sep + indexes.first().data(eMyMoney::Model::AccountNameRole).toString();
0669                 sep = QStringLiteral(", ");
0670             }
0671         }
0672         d->m_accountCombo->lineEdit()->setText(txt);
0673         d->m_accountCombo->lineEdit()->home(false);
0674         disabled = true;
0675     }
0676     break;
0677     }
0678     d->m_accountCombo->hidePopup();
0679     d->m_accountCombo->lineEdit()->setReadOnly(disabled);
0680 
0681     Q_EMIT accountComboEnabled(!disabled);
0682     Q_EMIT accountComboDisabled(disabled);
0683 
0684     d->m_norecursive = false;
0685 }