/*
    SPDX-FileCopyrightText: 2000-2002 Michael Edwardes <mte@users.sourceforge.net>
    SPDX-FileCopyrightText: 2000-2002 Javier Campos Morales <javi_c@users.sourceforge.net>
    SPDX-FileCopyrightText: 2000-2002 Felix Rodriguez <frodriguez@users.sourceforge.net>
    SPDX-FileCopyrightText: 2000-2002 John C <thetacoturtle@users.sourceforge.net>
    SPDX-FileCopyrightText: 2000-2002 Thomas Baumgart <ipwizard@users.sourceforge.net>
    SPDX-FileCopyrightText: 2000-2002 Kevin Tambascio <ktambascio@users.sourceforge.net>
    SPDX-FileCopyrightText: 2000-2002 Andreas Nicolai <Andreas.Nicolai@gmx.net>
    SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "kpayeesview_p.h"

// ----------------------------------------------------------------------------
// QT Includes

#include <QMap>
#include <QList>
#include <QTimer>
#include <QMenu>
#include <QDesktopServices>
#include <QIcon>

// ----------------------------------------------------------------------------
// KDE Includes

#include <KLocalizedString>
#include <KMessageBox>
#include <KGuiItem>
#include <KHelpClient>
#include <KSharedConfig>
#include <KListWidgetSearchLine>

// ----------------------------------------------------------------------------
// Project Includes

#include <config-kmymoney.h>
#include "ui_kpayeesview.h"
#include "kmymoneyviewbase_p.h"
#include "kpayeeidentifierview.h"
#include "mymoneypayee.h"
#include "mymoneyexception.h"
#include "mymoneyfile.h"
#include "mymoneyaccount.h"
#include "mymoneymoney.h"
#include "mymoneytransactionfilter.h"
#include "kmymoneysettings.h"
#include "models.h"
#include "accountsmodel.h"
#include "mymoneysecurity.h"
#include "mymoneycontact.h"
#include "mymoneyprice.h"
#include "mymoneysplit.h"
#include "mymoneytransaction.h"
#include "icons/icons.h"
#include "transaction.h"
#include "widgetenums.h"
#include "mymoneyenums.h"
#include "modelenums.h"
#include "menuenums.h"

using namespace Icons;

// *** KPayeesView Implementation ***

KPayeesView::KPayeesView(QWidget *parent) :
    KMyMoneyViewBase(*new KPayeesViewPrivate(this), parent)
{
    connect(pActions[eMenu::Action::NewPayee],    &QAction::triggered, this, &KPayeesView::slotNewPayee);
    connect(pActions[eMenu::Action::RenamePayee], &QAction::triggered, this, &KPayeesView::slotRenamePayee);
    connect(pActions[eMenu::Action::DeletePayee], &QAction::triggered, this, &KPayeesView::slotDeletePayee);
    connect(pActions[eMenu::Action::MergePayee],  &QAction::triggered, this, &KPayeesView::slotMergePayee);
}

KPayeesView::~KPayeesView()
{
}

void KPayeesView::slotChooseDefaultAccount()
{
    Q_D(KPayeesView);
    MyMoneyFile* file = MyMoneyFile::instance();
    QMap<QString, int> account_count;

    KMyMoneyRegister::RegisterItem* item = d->ui->m_register->firstItem();
    while (item) {
        //only walk through selectable items. eg. transactions and not group markers
        if (item->isSelectable()) {
            auto t = dynamic_cast<KMyMoneyRegister::Transaction*>(item);
            if (!t)
                return;
            MyMoneySplit s = t->transaction().splitByPayee(d->m_payee.id());
            const MyMoneyAccount& acc = file->account(s.accountId());

            if (s.action() != MyMoneySplit::actionName(eMyMoney::Split::Action::Amortization)
                    && acc.accountType() != eMyMoney::Account::Type::AssetLoan
                    && !file->isTransfer(t->transaction())
                    && t->transaction().splitCount() == 2) {
                MyMoneySplit s0 = t->transaction().splitByAccount(s.accountId(), false);
                if (account_count.contains(s0.accountId())) {
                    account_count[s0.accountId()]++;
                } else {
                    account_count[s0.accountId()] = 1;
                }
            }
        }
        item = item->nextItem();
    }

    QMap<QString, int>::Iterator most_frequent, iter;
    most_frequent = account_count.begin();
    for (iter = account_count.begin(); iter != account_count.end(); ++iter) {
        if (iter.value() > most_frequent.value()) {
            most_frequent = iter;
        }
    }

    if (most_frequent != account_count.end()) {
        d->ui->checkEnableDefaultCategory->setChecked(true);
        d->ui->comboDefaultCategory->setSelected(most_frequent.key());
        d->setDirty(true);
    }
}

void KPayeesView::slotClosePayeeIdentifierSource()
{
    Q_D(KPayeesView);
    if (!d->m_needLoad)
        d->ui->payeeIdentifiers->closeSource();
}

void KPayeesView::slotSelectByVariant(const QVariantList& variant, eView::Intent intent)
{
    switch (intent) {
    case eView::Intent::ShowPayee:
        if (variant.count() == 3)
            slotSelectPayeeAndTransaction(variant.at(0).toString(), variant.at(1).toString(), variant.at(2).toString());
        break;
    default:
        break;
    }
}


void KPayeesView::slotStartRename(QListWidgetItem* item)
{
    Q_D(KPayeesView);
    d->m_allowEditing = true;
    d->ui->m_payeesList->editItem(item);
}

// This variant is only called when a single payee is selected and renamed.
void KPayeesView::slotRenameSinglePayee(QListWidgetItem* p)
{
    Q_D(KPayeesView);
    //if there is no current item selected, exit
    if (d->m_allowEditing == false || !d->ui->m_payeesList->currentItem() || p != d->ui->m_payeesList->currentItem())
        return;

    //qDebug() << "[KPayeesView::slotRenamePayee]";
    // create a copy of the new name without appended whitespaces
    QString new_name = p->text();
    if (d->m_payee.name() != new_name) {
        MyMoneyFileTransaction ft;
        try {
            // check if we already have a payee with the new name
            try {
                // this function call will throw an exception, if the payee
                // hasn't been found.
                MyMoneyFile::instance()->payeeByName(new_name);
                // the name already exists, ask the user whether he's sure to keep the name
                if (KMessageBox::questionYesNo(this,
                                               i18n("A payee with the name '%1' already exists. It is not advisable to have "
                                                    "multiple payees with the same identification name. Are you sure you would like "
                                                    "to rename the payee?", new_name)) != KMessageBox::Yes) {
                    p->setText(d->m_payee.name());
                    return;
                }
            } catch (const MyMoneyException &) {
                // all ok, the name is unique
            }

            d->m_payee.setName(new_name);
            d->m_newName = new_name;
            MyMoneyFile::instance()->modifyPayee(d->m_payee);

            // the above call to modifyPayee will reload the view so
            // all references and pointers to the view have to be
            // re-established.

            // make sure, that the record is visible even if it moved
            // out of sight due to the rename operation
            d->ensurePayeeVisible(d->m_payee.id());

            ft.commit();

        } catch (const MyMoneyException &e) {
            KMessageBox::detailedSorry(this, i18n("Unable to modify payee"), QString::fromLatin1(e.what()));
        }
    } else {
        p->setText(new_name);
    }
}

void KPayeesView::slotSelectPayee(QListWidgetItem* cur, QListWidgetItem* prev)
{
    Q_D(KPayeesView);
    Q_UNUSED(cur);
    Q_UNUSED(prev);

    d->m_allowEditing = false;
}

void KPayeesView::slotSelectPayee()
{
    Q_D(KPayeesView);
    // check if the content of a currently selected payee was modified
    // and ask to store the data
    if (d->isDirty()) {
        if (KMessageBox::questionYesNo(this,
                                       i18n("<qt>Do you want to save the changes for <b>%1</b>?</qt>", d->m_newName),
                                       i18n("Save changes")) == KMessageBox::Yes) {
            d->m_inSelection = true;
            slotUpdatePayee();
            d->m_inSelection = false;
        }
    }

    // make sure we always clear the selected list when listing again
    d->m_selectedPayeesList.clear();

    // loop over all payees and count the number of payees, also
    // obtain last selected payee
    d->selectedPayees(d->m_selectedPayeesList);
    updatePayeeActions(d->m_selectedPayeesList);

    emit selectObjects(d->m_selectedPayeesList);

    if (d->m_selectedPayeesList.isEmpty()) {
        d->ui->m_tabWidget->setEnabled(false); // disable tab widget
        d->ui->m_balanceLabel->hide();
        d->ui->m_deleteButton->setEnabled(false); //disable delete, rename and merge buttons
        d->ui->m_renameButton->setEnabled(false);
        d->ui->m_mergeButton->setEnabled(false);
        d->clearItemData();
        d->m_payee = MyMoneyPayee();
        d->ui->m_syncAddressbook->setEnabled(false);
        return; // make sure we don't access an undefined payee
    }

    d->ui->m_deleteButton->setEnabled(true); //re-enable delete button

    d->ui->m_syncAddressbook->setEnabled(true);

    // if we have multiple payees selected, clear and disable the payee information
    if (d->m_selectedPayeesList.count() > 1) {
        d->ui->m_tabWidget->setEnabled(false); // disable tab widget
        d->ui->m_renameButton->setEnabled(false); // disable also the rename button
        d->ui->m_mergeButton->setEnabled(true);
        d->ui->m_balanceLabel->hide();
        d->clearItemData();
    } else {
        d->ui->m_mergeButton->setEnabled(false);
        d->ui->m_renameButton->setEnabled(true);
    }

    // otherwise we have just one selected, enable payee information widget
    d->ui->m_tabWidget->setEnabled(true);
    d->ui->m_balanceLabel->show();

    // as of now we are updating only the last selected payee, and until
    // selection mode of the QListView has been changed to Extended, this
    // will also be the only selection and behave exactly as before - Andreas
    try {
        d->m_payee = d->m_selectedPayeesList[0];

        d->m_newName = d->m_payee.name();

        d->ui->addressEdit->setEnabled(true);
        d->ui->addressEdit->setText(d->m_payee.address());
        d->ui->payeecityEdit->setEnabled(true);
        d->ui->payeecityEdit->setText(d->m_payee.city());
        d->ui->payeestateEdit->setEnabled(true);
        d->ui->payeestateEdit->setText(d->m_payee.state());
        d->ui->postcodeEdit->setEnabled(true);
        d->ui->postcodeEdit->setText(d->m_payee.postcode());
        d->ui->telephoneEdit->setEnabled(true);
        d->ui->telephoneEdit->setText(d->m_payee.telephone());
        d->ui->emailEdit->setEnabled(true);
        d->ui->emailEdit->setText(d->m_payee.email());
        d->ui->notesEdit->setText(d->m_payee.notes());

        QStringList keys;
        bool ignorecase = false;
        auto type = d->m_payee.matchData(ignorecase, keys);

        d->ui->matchTypeCombo->setCurrentIndex(d->ui->matchTypeCombo->findData(static_cast<int>(type)));
        d->ui->matchKeyEditList->clear();
        d->ui->matchKeyEditList->insertStringList(keys);
        d->ui->checkMatchIgnoreCase->setChecked(ignorecase);

        d->ui->checkEnableDefaultCategory->setChecked(!d->m_payee.defaultAccountId().isEmpty());
        d->ui->comboDefaultCategory->setCurrentIndex(-1);
        d->ui->comboDefaultCategory->setSelected(d->m_payee.defaultAccountId());

        d->ui->payeeIdentifiers->setSource(d->m_payee);

        slotPayeeDataChanged();

        d->showTransactions();

    } catch (const MyMoneyException &e) {
        qDebug("exception during display of payee: %s", e.what());
        d->ui->m_register->clear();
        d->m_selectedPayeesList.clear();
        d->m_payee = MyMoneyPayee();
    }
    d->m_allowEditing = true;
}

void KPayeesView::slotKeyListChanged()
{
    Q_D(KPayeesView);
    bool rc = false;
    bool ignorecase = false;
    QStringList keys;

    d->m_payee.matchData(ignorecase, keys);
    if (static_cast<eMyMoney::Payee::MatchType>(d->ui->matchTypeCombo->currentData().toUInt()) == eMyMoney::Payee::MatchType::Key) {
        rc |= (keys != d->ui->matchKeyEditList->items());
    }
    d->setDirty(rc);
}

void KPayeesView::slotPayeeDataChanged()
{
    Q_D(KPayeesView);
    bool rc = false;

    if (d->ui->m_tabWidget->isEnabled()) {
        rc |= ((d->m_payee.email().isEmpty() != d->ui->emailEdit->text().isEmpty())
               || (!d->ui->emailEdit->text().isEmpty() && d->m_payee.email() != d->ui->emailEdit->text()));
        rc |= ((d->m_payee.address().isEmpty() != d->ui->addressEdit->toPlainText().isEmpty())
               || (!d->ui->addressEdit->toPlainText().isEmpty() && d->m_payee.address() != d->ui->addressEdit->toPlainText()));
        rc |= ((d->m_payee.city().isEmpty() != d->ui->payeecityEdit->text().isEmpty())
               || (!d->ui->payeecityEdit->text().isEmpty() && d->m_payee.city() != d->ui->payeecityEdit->text()));
        rc |= ((d->m_payee.state().isEmpty() != d->ui->payeestateEdit->text().isEmpty())
               || (!d->ui->payeestateEdit->text().isEmpty() && d->m_payee.state() != d->ui->payeestateEdit->text()));
        rc |= ((d->m_payee.postcode().isEmpty() != d->ui->postcodeEdit->text().isEmpty())
               || (!d->ui->postcodeEdit->text().isEmpty() && d->m_payee.postcode() != d->ui->postcodeEdit->text()));
        rc |= ((d->m_payee.telephone().isEmpty() != d->ui->telephoneEdit->text().isEmpty())
               || (!d->ui->telephoneEdit->text().isEmpty() && d->m_payee.telephone() != d->ui->telephoneEdit->text()));
        rc |= ((d->m_payee.name().isEmpty() != d->m_newName.isEmpty())
               || (!d->m_newName.isEmpty() && d->m_payee.name() != d->m_newName));
        rc |= ((d->m_payee.notes().isEmpty() != d->ui->notesEdit->toPlainText().isEmpty())
               || (!d->ui->notesEdit->toPlainText().isEmpty() && d->m_payee.notes() != d->ui->notesEdit->toPlainText()));

        bool ignorecase = false;
        QStringList keys;

        const auto selectedMatchType = static_cast<eMyMoney::Payee::MatchType>(d->ui->matchTypeCombo->currentData().toUInt());
        const auto storedMatchType = d->m_payee.matchData(ignorecase, keys);
        rc |= (storedMatchType != selectedMatchType);

        d->ui->checkMatchIgnoreCase->setEnabled(false);
        d->ui->matchKeyEditList->setEnabled(false);

        if (selectedMatchType != eMyMoney::Payee::MatchType::Disabled) {
            d->ui->checkMatchIgnoreCase->setEnabled(true);
            // if we turn matching on, we default to 'ignore case'
            // TODO maybe make the default a user option
            if (storedMatchType == eMyMoney::Payee::MatchType::Disabled && (selectedMatchType != eMyMoney::Payee::MatchType::Disabled))
                d->ui->checkMatchIgnoreCase->setChecked(true);
            rc |= (ignorecase != d->ui->checkMatchIgnoreCase->isChecked());
            if (selectedMatchType == eMyMoney::Payee::MatchType::Key) {
                d->ui->matchKeyEditList->setEnabled(true);
                rc |= (keys != d->ui->matchKeyEditList->items());
            }
        }

        rc |= (d->ui->checkEnableDefaultCategory->isChecked() != !d->m_payee.defaultAccountId().isEmpty());
        if (d->ui->checkEnableDefaultCategory->isChecked()) {
            d->ui->comboDefaultCategory->setEnabled(true);
            d->ui->labelDefaultCategory->setEnabled(true);
            // this is only going to understand the first in the list of selected accounts
            if (d->ui->comboDefaultCategory->getSelected().isEmpty()) {
                rc |= !d->m_payee.defaultAccountId().isEmpty();
            } else {
                QString temp = d->ui->comboDefaultCategory->getSelected();
                rc |= (temp.isEmpty() != d->m_payee.defaultAccountId().isEmpty())
                      || (!d->m_payee.defaultAccountId().isEmpty() && temp != d->m_payee.defaultAccountId());
            }
        } else {
            d->ui->comboDefaultCategory->setEnabled(false);
            d->ui->labelDefaultCategory->setEnabled(false);
        }

        rc |= (d->m_payee.payeeIdentifiers() != d->ui->payeeIdentifiers->identifiers());
    }
    d->setDirty(rc);
}

void KPayeesView::slotUpdatePayee()
{
    Q_D(KPayeesView);
    if (d->isDirty()) {
        MyMoneyFileTransaction ft;
        d->setDirty(false);
        try {
            d->m_payee.setName(d->m_newName);
            d->m_payee.setAddress(d->ui->addressEdit->toPlainText());
            d->m_payee.setCity(d->ui->payeecityEdit->text());
            d->m_payee.setState(d->ui->payeestateEdit->text());
            d->m_payee.setPostcode(d->ui->postcodeEdit->text());
            d->m_payee.setTelephone(d->ui->telephoneEdit->text());
            d->m_payee.setEmail(d->ui->emailEdit->text());
            d->m_payee.setNotes(d->ui->notesEdit->toPlainText());
            d->m_payee.setMatchData(static_cast<eMyMoney::Payee::MatchType>(d->ui->matchTypeCombo->currentData().toUInt()), d->ui->checkMatchIgnoreCase->isChecked(), d->ui->matchKeyEditList->items());
            d->m_payee.setDefaultAccountId();
            d->m_payee.resetPayeeIdentifiers(d->ui->payeeIdentifiers->identifiers());

            if (d->ui->checkEnableDefaultCategory->isChecked()) {
                QString temp;
                if (!d->ui->comboDefaultCategory->getSelected().isEmpty()) {
                    temp = d->ui->comboDefaultCategory->getSelected();
                    d->m_payee.setDefaultAccountId(temp);
                }
            }

            MyMoneyFile::instance()->modifyPayee(d->m_payee);
            ft.commit();

        } catch (const MyMoneyException &e) {
            KMessageBox::detailedSorry(this, i18n("Unable to modify payee"), QString::fromLatin1(e.what()));
        }
    }
}

void KPayeesView::slotSyncAddressBook()
{
    Q_D(KPayeesView);
    if (d->m_payeeRows.isEmpty()) {                            // empty list means no syncing is pending...
        foreach (auto item, d->ui->m_payeesList->selectedItems()) {
            d->m_payeeRows.append(d->ui->m_payeesList->row(item));        // ...so initialize one
        }
        d->ui->m_payeesList->clearSelection();                       // otherwise slotSelectPayee will be run after every payee update
//    d->ui->m_syncAddressbook->setEnabled(false);                 // disallow concurrent syncs
    }

    if (d->m_payeeRows.count() <= d->m_payeeRow) {
        if (auto item = dynamic_cast<KPayeeListItem*>(d->ui->m_payeesList->currentItem())) { // update ui if something is selected
            d->m_payee = item->payee();
            d->ui->addressEdit->setText(d->m_payee.address());
            d->ui->payeecityEdit->setText(d->m_payee.city());
            d->ui->payeestateEdit->setText(d->m_payee.state());
            d->ui->postcodeEdit->setText(d->m_payee.postcode());
            d->ui->telephoneEdit->setText(d->m_payee.telephone());
        }
        d->m_payeeRows.clear();  // that means end of sync
        d->m_payeeRow = 0;
        return;
    }

    if (auto item = dynamic_cast<KPayeeListItem*>(d->ui->m_payeesList->item(d->m_payeeRows.at(d->m_payeeRow))))
        d->m_payee = item->payee();
    ++d->m_payeeRow;

    d->m_contact->fetchContact(d->m_payee.email()); // search for payee's data in addressbook and receive it in slotContactFetched
}

void KPayeesView::slotContactFetched(const ContactData &identity)
{
    Q_D(KPayeesView);
    if (!identity.email.isEmpty()) {  // empty e-mail means no identity fetched
        QString txt;
        if (!identity.street.isEmpty())
            txt.append(identity.street + '\n');
        if (!identity.locality.isEmpty()) {
            txt.append(identity.locality);
            if (!identity.postalCode.isEmpty())
                txt.append(' ' + identity.postalCode + '\n');
            else
                txt.append('\n');
        }
        if (!identity.country.isEmpty())
            txt.append(identity.country + '\n');

        if (!txt.isEmpty() && d->m_payee.address().compare(txt) != 0)
            d->m_payee.setAddress(txt);

        if (!identity.city.isEmpty() && d->m_payee.city().compare(identity.city) != 0)
            d->m_payee.setCity(identity.city);

        if (!identity.state.isEmpty() && d->m_payee.state().compare(identity.state) != 0)
            d->m_payee.setState(identity.state);

        if (!identity.postalCode.isEmpty() && d->m_payee.postcode().compare(identity.postalCode) != 0)
            d->m_payee.setPostcode(identity.postalCode);

        if (!identity.phoneNumber.isEmpty() && d->m_payee.telephone().compare(identity.phoneNumber) != 0)
            d->m_payee.setTelephone(identity.phoneNumber);

        MyMoneyFileTransaction ft;
        try {
            MyMoneyFile::instance()->modifyPayee(d->m_payee);
            ft.commit();
        } catch (const MyMoneyException &e) {
            KMessageBox::detailedSorry(this, i18n("Unable to modify payee"), QString::fromLatin1(e.what()));
        }
    }

    slotSyncAddressBook();  // process next payee
}

void KPayeesView::slotSendMail()
{
    Q_D(KPayeesView);
    QRegularExpression re(".+@.+");
    if (re.match(d->m_payee.email()).hasMatch())
        QDesktopServices::openUrl(QUrl(QStringLiteral("mailto:?to=") + d->m_payee.email(), QUrl::TolerantMode));
}

void KPayeesView::executeCustomAction(eView::Action action)
{
    Q_D(KPayeesView);
    switch(action) {
    case eView::Action::Refresh:
        refresh();
        break;

    case eView::Action::SetDefaultFocus:
        QTimer::singleShot(0, d->m_searchWidget, SLOT(setFocus()));
        break;

    case eView::Action::ClosePayeeIdentifierSource:
        slotClosePayeeIdentifierSource();
        break;

    default:
        break;
    }
}

void KPayeesView::refresh()
{
    Q_D(KPayeesView);
    if (isVisible()) {
        if (d->m_inSelection) {
            QTimer::singleShot(0, this, SLOT(refresh()));
        } else {
            d->loadPayees();
            d->m_needsRefresh = false;
        }
    } else {
        d->m_needsRefresh = true;
    }
}

void KPayeesView::showEvent(QShowEvent* event)
{
    if (MyMoneyFile::instance()->storageAttached()) {
        Q_D(KPayeesView);
        if (d->m_needLoad)
            d->init();

        emit customActionRequested(View::Payees, eView::Action::AboutToShow);

        if (d->m_needsRefresh)
            refresh();

        QList<MyMoneyPayee> list;
        d->selectedPayees(list);
        emit selectObjects(list);
    }

    // don't forget base class implementation
    QWidget::showEvent(event);
}

void KPayeesView::updatePayeeActions(const QList<MyMoneyPayee> &payees)
{
    pActions[eMenu::Action::NewPayee]->setEnabled(true);
    const auto payeesCount = payees.count();
    auto b = payeesCount == 1 ? true : false;
    pActions[eMenu::Action::RenamePayee]->setEnabled(b);
    b = payeesCount > 1 ? true : false;
    pActions[eMenu::Action::MergePayee]->setEnabled(b);
    b = payeesCount == 0 ? false : true;
    pActions[eMenu::Action::DeletePayee]->setEnabled(b);
}

void KPayeesView::slotSelectTransaction()
{
    Q_D(KPayeesView);
    auto list = d->ui->m_register->selectedItems();
    if (!list.isEmpty()) {
        const auto t = dynamic_cast<KMyMoneyRegister::Transaction*>(list[0]);
        if (t)
            emit selectByVariant(QVariantList {QVariant(t->split().accountId()), QVariant(t->transaction().id()) }, eView::Intent::ShowTransaction);
    }
}

void KPayeesView::slotSelectPayeeAndTransaction(const QString& payeeId, const QString& accountId, const QString& transactionId)
{
    Q_D(KPayeesView);
    if (!isVisible())
        return;

    try {
        // clear filter
        d->m_searchWidget->clear();
        d->m_searchWidget->updateSearch();

        // deselect all other selected items
        QList<QListWidgetItem *> selectedItems = d->ui->m_payeesList->selectedItems();
        QList<QListWidgetItem *>::const_iterator payeesIt = selectedItems.constBegin();
        while (payeesIt != selectedItems.constEnd()) {
            if (auto item = dynamic_cast<KPayeeListItem*>(*payeesIt))
                item->setSelected(false);
            ++payeesIt;
        }

        // find the payee in the list
        QListWidgetItem* it;
        for (int i = 0; i < d->ui->m_payeesList->count(); ++i) {
            it = d->ui->m_payeesList->item(i);
            auto item = dynamic_cast<KPayeeListItem *>(it);
            if (item && item->payee().id() == payeeId) {
                d->ui->m_payeesList->scrollToItem(it, QAbstractItemView::PositionAtCenter);

                d->ui->m_payeesList->setCurrentItem(it);     // active item and deselect all others
                d->ui->m_payeesList->setCurrentRow(i, QItemSelectionModel::ClearAndSelect); // and select it

                //make sure the payee selection is updated and transactions are updated accordingly
                slotSelectPayee();

                KMyMoneyRegister::RegisterItem *registerItem = 0;
                for (i = 0; i < d->ui->m_register->rowCount(); ++i) {
                    registerItem = d->ui->m_register->itemAtRow(i);
                    if (auto t = dynamic_cast<KMyMoneyRegister::Transaction*>(registerItem)) {
                        if (t->transaction().id() == transactionId && t->transaction().accountReferenced(accountId)) {
                            d->ui->m_register->selectItem(registerItem);
                            d->ui->m_register->ensureItemVisible(registerItem);
                            break;
                        }
                    }
                }
                // quit out of outer for() loop
                break;
            }
        }
    } catch (const MyMoneyException &e) {
        qWarning("Unexpected exception in KPayeesView::slotSelectPayeeAndTransaction %s", e.what());
    }
}

void KPayeesView::slotShowPayeesMenu(const QPoint& /*p*/)
{
    Q_D(KPayeesView);
    if (dynamic_cast<KPayeeListItem*>(d->ui->m_payeesList->currentItem())) {
        slotSelectPayee();
        pMenus[eMenu::Menu::Payee]->exec(QCursor::pos());
    }
}

