1 /***************************************************************************
2                           kpayeesview.cpp
3                           ---------------
4     begin                : Thu Jan 24 2002
5     copyright            : (C) 2000-2002 by Michael Edwardes <mte@users.sourceforge.net>
6                            Javier Campos Morales <javi_c@users.sourceforge.net>
7                            Felix Rodriguez <frodriguez@users.sourceforge.net>
8                            John C <thetacoturtle@users.sourceforge.net>
9                            Thomas Baumgart <ipwizard@users.sourceforge.net>
10                            Kevin Tambascio <ktambascio@users.sourceforge.net>
11                            Andreas Nicolai <Andreas.Nicolai@gmx.net>
12                            (C) 2017 by Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
13  ***************************************************************************/
14 
15 /***************************************************************************
16  *                                                                         *
17  *   This program is free software; you can redistribute it and/or modify  *
18  *   it under the terms of the GNU General Public License as published by  *
19  *   the Free Software Foundation; either version 2 of the License, or     *
20  *   (at your option) any later version.                                   *
21  *                                                                         *
22  ***************************************************************************/
23 
24 #include "kpayeesview_p.h"
25 
26 // ----------------------------------------------------------------------------
27 // QT Includes
28 
29 #include <QMap>
30 #include <QList>
31 #include <QTimer>
32 #include <QMenu>
33 #include <QDesktopServices>
34 #include <QIcon>
35 
36 // ----------------------------------------------------------------------------
37 // KDE Includes
38 
39 #include <KLocalizedString>
40 #include <KMessageBox>
41 #include <KGuiItem>
42 #include <KHelpClient>
43 #include <KSharedConfig>
44 #include <KListWidgetSearchLine>
45 
46 // ----------------------------------------------------------------------------
47 // Project Includes
48 
49 #include <config-kmymoney.h>
50 #include "ui_kpayeesview.h"
51 #include "kmymoneyviewbase_p.h"
52 #include "kpayeeidentifierview.h"
53 #include "mymoneypayee.h"
54 #include "mymoneyexception.h"
55 #include "mymoneyfile.h"
56 #include "mymoneyaccount.h"
57 #include "mymoneymoney.h"
58 #include "mymoneytransactionfilter.h"
59 #include "kmymoneysettings.h"
60 #include "models.h"
61 #include "accountsmodel.h"
62 #include "mymoneysecurity.h"
63 #include "mymoneycontact.h"
64 #include "mymoneyprice.h"
65 #include "mymoneysplit.h"
66 #include "mymoneytransaction.h"
67 #include "icons/icons.h"
68 #include "transaction.h"
69 #include "widgetenums.h"
70 #include "mymoneyenums.h"
71 #include "modelenums.h"
72 #include "menuenums.h"
73 
74 using namespace Icons;
75 
76 // *** KPayeesView Implementation ***
77 
KPayeesView(QWidget * parent)78 KPayeesView::KPayeesView(QWidget *parent) :
79     KMyMoneyViewBase(*new KPayeesViewPrivate(this), parent)
80 {
81   connect(pActions[eMenu::Action::NewPayee],    &QAction::triggered, this, &KPayeesView::slotNewPayee);
82   connect(pActions[eMenu::Action::RenamePayee], &QAction::triggered, this, &KPayeesView::slotRenamePayee);
83   connect(pActions[eMenu::Action::DeletePayee], &QAction::triggered, this, &KPayeesView::slotDeletePayee);
84   connect(pActions[eMenu::Action::MergePayee],  &QAction::triggered, this, &KPayeesView::slotMergePayee);
85 }
86 
~KPayeesView()87 KPayeesView::~KPayeesView()
88 {
89 }
90 
slotChooseDefaultAccount()91 void KPayeesView::slotChooseDefaultAccount()
92 {
93   Q_D(KPayeesView);
94   MyMoneyFile* file = MyMoneyFile::instance();
95   QMap<QString, int> account_count;
96 
97   KMyMoneyRegister::RegisterItem* item = d->ui->m_register->firstItem();
98   while (item) {
99     //only walk through selectable items. eg. transactions and not group markers
100     if (item->isSelectable()) {
101       auto t = dynamic_cast<KMyMoneyRegister::Transaction*>(item);
102       if (!t)
103         return;
104       MyMoneySplit s = t->transaction().splitByPayee(d->m_payee.id());
105       const MyMoneyAccount& acc = file->account(s.accountId());
106 
107       if (s.action() != MyMoneySplit::actionName(eMyMoney::Split::Action::Amortization)
108           && acc.accountType() != eMyMoney::Account::Type::AssetLoan
109           && !file->isTransfer(t->transaction())
110           && t->transaction().splitCount() == 2) {
111         MyMoneySplit s0 = t->transaction().splitByAccount(s.accountId(), false);
112         if (account_count.contains(s0.accountId())) {
113           account_count[s0.accountId()]++;
114         } else {
115           account_count[s0.accountId()] = 1;
116         }
117       }
118     }
119     item = item->nextItem();
120   }
121 
122   QMap<QString, int>::Iterator most_frequent, iter;
123   most_frequent = account_count.begin();
124   for (iter = account_count.begin(); iter != account_count.end(); ++iter) {
125     if (iter.value() > most_frequent.value()) {
126       most_frequent = iter;
127     }
128   }
129 
130   if (most_frequent != account_count.end()) {
131     d->ui->checkEnableDefaultCategory->setChecked(true);
132     d->ui->comboDefaultCategory->setSelected(most_frequent.key());
133     d->setDirty(true);
134   }
135 }
136 
slotClosePayeeIdentifierSource()137 void KPayeesView::slotClosePayeeIdentifierSource()
138 {
139   Q_D(KPayeesView);
140   if (!d->m_needLoad)
141     d->ui->payeeIdentifiers->closeSource();
142 }
143 
slotSelectByVariant(const QVariantList & variant,eView::Intent intent)144 void KPayeesView::slotSelectByVariant(const QVariantList& variant, eView::Intent intent)
145 {
146   switch (intent) {
147     case eView::Intent::ShowPayee:
148       if (variant.count() == 3)
149         slotSelectPayeeAndTransaction(variant.at(0).toString(), variant.at(1).toString(), variant.at(2).toString());
150       break;
151     default:
152       break;
153   }
154 }
155 
156 
slotStartRename(QListWidgetItem * item)157 void KPayeesView::slotStartRename(QListWidgetItem* item)
158 {
159   Q_D(KPayeesView);
160   d->m_allowEditing = true;
161   d->ui->m_payeesList->editItem(item);
162 }
163 
164 // This variant is only called when a single payee is selected and renamed.
slotRenameSinglePayee(QListWidgetItem * p)165 void KPayeesView::slotRenameSinglePayee(QListWidgetItem* p)
166 {
167   Q_D(KPayeesView);
168   //if there is no current item selected, exit
169   if (d->m_allowEditing == false || !d->ui->m_payeesList->currentItem() || p != d->ui->m_payeesList->currentItem())
170     return;
171 
172   //qDebug() << "[KPayeesView::slotRenamePayee]";
173   // create a copy of the new name without appended whitespaces
174   QString new_name = p->text();
175   if (d->m_payee.name() != new_name) {
176     MyMoneyFileTransaction ft;
177     try {
178       // check if we already have a payee with the new name
179       try {
180         // this function call will throw an exception, if the payee
181         // hasn't been found.
182         MyMoneyFile::instance()->payeeByName(new_name);
183         // the name already exists, ask the user whether he's sure to keep the name
184         if (KMessageBox::questionYesNo(this,
185                                        i18n("A payee with the name '%1' already exists. It is not advisable to have "
186                                             "multiple payees with the same identification name. Are you sure you would like "
187                                             "to rename the payee?", new_name)) != KMessageBox::Yes) {
188           p->setText(d->m_payee.name());
189           return;
190         }
191       } catch (const MyMoneyException &) {
192         // all ok, the name is unique
193       }
194 
195       d->m_payee.setName(new_name);
196       d->m_newName = new_name;
197       MyMoneyFile::instance()->modifyPayee(d->m_payee);
198 
199       // the above call to modifyPayee will reload the view so
200       // all references and pointers to the view have to be
201       // re-established.
202 
203       // make sure, that the record is visible even if it moved
204       // out of sight due to the rename operation
205       d->ensurePayeeVisible(d->m_payee.id());
206 
207       ft.commit();
208 
209     } catch (const MyMoneyException &e) {
210       KMessageBox::detailedSorry(this, i18n("Unable to modify payee"), QString::fromLatin1(e.what()));
211     }
212   } else {
213     p->setText(new_name);
214   }
215 }
216 
slotSelectPayee(QListWidgetItem * cur,QListWidgetItem * prev)217 void KPayeesView::slotSelectPayee(QListWidgetItem* cur, QListWidgetItem* prev)
218 {
219   Q_D(KPayeesView);
220   Q_UNUSED(cur);
221   Q_UNUSED(prev);
222 
223   d->m_allowEditing = false;
224 }
225 
slotSelectPayee()226 void KPayeesView::slotSelectPayee()
227 {
228   Q_D(KPayeesView);
229   // check if the content of a currently selected payee was modified
230   // and ask to store the data
231   if (d->isDirty()) {
232     if (KMessageBox::questionYesNo(this,
233                                    i18n("<qt>Do you want to save the changes for <b>%1</b>?</qt>", d->m_newName),
234                                    i18n("Save changes")) == KMessageBox::Yes) {
235       d->m_inSelection = true;
236       slotUpdatePayee();
237       d->m_inSelection = false;
238     }
239   }
240 
241   // make sure we always clear the selected list when listing again
242   d->m_selectedPayeesList.clear();
243 
244   // loop over all payees and count the number of payees, also
245   // obtain last selected payee
246   d->selectedPayees(d->m_selectedPayeesList);
247   updatePayeeActions(d->m_selectedPayeesList);
248 
249   emit selectObjects(d->m_selectedPayeesList);
250 
251   if (d->m_selectedPayeesList.isEmpty()) {
252     d->ui->m_tabWidget->setEnabled(false); // disable tab widget
253     d->ui->m_balanceLabel->hide();
254     d->ui->m_deleteButton->setEnabled(false); //disable delete, rename and merge buttons
255     d->ui->m_renameButton->setEnabled(false);
256     d->ui->m_mergeButton->setEnabled(false);
257     d->clearItemData();
258     d->m_payee = MyMoneyPayee();
259     d->ui->m_syncAddressbook->setEnabled(false);
260     return; // make sure we don't access an undefined payee
261   }
262 
263   d->ui->m_deleteButton->setEnabled(true); //re-enable delete button
264 
265   d->ui->m_syncAddressbook->setEnabled(true);
266 
267   // if we have multiple payees selected, clear and disable the payee information
268   if (d->m_selectedPayeesList.count() > 1) {
269     d->ui->m_tabWidget->setEnabled(false); // disable tab widget
270     d->ui->m_renameButton->setEnabled(false); // disable also the rename button
271     d->ui->m_mergeButton->setEnabled(true);
272     d->ui->m_balanceLabel->hide();
273     d->clearItemData();
274   } else {
275     d->ui->m_mergeButton->setEnabled(false);
276     d->ui->m_renameButton->setEnabled(true);
277   }
278 
279   // otherwise we have just one selected, enable payee information widget
280   d->ui->m_tabWidget->setEnabled(true);
281   d->ui->m_balanceLabel->show();
282 
283   // as of now we are updating only the last selected payee, and until
284   // selection mode of the QListView has been changed to Extended, this
285   // will also be the only selection and behave exactly as before - Andreas
286   try {
287     d->m_payee = d->m_selectedPayeesList[0];
288 
289     d->m_newName = d->m_payee.name();
290 
291     d->ui->addressEdit->setEnabled(true);
292     d->ui->addressEdit->setText(d->m_payee.address());
293     d->ui->payeecityEdit->setEnabled(true);
294     d->ui->payeecityEdit->setText(d->m_payee.city());
295     d->ui->payeestateEdit->setEnabled(true);
296     d->ui->payeestateEdit->setText(d->m_payee.state());
297     d->ui->postcodeEdit->setEnabled(true);
298     d->ui->postcodeEdit->setText(d->m_payee.postcode());
299     d->ui->telephoneEdit->setEnabled(true);
300     d->ui->telephoneEdit->setText(d->m_payee.telephone());
301     d->ui->emailEdit->setEnabled(true);
302     d->ui->emailEdit->setText(d->m_payee.email());
303     d->ui->notesEdit->setText(d->m_payee.notes());
304 
305     QStringList keys;
306     bool ignorecase = false;
307     auto type = d->m_payee.matchData(ignorecase, keys);
308 
309     d->ui->matchTypeCombo->setCurrentIndex(d->ui->matchTypeCombo->findData(static_cast<int>(type)));
310     d->ui->matchKeyEditList->clear();
311     d->ui->matchKeyEditList->insertStringList(keys);
312     d->ui->checkMatchIgnoreCase->setChecked(ignorecase);
313 
314     d->ui->checkEnableDefaultCategory->setChecked(!d->m_payee.defaultAccountId().isEmpty());
315     d->ui->comboDefaultCategory->setSelected(d->m_payee.defaultAccountId());
316 
317     d->ui->payeeIdentifiers->setSource(d->m_payee);
318 
319     slotPayeeDataChanged();
320 
321     d->showTransactions();
322 
323   } catch (const MyMoneyException &e) {
324     qDebug("exception during display of payee: %s", e.what());
325     d->ui->m_register->clear();
326     d->m_selectedPayeesList.clear();
327     d->m_payee = MyMoneyPayee();
328   }
329   d->m_allowEditing = true;
330 }
331 
slotKeyListChanged()332 void KPayeesView::slotKeyListChanged()
333 {
334   Q_D(KPayeesView);
335   bool rc = false;
336   bool ignorecase = false;
337   QStringList keys;
338 
339   d->m_payee.matchData(ignorecase, keys);
340   if (static_cast<eMyMoney::Payee::MatchType>(d->ui->matchTypeCombo->currentData().toUInt()) == eMyMoney::Payee::MatchType::Key) {
341     rc |= (keys != d->ui->matchKeyEditList->items());
342   }
343   d->setDirty(rc);
344 }
345 
slotPayeeDataChanged()346 void KPayeesView::slotPayeeDataChanged()
347 {
348   Q_D(KPayeesView);
349   bool rc = false;
350 
351   if (d->ui->m_tabWidget->isEnabled()) {
352     rc |= ((d->m_payee.email().isEmpty() != d->ui->emailEdit->text().isEmpty())
353            || (!d->ui->emailEdit->text().isEmpty() && d->m_payee.email() != d->ui->emailEdit->text()));
354     rc |= ((d->m_payee.address().isEmpty() != d->ui->addressEdit->toPlainText().isEmpty())
355            || (!d->ui->addressEdit->toPlainText().isEmpty() && d->m_payee.address() != d->ui->addressEdit->toPlainText()));
356     rc |= ((d->m_payee.city().isEmpty() != d->ui->payeecityEdit->text().isEmpty())
357            || (!d->ui->payeecityEdit->text().isEmpty() && d->m_payee.city() != d->ui->payeecityEdit->text()));
358     rc |= ((d->m_payee.state().isEmpty() != d->ui->payeestateEdit->text().isEmpty())
359            || (!d->ui->payeestateEdit->text().isEmpty() && d->m_payee.state() != d->ui->payeestateEdit->text()));
360     rc |= ((d->m_payee.postcode().isEmpty() != d->ui->postcodeEdit->text().isEmpty())
361            || (!d->ui->postcodeEdit->text().isEmpty() && d->m_payee.postcode() != d->ui->postcodeEdit->text()));
362     rc |= ((d->m_payee.telephone().isEmpty() != d->ui->telephoneEdit->text().isEmpty())
363            || (!d->ui->telephoneEdit->text().isEmpty() && d->m_payee.telephone() != d->ui->telephoneEdit->text()));
364     rc |= ((d->m_payee.name().isEmpty() != d->m_newName.isEmpty())
365            || (!d->m_newName.isEmpty() && d->m_payee.name() != d->m_newName));
366     rc |= ((d->m_payee.notes().isEmpty() != d->ui->notesEdit->toPlainText().isEmpty())
367            || (!d->ui->notesEdit->toPlainText().isEmpty() && d->m_payee.notes() != d->ui->notesEdit->toPlainText()));
368 
369     bool ignorecase = false;
370     QStringList keys;
371 
372     auto type = d->m_payee.matchData(ignorecase, keys);
373     rc |= (static_cast<unsigned int>(type) != d->ui->matchTypeCombo->currentData().toUInt());
374 
375     d->ui->checkMatchIgnoreCase->setEnabled(false);
376     d->ui->matchKeyEditList->setEnabled(false);
377 
378     if (static_cast<eMyMoney::Payee::MatchType>(d->ui->matchTypeCombo->currentData().toUInt()) != eMyMoney::Payee::MatchType::Disabled) {
379       d->ui->checkMatchIgnoreCase->setEnabled(true);
380       // if we turn matching on, we default to 'ignore case'
381       // TODO maybe make the default a user option
382       if (type == eMyMoney::Payee::MatchType::Disabled && static_cast<eMyMoney::Payee::MatchType>(d->ui->matchTypeCombo->currentData().toUInt()) != eMyMoney::Payee::MatchType::Disabled)
383         d->ui->checkMatchIgnoreCase->setChecked(true);
384       rc |= (ignorecase != d->ui->checkMatchIgnoreCase->isChecked());
385       if (static_cast<eMyMoney::Payee::MatchType>(d->ui->matchTypeCombo->currentData().toUInt()) == eMyMoney::Payee::MatchType::Key) {
386         d->ui->matchKeyEditList->setEnabled(true);
387         rc |= (keys != d->ui->matchKeyEditList->items());
388       }
389     }
390 
391     rc |= (d->ui->checkEnableDefaultCategory->isChecked() != !d->m_payee.defaultAccountId().isEmpty());
392     if (d->ui->checkEnableDefaultCategory->isChecked()) {
393       d->ui->comboDefaultCategory->setEnabled(true);
394       d->ui->labelDefaultCategory->setEnabled(true);
395       // this is only going to understand the first in the list of selected accounts
396       if (d->ui->comboDefaultCategory->getSelected().isEmpty()) {
397         rc |= !d->m_payee.defaultAccountId().isEmpty();
398       } else {
399         QString temp = d->ui->comboDefaultCategory->getSelected();
400         rc |= (temp.isEmpty() != d->m_payee.defaultAccountId().isEmpty())
401               || (!d->m_payee.defaultAccountId().isEmpty() && temp != d->m_payee.defaultAccountId());
402       }
403     } else {
404       d->ui->comboDefaultCategory->setEnabled(false);
405       d->ui->labelDefaultCategory->setEnabled(false);
406     }
407 
408     rc |= (d->m_payee.payeeIdentifiers() != d->ui->payeeIdentifiers->identifiers());
409   }
410   d->setDirty(rc);
411 }
412 
slotUpdatePayee()413 void KPayeesView::slotUpdatePayee()
414 {
415   Q_D(KPayeesView);
416   if (d->isDirty()) {
417     MyMoneyFileTransaction ft;
418     d->setDirty(false);
419     try {
420       d->m_payee.setName(d->m_newName);
421       d->m_payee.setAddress(d->ui->addressEdit->toPlainText());
422       d->m_payee.setCity(d->ui->payeecityEdit->text());
423       d->m_payee.setState(d->ui->payeestateEdit->text());
424       d->m_payee.setPostcode(d->ui->postcodeEdit->text());
425       d->m_payee.setTelephone(d->ui->telephoneEdit->text());
426       d->m_payee.setEmail(d->ui->emailEdit->text());
427       d->m_payee.setNotes(d->ui->notesEdit->toPlainText());
428       d->m_payee.setMatchData(static_cast<eMyMoney::Payee::MatchType>(d->ui->matchTypeCombo->currentData().toUInt()), d->ui->checkMatchIgnoreCase->isChecked(), d->ui->matchKeyEditList->items());
429       d->m_payee.setDefaultAccountId();
430       d->m_payee.resetPayeeIdentifiers(d->ui->payeeIdentifiers->identifiers());
431 
432       if (d->ui->checkEnableDefaultCategory->isChecked()) {
433         QString temp;
434         if (!d->ui->comboDefaultCategory->getSelected().isEmpty()) {
435           temp = d->ui->comboDefaultCategory->getSelected();
436           d->m_payee.setDefaultAccountId(temp);
437         }
438       }
439 
440       MyMoneyFile::instance()->modifyPayee(d->m_payee);
441       ft.commit();
442 
443     } catch (const MyMoneyException &e) {
444       KMessageBox::detailedSorry(this, i18n("Unable to modify payee"), QString::fromLatin1(e.what()));
445     }
446   }
447 }
448 
slotSyncAddressBook()449 void KPayeesView::slotSyncAddressBook()
450 {
451   Q_D(KPayeesView);
452   if (d->m_payeeRows.isEmpty()) {                            // empty list means no syncing is pending...
453     foreach (auto item, d->ui->m_payeesList->selectedItems()) {
454       d->m_payeeRows.append(d->ui->m_payeesList->row(item));        // ...so initialize one
455     }
456     d->ui->m_payeesList->clearSelection();                       // otherwise slotSelectPayee will be run after every payee update
457 //    d->ui->m_syncAddressbook->setEnabled(false);                 // disallow concurrent syncs
458   }
459 
460   if (d->m_payeeRows.count() <= d->m_payeeRow) {
461     if (auto item = dynamic_cast<KPayeeListItem*>(d->ui->m_payeesList->currentItem())) { // update ui if something is selected
462       d->m_payee = item->payee();
463       d->ui->addressEdit->setText(d->m_payee.address());
464       d->ui->payeecityEdit->setText(d->m_payee.city());
465       d->ui->payeestateEdit->setText(d->m_payee.state());
466       d->ui->postcodeEdit->setText(d->m_payee.postcode());
467       d->ui->telephoneEdit->setText(d->m_payee.telephone());
468     }
469     d->m_payeeRows.clear();  // that means end of sync
470     d->m_payeeRow = 0;
471     return;
472   }
473 
474   if (auto item = dynamic_cast<KPayeeListItem*>(d->ui->m_payeesList->item(d->m_payeeRows.at(d->m_payeeRow))))
475     d->m_payee = item->payee();
476   ++d->m_payeeRow;
477 
478   d->m_contact->fetchContact(d->m_payee.email()); // search for payee's data in addressbook and receive it in slotContactFetched
479 }
480 
slotContactFetched(const ContactData & identity)481 void KPayeesView::slotContactFetched(const ContactData &identity)
482 {
483   Q_D(KPayeesView);
484   if (!identity.email.isEmpty()) {  // empty e-mail means no identity fetched
485     QString txt;
486     if (!identity.street.isEmpty())
487       txt.append(identity.street + '\n');
488     if (!identity.locality.isEmpty()) {
489       txt.append(identity.locality);
490     if (!identity.postalCode.isEmpty())
491       txt.append(' ' + identity.postalCode + '\n');
492       else
493         txt.append('\n');
494     }
495     if (!identity.country.isEmpty())
496       txt.append(identity.country + '\n');
497 
498     if (!txt.isEmpty() && d->m_payee.address().compare(txt) != 0)
499       d->m_payee.setAddress(txt);
500 
501     if (!identity.city.isEmpty() && d->m_payee.city().compare(identity.city) != 0)
502       d->m_payee.setCity(identity.city);
503 
504     if (!identity.state.isEmpty() && d->m_payee.state().compare(identity.state) != 0)
505       d->m_payee.setState(identity.state);
506 
507     if (!identity.postalCode.isEmpty() && d->m_payee.postcode().compare(identity.postalCode) != 0)
508       d->m_payee.setPostcode(identity.postalCode);
509 
510     if (!identity.phoneNumber.isEmpty() && d->m_payee.telephone().compare(identity.phoneNumber) != 0)
511       d->m_payee.setTelephone(identity.phoneNumber);
512 
513     MyMoneyFileTransaction ft;
514     try {
515       MyMoneyFile::instance()->modifyPayee(d->m_payee);
516       ft.commit();
517     } catch (const MyMoneyException &e) {
518       KMessageBox::detailedSorry(this, i18n("Unable to modify payee"), QString::fromLatin1(e.what()));
519     }
520   }
521 
522   slotSyncAddressBook();  // process next payee
523 }
524 
slotSendMail()525 void KPayeesView::slotSendMail()
526 {
527   Q_D(KPayeesView);
528   QRegularExpression re(".+@.+");
529   if (re.match(d->m_payee.email()).hasMatch())
530     QDesktopServices::openUrl(QUrl(QStringLiteral("mailto:?to=") + d->m_payee.email(), QUrl::TolerantMode));
531 }
532 
executeCustomAction(eView::Action action)533 void KPayeesView::executeCustomAction(eView::Action action)
534 {
535   switch(action) {
536     case eView::Action::Refresh:
537       refresh();
538       break;
539 
540     case eView::Action::SetDefaultFocus:
541       {
542         Q_D(KPayeesView);
543         QTimer::singleShot(0, d->m_searchWidget, SLOT(setFocus()));
544       }
545       break;
546 
547     case eView::Action::ClosePayeeIdentifierSource:
548       slotClosePayeeIdentifierSource();
549       break;
550 
551     default:
552       break;
553   }
554 }
555 
refresh()556 void KPayeesView::refresh()
557 {
558   Q_D(KPayeesView);
559   if (isVisible()) {
560     if (d->m_inSelection) {
561       QTimer::singleShot(0, this, SLOT(refresh()));
562     } else {
563       d->loadPayees();
564       d->m_needsRefresh = false;
565     }
566   } else {
567     d->m_needsRefresh = true;
568   }
569 }
570 
showEvent(QShowEvent * event)571 void KPayeesView::showEvent(QShowEvent* event)
572 {
573   if (MyMoneyFile::instance()->storageAttached()) {
574     Q_D(KPayeesView);
575     if (d->m_needLoad)
576       d->init();
577 
578     emit customActionRequested(View::Payees, eView::Action::AboutToShow);
579 
580     if (d->m_needsRefresh)
581       refresh();
582 
583     QList<MyMoneyPayee> list;
584     d->selectedPayees(list);
585     emit selectObjects(list);
586   }
587 
588   // don't forget base class implementation
589   QWidget::showEvent(event);
590 }
591 
updatePayeeActions(const QList<MyMoneyPayee> & payees)592 void KPayeesView::updatePayeeActions(const QList<MyMoneyPayee> &payees)
593 {
594   pActions[eMenu::Action::NewPayee]->setEnabled(true);
595   const auto payeesCount = payees.count();
596   auto b = payeesCount == 1 ? true : false;
597   pActions[eMenu::Action::RenamePayee]->setEnabled(b);
598   b = payeesCount > 1 ? true : false;
599   pActions[eMenu::Action::MergePayee]->setEnabled(b);
600   b = payeesCount == 0 ? false : true;
601   pActions[eMenu::Action::DeletePayee]->setEnabled(b);
602 }
603 
slotSelectTransaction()604 void KPayeesView::slotSelectTransaction()
605 {
606   Q_D(KPayeesView);
607   auto list = d->ui->m_register->selectedItems();
608   if (!list.isEmpty()) {
609     const auto t = dynamic_cast<KMyMoneyRegister::Transaction*>(list[0]);
610     if (t)
611       emit selectByVariant(QVariantList {QVariant(t->split().accountId()), QVariant(t->transaction().id()) }, eView::Intent::ShowTransaction);
612   }
613 }
614 
slotSelectPayeeAndTransaction(const QString & payeeId,const QString & accountId,const QString & transactionId)615 void KPayeesView::slotSelectPayeeAndTransaction(const QString& payeeId, const QString& accountId, const QString& transactionId)
616 {
617   Q_D(KPayeesView);
618   if (!isVisible())
619     return;
620 
621   try {
622     // clear filter
623     d->m_searchWidget->clear();
624     d->m_searchWidget->updateSearch();
625 
626     // deselect all other selected items
627     QList<QListWidgetItem *> selectedItems = d->ui->m_payeesList->selectedItems();
628     QList<QListWidgetItem *>::const_iterator payeesIt = selectedItems.constBegin();
629     while (payeesIt != selectedItems.constEnd()) {
630       if (auto item = dynamic_cast<KPayeeListItem*>(*payeesIt))
631         item->setSelected(false);
632       ++payeesIt;
633     }
634 
635     // find the payee in the list
636     QListWidgetItem* it;
637     for (int i = 0; i < d->ui->m_payeesList->count(); ++i) {
638       it = d->ui->m_payeesList->item(i);
639       auto item = dynamic_cast<KPayeeListItem *>(it);
640       if (item && item->payee().id() == payeeId) {
641         d->ui->m_payeesList->scrollToItem(it, QAbstractItemView::PositionAtCenter);
642 
643         d->ui->m_payeesList->setCurrentItem(it);     // active item and deselect all others
644         d->ui->m_payeesList->setCurrentRow(i, QItemSelectionModel::ClearAndSelect); // and select it
645 
646         //make sure the payee selection is updated and transactions are updated accordingly
647         slotSelectPayee();
648 
649         KMyMoneyRegister::RegisterItem *registerItem = 0;
650         for (i = 0; i < d->ui->m_register->rowCount(); ++i) {
651           registerItem = d->ui->m_register->itemAtRow(i);
652           if (auto t = dynamic_cast<KMyMoneyRegister::Transaction*>(registerItem)) {
653             if (t->transaction().id() == transactionId && t->transaction().accountReferenced(accountId)) {
654               d->ui->m_register->selectItem(registerItem);
655               d->ui->m_register->ensureItemVisible(registerItem);
656               break;
657             }
658           }
659         }
660         // quit out of outer for() loop
661         break;
662       }
663     }
664   } catch (const MyMoneyException &e) {
665     qWarning("Unexpected exception in KPayeesView::slotSelectPayeeAndTransaction %s", e.what());
666   }
667 }
668 
slotShowPayeesMenu(const QPoint &)669 void KPayeesView::slotShowPayeesMenu(const QPoint& /*p*/)
670 {
671   Q_D(KPayeesView);
672   if (dynamic_cast<KPayeeListItem*>(d->ui->m_payeesList->currentItem())) {
673     slotSelectPayee();
674     pMenus[eMenu::Menu::Payee]->exec(QCursor::pos());
675   }
676 }
677 
slotHelp()678 void KPayeesView::slotHelp()
679 {
680   KHelpClient::invokeHelp("details.payees");
681 }
682 
slotChangeFilter(int index)683 void KPayeesView::slotChangeFilter(int index)
684 {
685   Q_D(KPayeesView);
686   //update the filter type then reload the payees list
687   d->m_payeeFilterType = index;
688   d->loadPayees();
689 }
690 
slotNewPayee()691 void KPayeesView::slotNewPayee()
692 {
693   QString id;
694   KMyMoneyUtils::newPayee(i18n("New Payee"), id);
695   slotSelectPayeeAndTransaction(id);
696 }
697 
slotRenamePayee()698 void KPayeesView::slotRenamePayee()
699 {
700   Q_D(KPayeesView);
701   if (d->ui->m_payeesList->currentItem() && d->ui->m_payeesList->selectedItems().count() == 1) {
702     slotStartRename(d->ui->m_payeesList->currentItem());
703   }
704 }
705 
slotDeletePayee()706 void KPayeesView::slotDeletePayee()
707 {
708   Q_D(KPayeesView);
709   if (d->m_selectedPayeesList.isEmpty())
710     return; // shouldn't happen
711 
712   // get confirmation from user
713   QString prompt;
714   if (d->m_selectedPayeesList.size() == 1)
715     prompt = i18n("<p>Do you really want to remove the payee <b>%1</b>?</p>", d->m_selectedPayeesList.front().name());
716   else
717     prompt = i18n("Do you really want to remove all selected payees?");
718 
719   if (KMessageBox::questionYesNo(this, prompt, i18n("Remove Payee")) == KMessageBox::No)
720     return;
721 
722   d->payeeReassign(KPayeeReassignDlg::TypeDelete);
723 }
724 
slotMergePayee()725 void KPayeesView::slotMergePayee()
726 {
727   Q_D(KPayeesView);
728   if (d->m_selectedPayeesList.size() < 1)
729     return; // shouldn't happen
730 
731   if (KMessageBox::questionYesNo(this, i18n("<p>Do you really want to merge the selected payees?"),
732                                  i18n("Merge Payees")) == KMessageBox::No)
733     return;
734 
735   if (d->payeeReassign(KPayeeReassignDlg::TypeMerge))
736     // clean selection since we just deleted the selected payees
737     d->m_selectedPayeesList.clear();
738 }
739