void KPayeesView::slotHelp()
{
    KHelpClient::invokeHelp("details.payees");
}

void KPayeesView::slotChangeFilter(int index)
{
    Q_D(KPayeesView);
    //update the filter type then reload the payees list
    d->m_payeeFilterType = index;
    d->loadPayees();
}

void KPayeesView::slotNewPayee()
{
    QString id;
    KMyMoneyUtils::newPayee(i18n("New Payee"), id);
    slotSelectPayeeAndTransaction(id);
}

void KPayeesView::slotRenamePayee()
{
    Q_D(KPayeesView);
    if (d->ui->m_payeesList->currentItem() && d->ui->m_payeesList->selectedItems().count() == 1) {
        slotStartRename(d->ui->m_payeesList->currentItem());
    }
}

void KPayeesView::slotDeletePayee()
{
    Q_D(KPayeesView);
    if (d->m_selectedPayeesList.isEmpty())
        return; // shouldn't happen

    // get confirmation from user
    QString prompt;
    if (d->m_selectedPayeesList.size() == 1)
        prompt = i18n("<p>Do you really want to remove the payee <b>%1</b>?</p>", d->m_selectedPayeesList.front().name());
    else
        prompt = i18n("Do you really want to remove all selected payees?");

    if (KMessageBox::questionYesNo(this, prompt, i18n("Remove Payee")) == KMessageBox::No)
        return;

    d->payeeReassign(KPayeeReassignDlg::TypeDelete);
}

void KPayeesView::slotMergePayee()
{
    Q_D(KPayeesView);
    if (d->m_selectedPayeesList.size() < 1)
        return; // shouldn't happen

    if (KMessageBox::questionYesNo(this, i18n("<p>Do you really want to merge the selected payees?"),
                                   i18n("Merge Payees")) == KMessageBox::No)
        return;

    if (d->payeeReassign(KPayeeReassignDlg::TypeMerge))
        // clean selection since we just deleted the selected payees
        d->m_selectedPayeesList.clear();
}
