diff --git a/kmymoney/converter/mymoneygncreader.cpp b/kmymoney/converter/mymoneygncreader.cpp index 67e0cfc88..49e7ca76c 100644 --- a/kmymoney/converter/mymoneygncreader.cpp +++ b/kmymoney/converter/mymoneygncreader.cpp @@ -1,2652 +1,2651 @@ /*************************************************************************** mymoneygncreader - description ------------------- begin : Wed Mar 3 2004 copyright : (C) 2000-2004 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "mymoneygncreader.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include #include -#include // ---------------------------------------------------------------------------- // KDE Includes #ifndef _GNCFILEANON #include #include #endif #include // ---------------------------------------------------------------------------- // Third party Includes // ------------------------------------------------------------Box21---------------- // Project Includes #include "config-kmymoney.h" #include "imymoneyserialize.h" #ifndef _GNCFILEANON #include "storage/imymoneystorage.h" #include "kmymoneyutils.h" #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "mymoneysecurity.h" #include "mymoneyschedule.h" #include "mymoneyprice.h" #include "mymoneypayee.h" #include "mymoneysplit.h" #include "mymoneytransaction.h" #include "mymoneyexception.h" #include "kgncimportoptionsdlg.h" #include "kgncpricesourcedlg.h" #include "keditscheduledlg.h" #include "kmymoneyedit.h" #define TRY try #define CATCH catch (const MyMoneyException &) #define PASS catch (const MyMoneyException &) { throw; } #else #include "mymoneymoney.h" #include // #define i18n QObject::tr #define TRY #define CATCH #define PASS #define MYMONEYEXCEPTION QString #define MyMoneyException QString #define PACKAGE "KMyMoney" #endif // _GNCFILEANON #include "mymoneyenums.h" using namespace eMyMoney; // init static variables double MyMoneyGncReader::m_fileHideFactor = 0.0; double GncObject::m_moneyHideFactor; // user options void MyMoneyGncReader::setOptions() { #ifndef _GNCFILEANON KGncImportOptionsDlg dlg; // display the dialog to allow the user to set own options if (dlg.exec()) { // set users input options m_dropSuspectSchedules = dlg.scheduleOption(); m_investmentOption = dlg.investmentOption(); m_useFinanceQuote = dlg.quoteOption(); m_useTxNotes = dlg.txNotesOption(); m_decoder = dlg.decodeOption(); gncdebug = dlg.generalDebugOption(); xmldebug = dlg.xmlDebugOption(); bAnonymize = dlg.anonymizeOption(); } else { // user declined, so set some sensible defaults m_dropSuspectSchedules = false; // investment option - 0, create investment a/c per stock a/c, 1 = single new investment account, 2 = prompt for each stock // option 2 doesn't really work too well at present m_investmentOption = 0; m_useFinanceQuote = false; m_useTxNotes = false; m_decoder = 0; gncdebug = false; // general debug messages xmldebug = false; // xml trace bAnonymize = false; // anonymize input } // no dialog option for the following; it will set base currency, and print actual XML data developerDebug = false; // set your fave currency here to save getting that enormous dialog each time you run a test // especially if you have to scroll down to USD... if (developerDebug) m_storage->setValue("kmm-baseCurrency", "GBP"); #endif // _GNCFILEANON } GncObject::GncObject() : pMain(0), m_subElementList(0), m_subElementListCount(0), m_dataElementList(0), m_dataElementListCount(0), m_dataPtr(0), m_state(0), m_anonClassList(0), m_anonClass(0) { } // Check that the current element is of a version we are coded for void GncObject::checkVersion(const QString& elName, const QXmlAttributes& elAttrs, const map_elementVersions& map) { TRY { if (map.contains(elName)) { // if it's not in the map, there's nothing to check if (!map[elName].contains(elAttrs.value("version"))) { QString em = Q_FUNC_INFO + i18n(": Sorry. This importer cannot handle version %1 of element %2" , elAttrs.value("version"), elName); throw MYMONEYEXCEPTION(em); } } return ; } PASS } // Check if this element is in the current object's sub element list GncObject *GncObject::isSubElement(const QString& elName, const QXmlAttributes& elAttrs) { TRY { uint i; GncObject *next = 0; for (i = 0; i < m_subElementListCount; i++) { if (elName == m_subElementList[i]) { m_state = i; next = startSubEl(); // go create the sub object if (next != 0) { next->initiate(elName, elAttrs); // initialize it next->m_elementName = elName; // save it's name so we can identify the end } break; } } return (next); } PASS } // Check if this element is in the current object's data element list bool GncObject::isDataElement(const QString &elName, const QXmlAttributes& elAttrs) { TRY { uint i; for (i = 0; i < m_dataElementListCount; i++) { if (elName == m_dataElementList[i]) { m_state = i; dataEl(elAttrs); // go set the pointer so the data can be stored return (true); } } m_dataPtr = 0; // we don't need this, so make sure we don't store extraneous data return (false); } PASS } // return the variable string, decoded if required QString GncObject::var(int i) const { /* This code was needed because the Qt3 XML reader apparently did not process the encoding parameter in the m_decoder == 0 ? m_v[i] : pMain->m_decoder->toUnicode(m_v[i].toUtf8())); } const QString GncObject::getKvpValue(const QString& key, const QString& type) const { QList::const_iterator it; // first check for exact match for (it = m_kvpList.begin(); it != m_kvpList.end(); ++it) { if (((*it).key() == key) && ((type.isEmpty()) || ((*it).type() == type))) return (*it).value(); } // then for partial match for (it = m_kvpList.begin(); it != m_kvpList.end(); ++it) { if (((*it).key().contains(key)) && ((type.isEmpty()) || ((*it).type() == type))) return (*it).value(); } return (QString()); } void GncObject::adjustHideFactor() { m_moneyHideFactor = pMain->m_fileHideFactor * (1.0 + (int)(200.0 * rand() / (RAND_MAX + 1.0))) / 100.0; } // data anonymizer QString GncObject::hide(QString data, unsigned int anonClass) { TRY { if (!pMain->bAnonymize) return (data); // no anonymizing required // counters used to generate names for anonymizer static int nextAccount; static int nextEquity; static int nextPayee; static int nextSched; static QMap anonPayees; // to check for duplicate payee names static QMap anonStocks; // for reference to equities QString result(data); QMap::const_iterator it; MyMoneyMoney in, mresult; switch (anonClass) { case ASIS: // this is not personal data break; case SUPPRESS: // this is personal and is not essential result = ""; break; case NXTACC: // generate account name result = ki18n("Account%1").subs(++nextAccount, -6).toString(); break; case NXTEQU: // generate/return an equity name it = anonStocks.constFind(data); if (it == anonStocks.constEnd()) { result = ki18n("Stock%1").subs(++nextEquity, -6).toString(); anonStocks.insert(data, result); } else { result = (*it); } break; case NXTPAY: // generate/return a payee name it = anonPayees.constFind(data); if (it == anonPayees.constEnd()) { result = ki18n("Payee%1").subs(++nextPayee, -6).toString(); anonPayees.insert(data, result); } else { result = (*it); } break; case NXTSCHD: // generate a schedule name result = ki18n("Schedule%1").subs(++nextSched, -6).toString(); break; case MONEY1: in = MyMoneyMoney(data); if (data == "-1/0") in = MyMoneyMoney(); // spurious gnucash data - causes a crash sometimes mresult = MyMoneyMoney(m_moneyHideFactor) * in; mresult.convert(10000); result = mresult.toString(); break; case MONEY2: in = MyMoneyMoney(data); if (data == "-1/0") in = MyMoneyMoney(); mresult = MyMoneyMoney(m_moneyHideFactor) * in; mresult.convert(10000); mresult.setThousandSeparator(' '); result = mresult.formatMoney("", 2); break; } return (result); } PASS } // dump current object data values // only called if gncdebug set void GncObject::debugDump() { uint i; qDebug() << "Object" << m_elementName; for (i = 0; i < m_dataElementListCount; i++) { qDebug() << m_dataElementList[i] << "=" << m_v[i]; } } //***************************************************************** GncFile::GncFile() { static const QString subEls[] = {"gnc:book", "gnc:count-data", "gnc:commodity", "price", "gnc:account", "gnc:transaction", "gnc:template-transactions", "gnc:schedxaction" }; m_subElementList = subEls; m_subElementListCount = END_FILE_SELS; m_dataElementListCount = 0; m_processingTemplates = false; m_bookFound = false; } GncFile::~GncFile() {} GncObject *GncFile::startSubEl() { TRY { if (pMain->xmldebug) qDebug("File start subel m_state %d", m_state); GncObject *next = 0; switch (m_state) { case BOOK: if (m_bookFound) throw MYMONEYEXCEPTION(i18n("This version of the importer cannot handle multi-book files.")); m_bookFound = true; break; case COUNT: next = new GncCountData; break; case CMDTY: next = new GncCommodity; break; case PRICE: next = new GncPrice; break; case ACCT: // accounts within the template section are ignored if (!m_processingTemplates) next = new GncAccount; break; case TX: next = new GncTransaction(m_processingTemplates); break; case TEMPLATES: m_processingTemplates = true; break; case SCHEDULES: m_processingTemplates = false; next = new GncSchedule; break; default: throw MYMONEYEXCEPTION("GncFile rcvd invalid state"); } return (next); } PASS } void GncFile::endSubEl(GncObject *subObj) { if (pMain->xmldebug) qDebug("File end subel"); if (!m_processingTemplates) delete subObj; // template txs must be saved awaiting schedules m_dataPtr = 0; return ; } //****************************************** GncDate ********************************************* GncDate::GncDate() { m_subElementListCount = 0; static const QString dEls[] = {"ts:date", "gdate"}; m_dataElementList = dEls; m_dataElementListCount = END_Date_DELS; static const unsigned int anonClasses[] = {ASIS, ASIS}; m_anonClassList = anonClasses; for (uint i = 0; i < m_dataElementListCount; i++) m_v.append(QString()); } GncDate::~GncDate() {} //*************************************GncCmdtySpec*************************************** GncCmdtySpec::GncCmdtySpec() { m_subElementListCount = 0; static const QString dEls[] = {"cmdty:space", "cmdty:id"}; m_dataElementList = dEls; m_dataElementListCount = END_CmdtySpec_DELS; static const unsigned int anonClasses[] = {ASIS, ASIS}; m_anonClassList = anonClasses; for (uint i = 0; i < m_dataElementListCount; i++) m_v.append(QString()); } GncCmdtySpec::~GncCmdtySpec() {} QString GncCmdtySpec::hide(QString data, unsigned int) { // hide equity names, but not currency names unsigned int newClass = ASIS; switch (m_state) { case CMDTYID: if (!isCurrency()) newClass = NXTEQU; } return (GncObject::hide(data, newClass)); } //************* GncKvp******************************************** GncKvp::GncKvp() { m_subElementListCount = END_Kvp_SELS; static const QString subEls[] = {"slot"}; // kvp's may be nested m_subElementList = subEls; m_dataElementListCount = END_Kvp_DELS; static const QString dataEls[] = {"slot:key", "slot:value"}; m_dataElementList = dataEls; static const unsigned int anonClasses[] = {ASIS, ASIS}; m_anonClassList = anonClasses; for (uint i = 0; i < m_dataElementListCount; i++) m_v.append(QString()); } GncKvp::~GncKvp() {} void GncKvp::dataEl(const QXmlAttributes& elAttrs) { switch (m_state) { case VALUE: m_kvpType = elAttrs.value("type"); } m_dataPtr = &(m_v[m_state]); if (key().contains("formula")) { m_anonClass = MONEY2; } else { m_anonClass = ASIS; } return ; } GncObject *GncKvp::startSubEl() { if (pMain->xmldebug) qDebug("Kvp start subel m_state %d", m_state); TRY { GncObject *next = 0; switch (m_state) { case KVP: next = new GncKvp; break; default: throw MYMONEYEXCEPTION("GncKvp rcvd invalid m_state "); } return (next); } PASS } void GncKvp::endSubEl(GncObject *subObj) { if (pMain->xmldebug) qDebug("Kvp end subel"); m_kvpList.append(*(static_cast (subObj))); m_dataPtr = 0; return ; } //*********************************GncLot********************************************* GncLot::GncLot() { m_subElementListCount = 0; m_dataElementListCount = 0; } GncLot::~GncLot() {} //*********************************GncCountData*************************************** GncCountData::GncCountData() { m_subElementListCount = 0; m_dataElementListCount = 0; m_v.append(QString()); // only 1 data item } GncCountData::~GncCountData() {} void GncCountData::initiate(const QString&, const QXmlAttributes& elAttrs) { m_countType = elAttrs.value("cd:type"); m_dataPtr = &(m_v[0]); return ; } void GncCountData::terminate() { int i = m_v[0].toInt(); if (m_countType == "commodity") { pMain->setGncCommodityCount(i); return ; } if (m_countType == "account") { pMain->setGncAccountCount(i); return ; } if (m_countType == "transaction") { pMain->setGncTransactionCount(i); return ; } if (m_countType == "schedxaction") { pMain->setGncScheduleCount(i); return ; } if (i != 0) { if (m_countType == "budget") pMain->setBudgetsFound(true); else if (m_countType.left(7) == "gnc:Gnc") pMain->setSmallBusinessFound(true); else if (pMain->xmldebug) qDebug() << "Unknown count type" << m_countType; } return ; } //*********************************GncCommodity*************************************** GncCommodity::GncCommodity() { m_subElementListCount = 0; static const QString dEls[] = {"cmdty:space", "cmdty:id", "cmdty:name", "cmdty:fraction"}; m_dataElementList = dEls; m_dataElementListCount = END_Commodity_DELS; static const unsigned int anonClasses[] = {ASIS, NXTEQU, SUPPRESS, ASIS}; m_anonClassList = anonClasses; for (uint i = 0; i < m_dataElementListCount; i++) m_v.append(QString()); } GncCommodity::~GncCommodity() {} void GncCommodity::terminate() { TRY { pMain->convertCommodity(this); return ; } PASS } //************* GncPrice******************************************** GncPrice::GncPrice() { static const QString subEls[] = {"price:commodity", "price:currency", "price:time"}; m_subElementList = subEls; m_subElementListCount = END_Price_SELS; m_dataElementListCount = END_Price_DELS; static const QString dataEls[] = {"price:value"}; m_dataElementList = dataEls; static const unsigned int anonClasses[] = {ASIS}; m_anonClassList = anonClasses; for (uint i = 0; i < m_dataElementListCount; i++) m_v.append(QString()); m_vpCommodity = 0; m_vpCurrency = 0; m_vpPriceDate = 0; } GncPrice::~GncPrice() { delete m_vpCommodity; delete m_vpCurrency; delete m_vpPriceDate; } GncObject *GncPrice::startSubEl() { TRY { GncObject *next = 0; switch (m_state) { case CMDTY: next = new GncCmdtySpec; break; case CURR: next = new GncCmdtySpec; break; case PRICEDATE: next = new GncDate; break; default: throw MYMONEYEXCEPTION("GncPrice rcvd invalid m_state"); } return (next); } PASS } void GncPrice::endSubEl(GncObject *subObj) { TRY { switch (m_state) { case CMDTY: m_vpCommodity = static_cast(subObj); break; case CURR: m_vpCurrency = static_cast(subObj); break; case PRICEDATE: m_vpPriceDate = static_cast(subObj); break; default: throw MYMONEYEXCEPTION("GncPrice rcvd invalid m_state"); } return; } PASS } void GncPrice::terminate() { TRY { pMain->convertPrice(this); return ; } PASS } //************* GncAccount******************************************** GncAccount::GncAccount() { m_subElementListCount = END_Account_SELS; static const QString subEls[] = {"act:commodity", "slot", "act:lots"}; m_subElementList = subEls; m_dataElementListCount = END_Account_DELS; static const QString dataEls[] = {"act:id", "act:name", "act:description", "act:type", "act:parent" }; m_dataElementList = dataEls; static const unsigned int anonClasses[] = {ASIS, NXTACC, SUPPRESS, ASIS, ASIS}; m_anonClassList = anonClasses; for (uint i = 0; i < m_dataElementListCount; i++) m_v.append(QString()); m_vpCommodity = 0; } GncAccount::~GncAccount() { delete m_vpCommodity; } GncObject *GncAccount::startSubEl() { TRY { if (pMain->xmldebug) qDebug("Account start subel m_state %d", m_state); GncObject *next = 0; switch (m_state) { case CMDTY: next = new GncCmdtySpec; break; case KVP: next = new GncKvp; break; case LOTS: next = new GncLot(); pMain->setLotsFound(true); // we don't handle lots; just set flag to report break; default: throw MYMONEYEXCEPTION("GncAccount rcvd invalid m_state"); } return (next); } PASS } void GncAccount::endSubEl(GncObject *subObj) { if (pMain->xmldebug) qDebug("Account end subel"); switch (m_state) { case CMDTY: m_vpCommodity = static_cast(subObj); break; case KVP: m_kvpList.append(*(static_cast (subObj))); } return ; } void GncAccount::terminate() { TRY { pMain->convertAccount(this); return ; } PASS } //************* GncTransaction******************************************** GncTransaction::GncTransaction(bool processingTemplates) { m_subElementListCount = END_Transaction_SELS; static const QString subEls[] = {"trn:currency", "trn:date-posted", "trn:date-entered", "trn:split", "slot" }; m_subElementList = subEls; m_dataElementListCount = END_Transaction_DELS; static const QString dataEls[] = {"trn:id", "trn:num", "trn:description"}; m_dataElementList = dataEls; static const unsigned int anonClasses[] = {ASIS, SUPPRESS, NXTPAY}; m_anonClassList = anonClasses; adjustHideFactor(); m_template = processingTemplates; for (uint i = 0; i < m_dataElementListCount; i++) m_v.append(QString()); m_vpCurrency = 0; m_vpDateEntered = m_vpDatePosted = 0; } GncTransaction::~GncTransaction() { delete m_vpCurrency; delete m_vpDatePosted; delete m_vpDateEntered; } GncObject *GncTransaction::startSubEl() { TRY { if (pMain->xmldebug) qDebug("Transaction start subel m_state %d", m_state); GncObject *next = 0; switch (m_state) { case CURRCY: next = new GncCmdtySpec; break; case POSTED: case ENTERED: next = new GncDate; break; case SPLIT: if (isTemplate()) { next = new GncTemplateSplit; } else { next = new GncSplit; } break; case KVP: next = new GncKvp; break; default: throw MYMONEYEXCEPTION("GncTransaction rcvd invalid m_state"); } return (next); } PASS } void GncTransaction::endSubEl(GncObject *subObj) { if (pMain->xmldebug) qDebug("Transaction end subel"); switch (m_state) { case CURRCY: m_vpCurrency = static_cast(subObj); break; case POSTED: m_vpDatePosted = static_cast(subObj); break; case ENTERED: m_vpDateEntered = static_cast(subObj); break; case SPLIT: m_splitList.append(subObj); break; case KVP: m_kvpList.append(*(static_cast (subObj))); } return ; } void GncTransaction::terminate() { TRY { if (isTemplate()) { pMain->saveTemplateTransaction(this); } else { pMain->convertTransaction(this); } return ; } PASS } //************* GncSplit******************************************** GncSplit::GncSplit() { m_subElementListCount = END_Split_SELS; static const QString subEls[] = {"split:reconcile-date"}; m_subElementList = subEls; m_dataElementListCount = END_Split_DELS; static const QString dataEls[] = {"split:id", "split:memo", "split:reconciled-state", "split:value", "split:quantity", "split:account" }; m_dataElementList = dataEls; static const unsigned int anonClasses[] = {ASIS, SUPPRESS, ASIS, MONEY1, MONEY1, ASIS}; m_anonClassList = anonClasses; for (uint i = 0; i < m_dataElementListCount; i++) m_v.append(QString()); m_vpDateReconciled = 0; } GncSplit::~GncSplit() { delete m_vpDateReconciled; } GncObject *GncSplit::startSubEl() { TRY { GncObject *next = 0; switch (m_state) { case RECDATE: next = new GncDate; break; default: throw MYMONEYEXCEPTION("GncTemplateSplit rcvd invalid m_state "); } return (next); } PASS } void GncSplit::endSubEl(GncObject *subObj) { if (pMain->xmldebug) qDebug("Split end subel"); switch (m_state) { case RECDATE: m_vpDateReconciled = static_cast(subObj); break; } return ; } //************* GncTemplateSplit******************************************** GncTemplateSplit::GncTemplateSplit() { m_subElementListCount = END_TemplateSplit_SELS; static const QString subEls[] = {"slot"}; m_subElementList = subEls; m_dataElementListCount = END_TemplateSplit_DELS; static const QString dataEls[] = {"split:id", "split:memo", "split:reconciled-state", "split:value", "split:quantity", "split:account" }; m_dataElementList = dataEls; static const unsigned int anonClasses[] = {ASIS, SUPPRESS, ASIS, MONEY1, MONEY1, ASIS}; m_anonClassList = anonClasses; for (uint i = 0; i < m_dataElementListCount; i++) m_v.append(QString()); } GncTemplateSplit::~GncTemplateSplit() {} GncObject *GncTemplateSplit::startSubEl() { if (pMain->xmldebug) qDebug("TemplateSplit start subel m_state %d", m_state); TRY { GncObject *next = 0; switch (m_state) { case KVP: next = new GncKvp; break; default: throw MYMONEYEXCEPTION("GncTemplateSplit rcvd invalid m_state"); } return (next); } PASS } void GncTemplateSplit::endSubEl(GncObject *subObj) { if (pMain->xmldebug) qDebug("TemplateSplit end subel"); m_kvpList.append(*(static_cast (subObj))); m_dataPtr = 0; return ; } //************* GncSchedule******************************************** GncSchedule::GncSchedule() { m_subElementListCount = END_Schedule_SELS; static const QString subEls[] = {"sx:start", "sx:last", "sx:end", "gnc:freqspec", "gnc:recurrence", "sx:deferredInstance"}; m_subElementList = subEls; m_dataElementListCount = END_Schedule_DELS; static const QString dataEls[] = {"sx:name", "sx:enabled", "sx:autoCreate", "sx:autoCreateNotify", "sx:autoCreateDays", "sx:advanceCreateDays", "sx:advanceRemindDays", "sx:instanceCount", "sx:num-occur", "sx:rem-occur", "sx:templ-acct" }; m_dataElementList = dataEls; static const unsigned int anonClasses[] = {NXTSCHD, ASIS, ASIS, ASIS, ASIS, ASIS, ASIS, ASIS, ASIS, ASIS, ASIS}; m_anonClassList = anonClasses; for (uint i = 0; i < m_dataElementListCount; i++) m_v.append(QString()); m_vpStartDate = m_vpLastDate = m_vpEndDate = 0; m_vpFreqSpec = 0; m_vpRecurrence.clear(); m_vpSchedDef = 0; } GncSchedule::~GncSchedule() { delete m_vpStartDate; delete m_vpLastDate; delete m_vpEndDate; delete m_vpFreqSpec; delete m_vpSchedDef; } GncObject *GncSchedule::startSubEl() { if (pMain->xmldebug) qDebug("Schedule start subel m_state %d", m_state); TRY { GncObject *next = 0; switch (m_state) { case STARTDATE: case LASTDATE: case ENDDATE: next = new GncDate; break; case FREQ: next = new GncFreqSpec; break; case RECURRENCE: next = new GncRecurrence; break; case DEFINST: next = new GncSchedDef; break; default: throw MYMONEYEXCEPTION("GncSchedule rcvd invalid m_state"); } return (next); } PASS } void GncSchedule::endSubEl(GncObject *subObj) { if (pMain->xmldebug) qDebug("Schedule end subel"); switch (m_state) { case STARTDATE: m_vpStartDate = static_cast(subObj); break; case LASTDATE: m_vpLastDate = static_cast(subObj); break; case ENDDATE: m_vpEndDate = static_cast(subObj); break; case FREQ: m_vpFreqSpec = static_cast(subObj); break; case RECURRENCE: m_vpRecurrence.append(static_cast(subObj)); break; case DEFINST: m_vpSchedDef = static_cast(subObj); break; } return ; } void GncSchedule::terminate() { TRY { pMain->convertSchedule(this); return ; } PASS } //************* GncFreqSpec******************************************** GncFreqSpec::GncFreqSpec() { m_subElementListCount = END_FreqSpec_SELS; static const QString subEls[] = {"gnc:freqspec"}; m_subElementList = subEls; m_dataElementListCount = END_FreqSpec_DELS; static const QString dataEls[] = {"fs:ui_type", "fs:monthly", "fs:daily", "fs:weekly", "fs:interval", "fs:offset", "fs:day" }; m_dataElementList = dataEls; static const unsigned int anonClasses[] = {ASIS, ASIS, ASIS, ASIS, ASIS, ASIS, ASIS }; m_anonClassList = anonClasses; for (uint i = 0; i < m_dataElementListCount; i++) m_v.append(QString()); } GncFreqSpec::~GncFreqSpec() {} GncObject *GncFreqSpec::startSubEl() { TRY { if (pMain->xmldebug) qDebug("FreqSpec start subel m_state %d", m_state); GncObject *next = 0; switch (m_state) { case COMPO: next = new GncFreqSpec; break; default: throw MYMONEYEXCEPTION("GncFreqSpec rcvd invalid m_state"); } return (next); } PASS } void GncFreqSpec::endSubEl(GncObject *subObj) { if (pMain->xmldebug) qDebug("FreqSpec end subel"); switch (m_state) { case COMPO: m_fsList.append(subObj); break; } m_dataPtr = 0; return ; } void GncFreqSpec::terminate() { pMain->convertFreqSpec(this); return ; } //************* GncRecurrence******************************************** GncRecurrence::GncRecurrence() : m_vpStartDate(0) { m_subElementListCount = END_Recurrence_SELS; static const QString subEls[] = {"recurrence:start"}; m_subElementList = subEls; m_dataElementListCount = END_Recurrence_DELS; static const QString dataEls[] = {"recurrence:mult", "recurrence:period_type"}; m_dataElementList = dataEls; static const unsigned int anonClasses[] = {ASIS, ASIS}; m_anonClassList = anonClasses; for (uint i = 0; i < m_dataElementListCount; i++) m_v.append(QString()); } GncRecurrence::~GncRecurrence() { delete m_vpStartDate; } GncObject *GncRecurrence::startSubEl() { TRY { if (pMain->xmldebug) qDebug("Recurrence start subel m_state %d", m_state); GncObject *next = 0; switch (m_state) { case STARTDATE: next = new GncDate; break; default: throw MYMONEYEXCEPTION("GncRecurrence rcvd invalid m_state"); } return (next); } PASS } void GncRecurrence::endSubEl(GncObject *subObj) { if (pMain->xmldebug) qDebug("Recurrence end subel"); switch (m_state) { case STARTDATE: m_vpStartDate = static_cast(subObj); break; } m_dataPtr = 0; return ; } void GncRecurrence::terminate() { pMain->convertRecurrence(this); return ; } QString GncRecurrence::getFrequency() const { // This function converts a gnucash 2.2 recurrence specification into it's previous equivalent // This will all need re-writing when MTE finishes the schedule re-write if (periodType() == "once") return("once"); if ((periodType() == "day") && (mult() == "1")) return("daily"); if (periodType() == "week") { if (mult() == "1") return ("weekly"); if (mult() == "2") return ("bi_weekly"); if (mult() == "4") return ("four-weekly"); } if (periodType() == "month") { if (mult() == "1") return ("monthly"); if (mult() == "2") return ("two-monthly"); if (mult() == "3") return ("quarterly"); if (mult() == "4") return ("tri_annually"); if (mult() == "6") return ("semi_yearly"); if (mult() == "12") return ("yearly"); if (mult() == "24") return ("two-yearly"); } return ("unknown"); } //************* GncSchedDef******************************************** GncSchedDef::GncSchedDef() { // process ing for this sub-object is undefined at the present time m_subElementListCount = 0; m_dataElementListCount = 0; } GncSchedDef::~GncSchedDef() {} /************************************************************************************************ XML Reader ************************************************************************************************/ XmlReader::XmlReader(MyMoneyGncReader *pM) : m_source(0), m_reader(0), m_co(0), pMain(pM), m_headerFound(false) { } void XmlReader::processFile(QIODevice* pDevice) { m_source = new QXmlInputSource(pDevice); // set up the Qt XML reader m_reader = new QXmlSimpleReader; m_reader->setContentHandler(this); // go read the file if (!m_reader->parse(m_source)) { throw MYMONEYEXCEPTION(i18n("Input file cannot be parsed; may be corrupt\n%1", errorString())); } delete m_reader; delete m_source; return ; } // XML handling routines bool XmlReader::startDocument() { m_co = new GncFile; // create initial object, push to stack , pass it the 'main' pointer m_os.push(m_co); m_co->setPm(pMain); m_headerFound = false; #ifdef _GNCFILEANON pMain->oStream << ""; lastType = -1; indentCount = 0; #endif // _GNCFILEANON return (true); } bool XmlReader::startElement(const QString&, const QString&, const QString& elName , const QXmlAttributes& elAttrs) { try { if (pMain->gncdebug) qDebug() << "XML start -" << elName; #ifdef _GNCFILEANON int i; QString spaces; // anonymizer - write data if (elName == "gnc:book" || elName == "gnc:count-data" || elName == "book:id") lastType = -1; pMain->oStream << endl; switch (lastType) { case 0: indentCount += 2; // tricky fall through here case 2: spaces.fill(' ', indentCount); pMain->oStream << spaces.toLatin1(); break; } pMain->oStream << '<' << elName; for (i = 0; i < elAttrs.count(); ++i) { pMain->oStream << ' ' << elAttrs.qName(i) << '=' << '"' << elAttrs.value(i) << '"'; } pMain->oStream << '>'; lastType = 0; #else if ((!m_headerFound) && (elName != "gnc-v2")) throw MYMONEYEXCEPTION(i18n("Invalid header for file. Should be 'gnc-v2'")); m_headerFound = true; #endif // _GNCFILEANON m_co->checkVersion(elName, elAttrs, pMain->m_versionList); // check if this is a sub object element; if so, push stack and initialize GncObject *temp = m_co->isSubElement(elName, elAttrs); if (temp != 0) { m_os.push(temp); m_co = m_os.top(); m_co->setVersion(elAttrs.value("version")); m_co->setPm(pMain); // pass the 'main' pointer to the sub object // return true; // removed, as we hit a return true anyway } #if 0 // check for a data element if (m_co->isDataElement(elName, elAttrs)) return (true); #endif else { // reduced the above to m_co->isDataElement(elName, elAttrs); } } catch (const MyMoneyException &e) { #ifndef _GNCFILEANON // we can't pass on exceptions here coz the XML reader won't catch them and we just abort KMessageBox::error(0, i18n("Import failed:\n\n%1", e.what()), PACKAGE); qWarning("%s", qPrintable(e.what())); #else qWarning("%s", e->toLatin1()); #endif // _GNCFILEANON } return true; // to keep compiler happy } bool XmlReader::endElement(const QString&, const QString&, const QString&elName) { try { if (pMain->xmldebug) qDebug() << "XML end -" << elName; #ifdef _GNCFILEANON QString spaces; switch (lastType) { case 2: indentCount -= 2; spaces.fill(' ', indentCount); pMain->oStream << endl << spaces.toLatin1(); break; } pMain->oStream << "' ; lastType = 2; #endif // _GNCFILEANON m_co->resetDataPtr(); // so we don't get extraneous data loaded into the variables if (elName == m_co->getElName()) { // check if this is the end of the current object if (pMain->gncdebug) m_co->debugDump(); // dump the object data (temp) // call the terminate routine, pop the stack, and advise the parent that it's done m_co->terminate(); GncObject *temp = m_co; m_os.pop(); m_co = m_os.top(); m_co->endSubEl(temp); } return (true); } catch (const MyMoneyException &e) { #ifndef _GNCFILEANON // we can't pass on exceptions here coz the XML reader won't catch them and we just abort KMessageBox::error(0, i18n("Import failed:\n\n%1", e.what()), PACKAGE); qWarning("%s", qPrintable(e.what())); #else qWarning("%s", e->toLatin1()); #endif // _GNCFILEANON } return (true); // to keep compiler happy } bool XmlReader::characters(const QString &data) { if (pMain->xmldebug) qDebug("XML Data received - %d bytes", data.length()); QString pData = data.trimmed(); // data may contain line feeds and indentation spaces if (!pData.isEmpty()) { if (pMain->developerDebug) qDebug() << "XML Data -" << pData; m_co->storeData(pData); //go store it #ifdef _GNCFILEANON QString anonData = m_co->getData(); if (anonData.isEmpty()) anonData = pData; // there must be a Qt standard way of doing the following but I can't ... find it anonData.replace('<', "<"); anonData.replace('>', ">"); anonData.replace('&', "&"); pMain->oStream << anonData; // write original data lastType = 1; #endif // _GNCFILEANON } return (true); } bool XmlReader::endDocument() { #ifdef _GNCFILEANON pMain->oStream << endl << endl; pMain->oStream << "" << endl; pMain->oStream << "" << endl; pMain->oStream << "" << endl; #endif // _GNCFILEANON return (true); } /******************************************************************************************* Main class for this module Controls overall operation of the importer ********************************************************************************************/ //***************** Constructor *********************** MyMoneyGncReader::MyMoneyGncReader() : m_dropSuspectSchedules(0), m_investmentOption(0), m_useFinanceQuote(0), m_useTxNotes(0), gncdebug(0), xmldebug(0), bAnonymize(0), developerDebug(0), m_xr(0), m_progressCallback(0), m_ccCount(0), m_orCount(0), m_scCount(0), m_potentialTransfer(0), m_suspectSchedule(false) { #ifndef _GNCFILEANON m_storage = 0; #endif // _GNCFILEANON // to hold gnucash count data (only used for progress bar) m_gncCommodityCount = m_gncAccountCount = m_gncTransactionCount = m_gncScheduleCount = 0; m_smallBusinessFound = m_budgetsFound = m_lotsFound = false; m_commodityCount = m_priceCount = m_accountCount = m_transactionCount = m_templateCount = m_scheduleCount = 0; m_decoder = 0; // build a list of valid versions static const QString versionList[] = {"gnc:book 2.0.0", "gnc:commodity 2.0.0", "gnc:pricedb 1", "gnc:account 2.0.0", "gnc:transaction 2.0.0", "gnc:schedxaction 1.0.0", "gnc:schedxaction 2.0.0", // for gnucash 2.2 onward "gnc:freqspec 1.0.0", "zzz" // zzz = stopper }; unsigned int i; for (i = 0; versionList[i] != "zzz"; ++i) m_versionList[versionList[i].section(' ', 0, 0)].append(versionList[i].section(' ', 1, 1)); } //***************** Destructor ************************* MyMoneyGncReader::~MyMoneyGncReader() {} //**************************** Main Entry Point ************************************ #ifndef _GNCFILEANON void MyMoneyGncReader::readFile(QIODevice* pDevice, IMyMoneySerialize* storage) { Q_CHECK_PTR(pDevice); Q_CHECK_PTR(storage); m_storage = dynamic_cast(storage); qDebug("Entering gnucash importer"); setOptions(); // get a file anonymization factor from the user if (bAnonymize) setFileHideFactor(); //m_defaultPayee = createPayee (i18n("Unknown payee")); MyMoneyFile::instance()->attachStorage(m_storage); MyMoneyFileTransaction ft; m_xr = new XmlReader(this); bool blocked = MyMoneyFile::instance()->signalsBlocked(); MyMoneyFile::instance()->blockSignals(true); try { m_xr->processFile(pDevice); terminate(); // do all the wind-up things ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::error(0, i18n("Import failed:\n\n%1", e.what()), PACKAGE); qWarning("%s", qPrintable(e.what())); } // end catch MyMoneyFile::instance()->blockSignals(blocked); MyMoneyFile::instance()->detachStorage(m_storage); signalProgress(0, 1, i18n("Import complete")); // switch off progress bar delete m_xr; qDebug("Exiting gnucash importer"); } #else // Control code for the file anonymizer void MyMoneyGncReader::readFile(QString in, QString out) { QFile pDevice(in); if (!pDevice.open(QIODevice::ReadOnly)) qWarning("Can't open input file"); QFile outFile(out); if (!outFile.open(QIODevice::WriteOnly)) qWarning("Can't open output file"); oStream.setDevice(&outFile); bAnonymize = true; // get a file anonymization factor from the user setFileHideFactor(); m_xr = new XmlReader(this); try { m_xr->processFile(&pDevice); } catch (const MyMoneyException &e) { qWarning("%s", e->toLatin1()); } // end catch delete m_xr; pDevice.close(); outFile.close(); return ; } #include int main(int argc, char ** argv) { QApplication a(argc, argv); MyMoneyGncReader m; QString inFile, outFile; if (argc > 0) inFile = a.argv()[1]; if (argc > 1) outFile = a.argv()[2]; if (inFile.isEmpty()) { inFile = KFileDialog::getOpenFileName("", "Gnucash files(*.nc *)", 0); } if (inFile.isEmpty()) qWarning("Input file required"); if (outFile.isEmpty()) outFile = inFile + ".anon"; m.readFile(inFile, outFile); exit(0); } #endif // _GNCFILEANON void MyMoneyGncReader::setFileHideFactor() { #define MINFILEHIDEF 0.01 #define MAXFILEHIDEF 99.99 srand(QTime::currentTime().second()); // seed randomizer for anonymize m_fileHideFactor = 0.0; while (m_fileHideFactor == 0.0) { m_fileHideFactor = QInputDialog::getDouble(0, i18n("Disguise your wealth"), i18n("Each monetary value on your file will be multiplied by a random number between 0.01 and 1.99\n" "with a different value used for each transaction. In addition, to further disguise the true\n" "values, you may enter a number between %1 and %2 which will be applied to all values.\n" "These numbers will not be stored in the file.", MINFILEHIDEF, MAXFILEHIDEF), (1.0 + (int)(1000.0 * rand() / (RAND_MAX + 1.0))) / 100.0, MINFILEHIDEF, MAXFILEHIDEF, 2); } } #ifndef _GNCFILEANON //********************************* convertCommodity ******************************************* void MyMoneyGncReader::convertCommodity(const GncCommodity *gcm) { Q_CHECK_PTR(gcm); MyMoneySecurity equ; if (m_commodityCount == 0) signalProgress(0, m_gncCommodityCount, i18n("Loading commodities...")); if (!gcm->isCurrency()) { // currencies should not be present here but... equ.setName(gcm->name()); equ.setTradingSymbol(gcm->id()); equ.setTradingMarket(gcm->space()); // the 'space' may be market or quote source, dep on what the user did // don't set the source here since he may not want quotes //equ.setValue ("kmm-online-source", gcm->space()); // we don't know, so use it as both equ.setTradingCurrency(""); // not available here, will set from pricedb or transaction equ.setSecurityType(Security::Stock); // default to it being a stock //tell the storage objects we have a new equity object. equ.setSmallestAccountFraction(gcm->fraction().toInt()); m_storage->addSecurity(equ); //assign the gnucash id as the key into the map to find our id if (gncdebug) qDebug() << "mapping, key =" << gcm->id() << "id =" << equ.id(); m_mapEquities[gcm->id().toUtf8()] = equ.id(); } signalProgress(++m_commodityCount, 0); return ; } //******************************* convertPrice ************************************************ void MyMoneyGncReader::convertPrice(const GncPrice *gpr) { Q_CHECK_PTR(gpr); // add this to our price history if (m_priceCount == 0) signalProgress(0, 1, i18n("Loading prices...")); MyMoneyMoney rate(convBadValue(gpr->value())); if (gpr->commodity()->isCurrency()) { MyMoneyPrice exchangeRate(gpr->commodity()->id().toUtf8(), gpr->currency()->id().toUtf8(), gpr->priceDate(), rate, i18n("Imported History")); if (!exchangeRate.rate(QString()).isZero()) m_storage->addPrice(exchangeRate); } else { MyMoneySecurity e = m_storage->security(m_mapEquities[gpr->commodity()->id().toUtf8()]); if (gncdebug) qDebug() << "Searching map, key = " << gpr->commodity()->id() << ", found id =" << e.id().data(); e.setTradingCurrency(gpr->currency()->id().toUtf8()); MyMoneyPrice stockPrice(e.id(), gpr->currency()->id().toUtf8(), gpr->priceDate(), rate, i18n("Imported History")); if (!stockPrice.rate(QString()).isZero()) m_storage->addPrice(stockPrice); m_storage->modifySecurity(e); } signalProgress(++m_priceCount, 0); return ; } //*********************************convertAccount **************************************** void MyMoneyGncReader::convertAccount(const GncAccount* gac) { Q_CHECK_PTR(gac); TRY { // we don't care about the GNC root account if ("ROOT" == gac->type()) { m_rootId = gac->id().toUtf8(); return; } MyMoneyAccount acc; if (m_accountCount == 0) signalProgress(0, m_gncAccountCount, i18n("Loading accounts...")); acc.setName(gac->name()); acc.setDescription(gac->desc()); QDate currentDate = QDate::currentDate(); acc.setOpeningDate(currentDate); acc.setLastModified(currentDate); acc.setLastReconciliationDate(currentDate); if (gac->commodity()->isCurrency()) { acc.setCurrencyId(gac->commodity()->id().toUtf8()); m_currencyCount[gac->commodity()->id()]++; } acc.setParentAccountId(gac->parent().toUtf8()); // now determine the account type and its parent id /* This list taken from # Feb 2006: A RELAX NG Compact schema for gnucash "v2" XML files. # Copyright (C) 2006 Joshua Sled "NO_TYPE" "BANK" "CASH" "CREDIT" "ASSET" "LIABILITY" "STOCK" "MUTUAL" "CURRENCY" "INCOME" "EXPENSE" "EQUITY" "RECEIVABLE" "PAYABLE" "CHECKING" "SAVINGS" "MONEYMRKT" "CREDITLINE" Some don't seem to be used in practice. Not sure what CREDITLINE s/be converted as. */ if ("BANK" == gac->type() || "CHECKING" == gac->type()) { acc.setAccountType(Account::Checkings); } else if ("SAVINGS" == gac->type()) { acc.setAccountType(Account::Savings); } else if ("ASSET" == gac->type()) { acc.setAccountType(Account::Asset); } else if ("CASH" == gac->type()) { acc.setAccountType(Account::Cash); } else if ("CURRENCY" == gac->type()) { acc.setAccountType(Account::Cash); } else if ("STOCK" == gac->type() || "MUTUAL" == gac->type()) { // gnucash allows a 'broker' account to be denominated as type STOCK, but with // a currency balance. We do not need to create a stock account for this // actually, the latest version of gnc (1.8.8) doesn't seem to allow you to do // this any more, though I do have one in my own account... if (gac->commodity()->isCurrency()) { acc.setAccountType(Account::Investment); } else { acc.setAccountType(Account::Stock); } } else if ("EQUITY" == gac->type()) { acc.setAccountType(Account::Equity); } else if ("LIABILITY" == gac->type()) { acc.setAccountType(Account::Liability); } else if ("CREDIT" == gac->type()) { acc.setAccountType(Account::CreditCard); } else if ("INCOME" == gac->type()) { acc.setAccountType(Account::Income); } else if ("EXPENSE" == gac->type()) { acc.setAccountType(Account::Expense); } else if ("RECEIVABLE" == gac->type()) { acc.setAccountType(Account::Asset); } else if ("PAYABLE" == gac->type()) { acc.setAccountType(Account::Liability); } else if ("MONEYMRKT" == gac->type()) { acc.setAccountType(Account::MoneyMarket); } else { // we have here an account type we can't currently handle QString em = i18n("Current importer does not recognize GnuCash account type %1", gac->type()); throw MYMONEYEXCEPTION(em); } // if no parent account is present, assign to one of our standard accounts if ((acc.parentAccountId().isEmpty()) || (acc.parentAccountId() == m_rootId)) { switch (acc.accountGroup()) { case Account::Asset: acc.setParentAccountId(m_storage->asset().id()); break; case Account::Liability: acc.setParentAccountId(m_storage->liability().id()); break; case Account::Income: acc.setParentAccountId(m_storage->income().id()); break; case Account::Expense: acc.setParentAccountId(m_storage->expense().id()); break; case Account::Equity: acc.setParentAccountId(m_storage->equity().id()); break; default: break; // not necessary but avoids compiler warnings } } // extra processing for a stock account if (acc.accountType() == Account::Stock) { // save the id for later linking to investment account m_stockList.append(gac->id()); // set the equity type MyMoneySecurity e = m_storage->security(m_mapEquities[gac->commodity()->id().toUtf8()]); if (gncdebug) qDebug() << "Acct equity search, key =" << gac->commodity()->id() << "found id =" << e.id(); acc.setCurrencyId(e.id()); // actually, the security id if ("MUTUAL" == gac->type()) { e.setSecurityType(Security::MutualFund); if (gncdebug) qDebug() << "Setting" << e.name() << "to mutual"; m_storage->modifySecurity(e); } QString priceSource = gac->getKvpValue("price-source", "string"); if (!priceSource.isEmpty()) getPriceSource(e, priceSource); } if (gac->getKvpValue("tax-related", "integer") == QChar('1')) acc.setValue("Tax", "Yes"); // all the details from the file about the account should be known by now. // calling addAccount will automatically fill in the account ID. m_storage->addAccount(acc); m_mapIds[gac->id().toUtf8()] = acc.id(); // to link gnucash id to ours for tx posting if (gncdebug) qDebug() << "Gnucash account" << gac->id() << "has id of" << acc.id() << ", type of" << MyMoneyAccount::accountTypeToString(acc.accountType()) << "parent is" << acc.parentAccountId(); signalProgress(++m_accountCount, 0); return ; } PASS } //********************************************** convertTransaction ***************************** void MyMoneyGncReader::convertTransaction(const GncTransaction *gtx) { Q_CHECK_PTR(gtx); MyMoneyTransaction tx; MyMoneySplit split; unsigned int i; if (m_transactionCount == 0) signalProgress(0, m_gncTransactionCount, i18n("Loading transactions...")); // initialize class variables related to transactions m_txCommodity = ""; m_txPayeeId = ""; m_potentialTransfer = true; m_splitList.clear(); m_liabilitySplitList.clear(); m_otherSplitList.clear(); // payee, dates, commodity if (!gtx->desc().isEmpty()) m_txPayeeId = createPayee(gtx->desc()); tx.setEntryDate(gtx->dateEntered()); tx.setPostDate(gtx->datePosted()); m_txDatePosted = tx.postDate(); // save for use in splits m_txChequeNo = gtx->no(); // ditto tx.setCommodity(gtx->currency().toUtf8()); m_txCommodity = tx.commodity(); // save in storage, maybe needed for Orphan accounts // process splits for (i = 0; i < gtx->splitCount(); i++) { convertSplit(static_cast(gtx->getSplit(i))); } // handle the odd case of just one split, which gnc allows, // by just duplicating the split // of course, we should change the sign but this case has only ever been seen // when the balance is zero, and can cause kmm to crash, so... if (gtx->splitCount() == 1) { convertSplit(static_cast(gtx->getSplit(0))); } m_splitList += m_liabilitySplitList += m_otherSplitList; // the splits are in order in splitList. Link them to the tx. also, determine the // action type, and fill in some fields which gnc holds at transaction level // first off, is it a transfer (can only have 2 splits?) // also, a tx with just 2 splits is shown by GnuCash as non-split bool nonSplitTx = true; if (m_splitList.count() != 2) { m_potentialTransfer = false; nonSplitTx = false; } QString slotMemo = gtx->getKvpValue(QString("notes")); if (!slotMemo.isEmpty()) tx.setMemo(slotMemo); QList::iterator it = m_splitList.begin(); while (!m_splitList.isEmpty()) { split = *it; // at this point, if m_potentialTransfer is still true, it is actually one! if (m_potentialTransfer) split.setAction(MyMoneySplit::ActionTransfer); if ((m_useTxNotes) // if use txnotes option is set && (nonSplitTx) // and it's a (GnuCash) non-split transaction && (!tx.memo().isEmpty())) // and tx notes are present split.setMemo(tx.memo()); // use the tx notes as memo tx.addSplit(split); it = m_splitList.erase(it); } m_storage->addTransaction(tx, true); // all done, add the transaction to storage signalProgress(++m_transactionCount, 0); return ; } //******************************************convertSplit******************************** void MyMoneyGncReader::convertSplit(const GncSplit *gsp) { Q_CHECK_PTR(gsp); MyMoneySplit split; MyMoneyAccount splitAccount; // find the kmm account id corresponding to the gnc id QString kmmAccountId; map_accountIds::const_iterator id = m_mapIds.constFind(gsp->acct().toUtf8()); if (id != m_mapIds.constEnd()) { kmmAccountId = id.value(); } else { // for the case where the acs not found (which shouldn't happen?), create an account with gnc name kmmAccountId = createOrphanAccount(gsp->acct()); } // find the account pointer and save for later splitAccount = m_storage->account(kmmAccountId); // print some data so we can maybe identify this split later // TODO : prints personal data //if (gncdebug) qDebug ("Split data - gncid %s, kmmid %s, memo %s, value %s, recon state %s", // gsp->acct().toLatin1(), kmmAccountId.data(), gsp->memo().toLatin1(), gsp->value().toLatin1(), // gsp->recon().toLatin1()); // payee id split.setPayeeId(m_txPayeeId.toUtf8()); // reconciled state and date switch (gsp->recon().at(0).toLatin1()) { case 'n': split.setReconcileFlag(eMyMoney::Split::State::NotReconciled); break; case 'c': split.setReconcileFlag(eMyMoney::Split::State::Cleared); break; case 'y': split.setReconcileFlag(eMyMoney::Split::State::Reconciled); break; } split.setReconcileDate(gsp->reconDate()); // memo split.setMemo(gsp->memo()); // accountId split.setAccountId(kmmAccountId); // cheque no split.setNumber(m_txChequeNo); // value and quantity MyMoneyMoney splitValue(convBadValue(gsp->value())); if (gsp->value() == "-1/0") { // treat gnc invalid value as zero // it's not quite a consistency check, but easier to treat it as such m_messageList["CC"].append (i18n("Account or Category %1, transaction date %2; split contains invalid value; please check", splitAccount.name(), m_txDatePosted.toString(Qt::ISODate))); } MyMoneyMoney splitQuantity(convBadValue(gsp->qty())); split.setValue(splitValue); // if split currency = tx currency, set shares = value (14/10/05) if (splitAccount.currencyId() == m_txCommodity) { split.setShares(splitValue); } else { split.setShares(splitQuantity); } // in kmm, the first split is important. in this routine we will // save the splits in our split list with the priority: // 1. assets // 2. liabilities // 3. others (categories) // but keeping each in same order as gnucash switch (splitAccount.accountGroup()) { case Account::Asset: if (splitAccount.accountType() == Account::Stock) { split.value().isZero() ? split.setAction(MyMoneySplit::ActionAddShares) : // free shares? split.setAction(MyMoneySplit::ActionBuyShares); m_potentialTransfer = false; // ? // add a price history entry MyMoneySecurity e = m_storage->security(splitAccount.currencyId()); MyMoneyMoney price; if (!split.shares().isZero()) { static const signed64 NEW_DENOM = 10000; price = split.value() / split.shares(); price = MyMoneyMoney(price.toDouble(), NEW_DENOM); } if (!price.isZero()) { TRY { // we can't use m_storage->security coz security list is not built yet m_storage->currency(m_txCommodity); // will throw exception if not currency e.setTradingCurrency(m_txCommodity); if (gncdebug) qDebug() << "added price for" << e.name() << price.toString() << "date" << m_txDatePosted.toString(Qt::ISODate); m_storage->modifySecurity(e); MyMoneyPrice dealPrice(e.id(), m_txCommodity, m_txDatePosted, price, i18n("Imported Transaction")); m_storage->addPrice(dealPrice); } CATCH { // stock transfer; treat like free shares? split.setAction(MyMoneySplit::ActionAddShares); } } } else { // not stock if (split.value().isNegative()) { bool isNumeric = false; if (!split.number().isEmpty()) { split.number().toLong(&isNumeric); // No QString.isNumeric()?? } if (isNumeric) { split.setAction(MyMoneySplit::ActionCheck); } else { split.setAction(MyMoneySplit::ActionWithdrawal); } } else { split.setAction(MyMoneySplit::ActionDeposit); } } m_splitList.append(split); break; case Account::Liability: split.value().isNegative() ? split.setAction(MyMoneySplit::ActionWithdrawal) : split.setAction(MyMoneySplit::ActionDeposit); m_liabilitySplitList.append(split); break; default: m_potentialTransfer = false; m_otherSplitList.append(split); } // backdate the account opening date if necessary if (m_txDatePosted < splitAccount.openingDate()) { splitAccount.setOpeningDate(m_txDatePosted); m_storage->modifyAccount(splitAccount); } return ; } //********************************* convertTemplateTransaction ********************************************** MyMoneyTransaction MyMoneyGncReader::convertTemplateTransaction(const QString& schedName, const GncTransaction *gtx) { Q_CHECK_PTR(gtx); MyMoneyTransaction tx; MyMoneySplit split; unsigned int i; if (m_templateCount == 0) signalProgress(0, 1, i18n("Loading templates...")); // initialize class variables related to transactions m_txCommodity = ""; m_txPayeeId = ""; m_potentialTransfer = true; m_splitList.clear(); m_liabilitySplitList.clear(); m_otherSplitList.clear(); // payee, dates, commodity if (!gtx->desc().isEmpty()) { m_txPayeeId = createPayee(gtx->desc()); } else { m_txPayeeId = createPayee(i18n("Unknown payee")); // schedules require a payee tho normal tx's don't. not sure why... } tx.setEntryDate(gtx->dateEntered()); tx.setPostDate(gtx->datePosted()); m_txDatePosted = tx.postDate(); tx.setCommodity(gtx->currency().toUtf8()); m_txCommodity = tx.commodity(); // save for possible use in orphan account // process splits for (i = 0; i < gtx->splitCount(); i++) { convertTemplateSplit(schedName, static_cast(gtx->getSplit(i))); } // determine the action type for the splits and link them to the template tx if (!m_otherSplitList.isEmpty()) m_potentialTransfer = false; // tfrs can occur only between assets and asset/liabilities m_splitList += m_liabilitySplitList += m_otherSplitList; // the splits are in order in splitList. Transfer them to the tx // also, determine the action type. first off, is it a transfer (can only have 2 splits?) if (m_splitList.count() != 2) m_potentialTransfer = false; // at this point, if m_potentialTransfer is still true, it is actually one! QString txMemo = ""; QList::iterator it = m_splitList.begin(); while (!m_splitList.isEmpty()) { split = *it; if (m_potentialTransfer) { split.setAction(MyMoneySplit::ActionTransfer); } else { if (split.value().isNegative()) { //split.setAction (negativeActionType); split.setAction(MyMoneySplit::ActionWithdrawal); } else { //split.setAction (positiveActionType); split.setAction(MyMoneySplit::ActionDeposit); } } split.setNumber(gtx->no()); // set cheque no (or equivalent description) // Arbitrarily, save the first non-null split memo as the memo for the whole tx // I think this is necessary because txs with just 2 splits (the majority) // are not viewable as split transactions in kmm so the split memo is not seen if ((txMemo.isEmpty()) && (!split.memo().isEmpty())) txMemo = split.memo(); tx.addSplit(split); it = m_splitList.erase(it); } // memo - set from split tx.setMemo(txMemo); signalProgress(++m_templateCount, 0); return (tx); } //********************************* convertTemplateSplit **************************************************** void MyMoneyGncReader::convertTemplateSplit(const QString& schedName, const GncTemplateSplit *gsp) { Q_CHECK_PTR(gsp); // convertTemplateSplit MyMoneySplit split; MyMoneyAccount splitAccount; unsigned int i, j; bool nonNumericFormula = false; // action, value and account will be set from slots // reconcile state, always Not since it hasn't even been posted yet (?) split.setReconcileFlag(eMyMoney::Split::State::NotReconciled); // memo split.setMemo(gsp->memo()); // payee id split.setPayeeId(m_txPayeeId.toUtf8()); // read split slots (KVPs) int xactionCount = 0; int validSlotCount = 0; QString gncAccountId; for (i = 0; i < gsp->kvpCount(); i++) { const GncKvp& slot = gsp->getKvp(i); if ((slot.key() == "sched-xaction") && (slot.type() == "frame")) { bool bFoundStringCreditFormula = false; bool bFoundStringDebitFormula = false; bool bFoundGuidAccountId = false; QString gncCreditFormula, gncDebitFormula; for (j = 0; j < slot.kvpCount(); j++) { const GncKvp& subSlot = slot.getKvp(j); // again, see comments above. when we have a full specification // of all the options available to us, we can no doubt improve on this if ((subSlot.key() == "credit-formula") && (subSlot.type() == "string")) { gncCreditFormula = subSlot.value(); bFoundStringCreditFormula = true; } if ((subSlot.key() == "debit-formula") && (subSlot.type() == "string")) { gncDebitFormula = subSlot.value(); bFoundStringDebitFormula = true; } if ((subSlot.key() == "account") && (subSlot.type() == "guid")) { gncAccountId = subSlot.value(); bFoundGuidAccountId = true; } } // all data read, now check we have everything if ((bFoundStringCreditFormula) && (bFoundStringDebitFormula) && (bFoundGuidAccountId)) { if (gncdebug) qDebug() << "Found valid slot; credit" << gncCreditFormula << "debit" << gncDebitFormula << "acct" << gncAccountId; validSlotCount++; } // validate numeric, work out sign MyMoneyMoney exFormula; exFormula.setNegativeMonetarySignPosition(MyMoneyMoney::BeforeQuantityMoney); QString numericTest; char crdr = 0 ; if (!gncCreditFormula.isEmpty()) { crdr = 'C'; numericTest = gncCreditFormula; } else if (!gncDebitFormula.isEmpty()) { crdr = 'D'; numericTest = gncDebitFormula; } kMyMoneyMoneyValidator v(0); int pos; // useless, but required for validator if (v.validate(numericTest, pos) == QValidator::Acceptable) { switch (crdr) { case 'C': exFormula = QString("-" + numericTest); break; case 'D': exFormula = numericTest; } } else { if (gncdebug) qDebug() << numericTest << "is not numeric"; nonNumericFormula = true; } split.setValue(exFormula); xactionCount++; } else { m_messageList["SC"].append( i18n("Schedule %1 contains unknown action (key = %2, type = %3)", schedName, slot.key(), slot.type())); m_suspectSchedule = true; } } // report this as untranslatable tx if (xactionCount > 1) { m_messageList["SC"].append( i18n("Schedule %1 contains multiple actions; only one has been imported", schedName)); m_suspectSchedule = true; } if (validSlotCount == 0) { m_messageList["SC"].append( i18n("Schedule %1 contains no valid splits", schedName)); m_suspectSchedule = true; } if (nonNumericFormula) { m_messageList["SC"].append( i18n("Schedule %1 appears to contain a formula. GnuCash formulae are not convertible", schedName)); m_suspectSchedule = true; } // find the kmm account id corresponding to the gnc id QString kmmAccountId; map_accountIds::const_iterator id = m_mapIds.constFind(gncAccountId.toUtf8()); if (id != m_mapIds.constEnd()) { kmmAccountId = id.value(); } else { // for the case where the acs not found (which shouldn't happen?), create an account with gnc name kmmAccountId = createOrphanAccount(gncAccountId); } splitAccount = m_storage->account(kmmAccountId); split.setAccountId(kmmAccountId); // if split currency = tx currency, set shares = value (14/10/05) if (splitAccount.currencyId() == m_txCommodity) { split.setShares(split.value()); } /* else { //FIXME: scheduled currency or investment tx needs to be investigated split.setShares (splitQuantity); } */ // add the split to one of the lists switch (splitAccount.accountGroup()) { case Account::Asset: m_splitList.append(split); break; case Account::Liability: m_liabilitySplitList.append(split); break; default: m_otherSplitList.append(split); } // backdate the account opening date if necessary if (m_txDatePosted < splitAccount.openingDate()) { splitAccount.setOpeningDate(m_txDatePosted); m_storage->modifyAccount(splitAccount); } return ; } //********************************* convertSchedule ******************************************************** void MyMoneyGncReader::convertSchedule(const GncSchedule *gsc) { TRY { Q_CHECK_PTR(gsc); MyMoneySchedule sc; MyMoneyTransaction tx; m_suspectSchedule = false; QDate startDate, nextDate, lastDate, endDate; // for date calculations QDate today = QDate::currentDate(); int numOccurs, remOccurs; if (m_scheduleCount == 0) signalProgress(0, m_gncScheduleCount, i18n("Loading schedules...")); // schedule name sc.setName(gsc->name()); // find the transaction template as stored earlier QList::const_iterator itt; for (itt = m_templateList.constBegin(); itt != m_templateList.constEnd(); ++itt) { // the id to match against is the split:account value in the splits if (static_cast((*itt)->getSplit(0))->acct() == gsc->templId()) break; } if (itt == m_templateList.constEnd()) { throw MYMONEYEXCEPTION(i18n("Cannot find template transaction for schedule %1", sc.name())); } else { tx = convertTemplateTransaction(sc.name(), *itt); } tx.clearId(); // define the conversion table for intervals struct convIntvl { QString gncType; // the gnucash name unsigned char interval; // for date calculation unsigned int intervalCount; Schedule::Occurrence occ; // equivalent occurrence code Schedule::WeekendOption wo; }; /* other intervals supported by gnc according to Josh Sled's schema (see above) "none" "semi_monthly" */ /* some of these type names do not appear in gnucash and are difficult to generate for pre 2.2 files.They can be generated for 2.2 however, by GncRecurrence::getFrequency() */ static convIntvl vi [] = { {"once", 'o', 1, Schedule::Occurrence::Once, Schedule::WeekendOption::MoveNothing }, {"daily" , 'd', 1, Schedule::Occurrence::Daily, Schedule::WeekendOption::MoveNothing }, //{"daily_mf", 'd', 1, Schedule::Occurrence::Daily, Schedule::WeekendOption::MoveAfter }, doesn't work, need new freq in kmm {"30-days" , 'd', 30, Schedule::Occurrence::EveryThirtyDays, Schedule::WeekendOption::MoveNothing }, {"weekly", 'w', 1, Schedule::Occurrence::Weekly, Schedule::WeekendOption::MoveNothing }, {"bi_weekly", 'w', 2, Schedule::Occurrence::EveryOtherWeek, Schedule::WeekendOption::MoveNothing }, {"three-weekly", 'w', 3, Schedule::Occurrence::EveryThreeWeeks, Schedule::WeekendOption::MoveNothing }, {"four-weekly", 'w', 4, Schedule::Occurrence::EveryFourWeeks, Schedule::WeekendOption::MoveNothing }, {"eight-weekly", 'w', 8, Schedule::Occurrence::EveryEightWeeks, Schedule::WeekendOption::MoveNothing }, {"monthly", 'm', 1, Schedule::Occurrence::Monthly, Schedule::WeekendOption::MoveNothing }, {"two-monthly", 'm', 2, Schedule::Occurrence::EveryOtherMonth, Schedule::WeekendOption::MoveNothing }, {"quarterly", 'm', 3, Schedule::Occurrence::Quarterly, Schedule::WeekendOption::MoveNothing }, {"tri_annually", 'm', 4, Schedule::Occurrence::EveryFourMonths, Schedule::WeekendOption::MoveNothing }, {"semi_yearly", 'm', 6, Schedule::Occurrence::TwiceYearly, Schedule::WeekendOption::MoveNothing }, {"yearly", 'y', 1, Schedule::Occurrence::Yearly, Schedule::WeekendOption::MoveNothing }, {"two-yearly", 'y', 2, Schedule::Occurrence::EveryOtherYear, Schedule::WeekendOption::MoveNothing }, {"zzz", 'y', 1, Schedule::Occurrence::Yearly, Schedule::WeekendOption::MoveNothing} // zzz = stopper, may cause problems. what else can we do? }; QString frequency = "unknown"; // set default to unknown frequency bool unknownOccurs = false; // may have zero, or more than one frequency/recurrence spec QString schedEnabled; if (gsc->version() == "2.0.0") { if (gsc->m_vpRecurrence.count() != 1) { unknownOccurs = true; } else { const GncRecurrence *gre = gsc->m_vpRecurrence.first(); //qDebug (QString("Sched %1, pt %2, mu %3, sd %4").arg(gsc->name()).arg(gre->periodType()) // .arg(gre->mult()).arg(gre->startDate().toString(Qt::ISODate))); frequency = gre->getFrequency(); schedEnabled = gsc->enabled(); } sc.setOccurrence(Schedule::Occurrence::Once); // FIXME - how to convert } else { // find this interval const GncFreqSpec *fs = gsc->getFreqSpec(); if (fs == 0) { unknownOccurs = true; } else { frequency = fs->intervalType(); if (!fs->m_fsList.isEmpty()) unknownOccurs = true; // nested freqspec } schedEnabled = 'y'; // earlier versions did not have an enable flag } int i; for (i = 0; vi[i].gncType != "zzz"; i++) { if (frequency == vi[i].gncType) break; } if (vi[i].gncType == "zzz") { m_messageList["SC"].append( i18n("Schedule %1 has interval of %2 which is not currently available", sc.name(), frequency)); i = 0; // treat as single occurrence m_suspectSchedule = true; } if (unknownOccurs) { m_messageList["SC"].append( i18n("Schedule %1 contains unknown interval specification; please check for correct operation", sc.name())); m_suspectSchedule = true; } // set the occurrence interval, weekend option, start date sc.setOccurrence(vi[i].occ); sc.setWeekendOption(vi[i].wo); sc.setStartDate(gsc->startDate()); // if a last date was specified, use it, otherwise try to work out the last date sc.setLastPayment(gsc->lastDate()); numOccurs = gsc->numOccurs().toInt(); if (sc.lastPayment() == QDate()) { nextDate = lastDate = gsc->startDate(); while ((nextDate < today) && (numOccurs-- != 0)) { lastDate = nextDate; nextDate = incrDate(lastDate, vi[i].interval, vi[i].intervalCount); } sc.setLastPayment(lastDate); } // under Tom's new regime, the tx dates are the next due date (I think) tx.setPostDate(incrDate(sc.lastPayment(), vi[i].interval, vi[i].intervalCount)); tx.setEntryDate(incrDate(sc.lastPayment(), vi[i].interval, vi[i].intervalCount)); // if an end date was specified, use it, otherwise if the input file had a number // of occurs remaining, work out the end date sc.setEndDate(gsc->endDate()); numOccurs = gsc->numOccurs().toInt(); remOccurs = gsc->remOccurs().toInt(); if ((sc.endDate() == QDate()) && (remOccurs > 0)) { endDate = sc.lastPayment(); while (remOccurs-- > 0) { endDate = incrDate(endDate, vi[i].interval, vi[i].intervalCount); } sc.setEndDate(endDate); } // Check for sched deferred interval. Don't know how/if we can handle it, or even what it means... if (gsc->getSchedDef() != 0) { m_messageList["SC"].append( i18n("Schedule %1 contains a deferred interval specification; please check for correct operation", sc.name())); m_suspectSchedule = true; } // payment type, options sc.setPaymentType((Schedule::PaymentType)Schedule::PaymentType::Other); sc.setFixed(!m_suspectSchedule); // if any probs were found, set it as variable so user will always be prompted // we don't currently have a 'disable' option, but just make sure auto-enter is off if not enabled //qDebug(QString("%1 and %2").arg(gsc->autoCreate()).arg(schedEnabled)); sc.setAutoEnter((gsc->autoCreate() == QChar('y')) && (schedEnabled == QChar('y'))); //qDebug(QString("autoEnter set to %1").arg(sc.autoEnter())); // type QString actionType = tx.splits().first().action(); if (actionType == MyMoneySplit::ActionDeposit) { sc.setType((Schedule::Type)Schedule::Type::Deposit); } else if (actionType == MyMoneySplit::ActionTransfer) { sc.setType((Schedule::Type)Schedule::Type::Transfer); } else { sc.setType((Schedule::Type)Schedule::Type::Bill); } // finally, set the transaction pointer sc.setTransaction(tx); //tell the storage objects we have a new schedule object. if (m_suspectSchedule && m_dropSuspectSchedules) { m_messageList["SC"].append( i18n("Schedule %1 dropped at user request", sc.name())); } else { m_storage->addSchedule(sc); if (m_suspectSchedule) m_suspectList.append(sc.id()); } signalProgress(++m_scheduleCount, 0); return ; } PASS } //********************************* convertFreqSpec ******************************************************** void MyMoneyGncReader::convertFreqSpec(const GncFreqSpec *) { // Nowt to do here at the moment, convertSched only retrieves the interval type // but we will probably need to look into the nested freqspec when we properly implement semi-monthly and stuff return ; } //********************************* convertRecurrence ******************************************************** void MyMoneyGncReader::convertRecurrence(const GncRecurrence *) { return ; } //********************************************************************************************************** //************************************* terminate ********************************************************** void MyMoneyGncReader::terminate() { TRY { // All data has been converted and added to storage // this code is just temporary to show us what is in the file. if (gncdebug) qDebug("%d accounts found in the GnuCash file", (unsigned int)m_mapIds.count()); for (map_accountIds::const_iterator it = m_mapIds.constBegin(); it != m_mapIds.constEnd(); ++it) { if (gncdebug) qDebug() << "key =" << it.key() << "value =" << it.value(); } // first step is to implement the users investment option, now we // have all the accounts available QList::iterator stocks; for (stocks = m_stockList.begin(); stocks != m_stockList.end(); ++stocks) { checkInvestmentOption(*stocks); } // Next step is to walk the list and assign the parent/child relationship between the objects. unsigned int i = 0; signalProgress(0, m_accountCount, i18n("Reorganizing accounts...")); QList list; QList::iterator acc; m_storage->accountList(list); for (acc = list.begin(); acc != list.end(); ++acc) { if ((*acc).parentAccountId() == m_storage->asset().id()) { MyMoneyAccount assets = m_storage->asset(); m_storage->addAccount(assets, (*acc)); if (gncdebug) qDebug() << "Account id" << (*acc).id() << "is a child of the main asset account"; } else if ((*acc).parentAccountId() == m_storage->liability().id()) { MyMoneyAccount liabilities = m_storage->liability(); m_storage->addAccount(liabilities, (*acc)); if (gncdebug) qDebug() << "Account id" << (*acc).id() << "is a child of the main liability account"; } else if ((*acc).parentAccountId() == m_storage->income().id()) { MyMoneyAccount incomes = m_storage->income(); m_storage->addAccount(incomes, (*acc)); if (gncdebug) qDebug() << "Account id" << (*acc).id() << "is a child of the main income account"; } else if ((*acc).parentAccountId() == m_storage->expense().id()) { MyMoneyAccount expenses = m_storage->expense(); m_storage->addAccount(expenses, (*acc)); if (gncdebug) qDebug() << "Account id" << (*acc).id() << "is a child of the main expense account"; } else if ((*acc).parentAccountId() == m_storage->equity().id()) { MyMoneyAccount equity = m_storage->equity(); m_storage->addAccount(equity, (*acc)); if (gncdebug) qDebug() << "Account id" << (*acc).id() << "is a child of the main equity account"; } else if ((*acc).parentAccountId() == m_rootId) { if (gncdebug) qDebug() << "Account id" << (*acc).id() << "is a child of the main root account"; } else { // it is not under one of the main accounts, so find gnucash parent QString parentKey = (*acc).parentAccountId(); if (gncdebug) qDebug() << "Account id" << (*acc).id() << "is a child of " << (*acc).parentAccountId(); map_accountIds::const_iterator id = m_mapIds.constFind(parentKey); if (id != m_mapIds.constEnd()) { if (gncdebug) qDebug() << "Setting account id" << (*acc).id() << "parent account id to" << id.value(); MyMoneyAccount parent = m_storage->account(id.value()); parent = checkConsistency(parent, (*acc)); m_storage->addAccount(parent, (*acc)); } else { throw MYMONEYEXCEPTION("terminate() could not find account id"); } } signalProgress(++i, 0); } // end for account signalProgress(0, 1, (".")); // debug - get rid of reorg message // offer the most common account currency as a default QString mainCurrency = ""; unsigned int maxCount = 0; QMap::ConstIterator it; for (it = m_currencyCount.constBegin(); it != m_currencyCount.constEnd(); ++it) { if (it.value() > maxCount) { maxCount = it.value(); mainCurrency = it.key(); } } if (mainCurrency != "") { QString question = i18n("Your main currency seems to be %1 (%2); do you want to set this as your base currency?", mainCurrency, m_storage->currency(mainCurrency.toUtf8()).name()); if (KMessageBox::questionYesNo(0, question, PACKAGE) == KMessageBox::Yes) { m_storage->setValue("kmm-baseCurrency", mainCurrency); } } // now produce the end of job reports - first, work out which ones are required QList sectionsToReport; // list of sections needing report sectionsToReport.append("MN"); // always build the main section if ((m_ccCount = m_messageList["CC"].count()) > 0) sectionsToReport.append("CC"); if ((m_orCount = m_messageList["OR"].count()) > 0) sectionsToReport.append("OR"); if ((m_scCount = m_messageList["SC"].count()) > 0) sectionsToReport.append("SC"); // produce the sections in separate message boxes bool exit = false; int si; for (si = 0; (si < sectionsToReport.count()) && !exit; ++si) { QString button0Text = i18nc("Button to show more detailed data", "More"); if (si + 1 == sectionsToReport.count()) button0Text = i18nc("Button to close the current dialog", "Done"); // last section KGuiItem yesItem(button0Text, QIcon(), "", ""); KGuiItem noItem(i18n("Save Report"), QIcon(), "", ""); switch (KMessageBox::questionYesNoCancel(0, buildReportSection(sectionsToReport[si]), PACKAGE, yesItem, noItem)) { case KMessageBox::Yes: break; case KMessageBox::No: exit = writeReportToFile(sectionsToReport); break; default: exit = true; break; } } for (si = 0; si < m_suspectList.count(); ++si) { MyMoneySchedule sc = m_storage->schedule(m_suspectList[si]); KEditScheduleDlg *s; switch (KMessageBox::warningYesNo(0, i18n("Problems were encountered in converting schedule '%1'.\nDo you want to review or edit it now?", sc.name()), PACKAGE)) { case KMessageBox::Yes: s = new KEditScheduleDlg(sc); if (s->exec()) m_storage->modifySchedule(s->schedule()); delete s; break; default: break; } } } PASS } //************************************ buildReportSection************************************ QString MyMoneyGncReader::buildReportSection(const QString& source) { TRY { QString s = ""; bool more = false; if (source == "MN") { s.append(i18n("Found:\n\n")); s.append(i18np("%1 commodity (equity)\n", "%1 commodities (equities)\n", m_commodityCount)); s.append(i18np("%1 price\n", "%1 prices\n", m_priceCount)); s.append(i18np("%1 account\n", "%1 accounts\n", m_accountCount)); s.append(i18np("%1 transaction\n", "%1 transactions\n", m_transactionCount)); s.append(i18np("%1 schedule\n", "%1 schedules\n", m_scheduleCount)); s.append("\n\n"); if (m_ccCount == 0) { s.append(i18n("No inconsistencies were detected\n")); } else { s.append(i18np("%1 inconsistency was detected and corrected\n", "%1 inconsistencies were detected and corrected\n", m_ccCount)); more = true; } if (m_orCount > 0) { s.append("\n\n"); s.append(i18np("%1 orphan account was created\n", "%1 orphan accounts were created\n", m_orCount)); more = true; } if (m_scCount > 0) { s.append("\n\n"); s.append(i18np("%1 possible schedule problem was noted\n", "%1 possible schedule problems were noted\n", m_scCount)); more = true; } QString unsupported(""); QString lineSep("\n - "); if (m_smallBusinessFound) unsupported.append(lineSep + i18n("Small Business Features (Customers, Invoices, etc.)")); if (m_budgetsFound) unsupported.append(lineSep + i18n("Budgets")); if (m_lotsFound) unsupported.append(lineSep + i18n("Lots")); if (!unsupported.isEmpty()) { unsupported.prepend(i18n("The following features found in your file are not currently supported:")); s.append(unsupported); } if (more) s.append(i18n("\n\nPress More for further information")); } else { s = m_messageList[source].join(QChar('\n')); } if (gncdebug) qDebug() << s; return (static_cast(s)); } PASS } //************************ writeReportToFile********************************* bool MyMoneyGncReader::writeReportToFile(const QList& sectionsToReport) { TRY { int i; QString fd = QFileDialog::getSaveFileName(0, QString(), QString(), i18n("Save report as")); if (fd.isEmpty()) return (false); QFile reportFile(fd); if (!reportFile.open(QIODevice::WriteOnly)) { return (false); } QTextStream stream(&reportFile); for (i = 0; i < sectionsToReport.count(); i++) stream << buildReportSection(sectionsToReport[i]) << endl; reportFile.close(); return (true); } PASS } /**************************************************************************** Utility routines *****************************************************************************/ //************************ createPayee *************************** QString MyMoneyGncReader::createPayee(const QString& gncDescription) { MyMoneyPayee payee; TRY { payee = m_storage->payeeByName(gncDescription); } CATCH { // payee not found, create one payee.setName(gncDescription); m_storage->addPayee(payee); } return (payee.id()); } //************************************** createOrphanAccount ******************************* QString MyMoneyGncReader::createOrphanAccount(const QString& gncName) { MyMoneyAccount acc; acc.setName("orphan_" + gncName); acc.setDescription(i18n("Orphan created from unknown GnuCash account")); QDate today = QDate::currentDate(); acc.setOpeningDate(today); acc.setLastModified(today); acc.setLastReconciliationDate(today); acc.setCurrencyId(m_txCommodity); acc.setAccountType(Account::Asset); acc.setParentAccountId(m_storage->asset().id()); m_storage->addAccount(acc); // assign the gnucash id as the key into the map to find our id m_mapIds[gncName.toUtf8()] = acc.id(); m_messageList["OR"].append( i18n("One or more transactions contain a reference to an otherwise unknown account\n" "An asset account with the name %1 has been created to hold the data", acc.name())); return (acc.id()); } //****************************** incrDate ********************************************* QDate MyMoneyGncReader::incrDate(QDate lastDate, unsigned char interval, unsigned int intervalCount) { TRY { switch (interval) { case 'd': return (lastDate.addDays(intervalCount)); case 'w': return (lastDate.addDays(intervalCount * 7)); case 'm': return (lastDate.addMonths(intervalCount)); case 'y': return (lastDate.addYears(intervalCount)); case 'o': // once-only return (lastDate); } throw MYMONEYEXCEPTION(i18n("Internal error - invalid interval char in incrDate")); QDate r = QDate(); return (r); // to keep compiler happy } PASS } //********************************* checkConsistency ********************************** MyMoneyAccount MyMoneyGncReader::checkConsistency(MyMoneyAccount& parent, MyMoneyAccount& child) { TRY { // gnucash is flexible/weird enough to allow various inconsistencies // these are a couple I found in my file, no doubt more will be discovered if ((child.accountType() == Account::Investment) && (parent.accountType() != Account::Asset)) { m_messageList["CC"].append( i18n("An Investment account must be a child of an Asset account\n" "Account %1 will be stored under the main Asset account", child.name())); return m_storage->asset(); } if ((child.accountType() == Account::Income) && (parent.accountType() != Account::Income)) { m_messageList["CC"].append( i18n("An Income account must be a child of an Income account\n" "Account %1 will be stored under the main Income account", child.name())); return m_storage->income(); } if ((child.accountType() == Account::Expense) && (parent.accountType() != Account::Expense)) { m_messageList["CC"].append( i18n("An Expense account must be a child of an Expense account\n" "Account %1 will be stored under the main Expense account", child.name())); return m_storage->expense(); } return (parent); } PASS } //*********************************** checkInvestmentOption ************************* void MyMoneyGncReader::checkInvestmentOption(QString stockId) { // implement the investment option for stock accounts // first check whether the parent account (gnucash id) is actually an // investment account. if it is, no further action is needed MyMoneyAccount stockAcc = m_storage->account(m_mapIds[stockId.toUtf8()]); MyMoneyAccount parent; QString parentKey = stockAcc.parentAccountId(); map_accountIds::const_iterator id = m_mapIds.constFind(parentKey); if (id != m_mapIds.constEnd()) { parent = m_storage->account(id.value()); if (parent.accountType() == Account::Investment) return ; } // so now, check the investment option requested by the user // option 0 creates a separate investment account for each stock account if (m_investmentOption == 0) { MyMoneyAccount invAcc(stockAcc); invAcc.setAccountType(Account::Investment); invAcc.setCurrencyId(QString("")); // we don't know what currency it is!! invAcc.setParentAccountId(parentKey); // intersperse it between old parent and child stock acct m_storage->addAccount(invAcc); m_mapIds [invAcc.id()] = invAcc.id(); // so stock account gets parented (again) to investment account later if (gncdebug) qDebug() << "Created investment account" << invAcc.name() << "as id" << invAcc.id() << "parent" << invAcc.parentAccountId(); if (gncdebug) qDebug() << "Setting stock" << stockAcc.name() << "id" << stockAcc.id() << "as child of" << invAcc.id(); stockAcc.setParentAccountId(invAcc.id()); m_storage->addAccount(invAcc, stockAcc); // investment option 1 creates a single investment account for all stocks } else if (m_investmentOption == 1) { static QString singleInvAccId = ""; MyMoneyAccount singleInvAcc; bool ok = false; if (singleInvAccId.isEmpty()) { // if the account has not yet been created QString invAccName; while (!ok) { invAccName = QInputDialog::getText(0, QStringLiteral(PACKAGE), i18n("Enter the investment account name "), QLineEdit::Normal, i18n("My Investments"), &ok); } singleInvAcc.setName(invAccName); singleInvAcc.setAccountType(Account::Investment); singleInvAcc.setCurrencyId(QString("")); singleInvAcc.setParentAccountId(m_storage->asset().id()); m_storage->addAccount(singleInvAcc); m_mapIds [singleInvAcc.id()] = singleInvAcc.id(); // so stock account gets parented (again) to investment account later if (gncdebug) qDebug() << "Created investment account" << singleInvAcc.name() << "as id" << singleInvAcc.id() << "parent" << singleInvAcc.parentAccountId() << "reparenting stock"; singleInvAccId = singleInvAcc.id(); } else { // the account has already been created singleInvAcc = m_storage->account(singleInvAccId); } m_storage->addAccount(singleInvAcc, stockAcc); // add stock as child // the original intention of option 2 was to allow any asset account to be converted to an investment (broker) account // however, since we have already stored the accounts as asset, we have no way at present of changing their type // the only alternative would be to hold all the gnucash data in memory, then implement this option, then convert all the data // that would mean a major overhaul of the code. Perhaps I'll think of another way... } else if (m_investmentOption == 2) { static int lastSelected = 0; MyMoneyAccount invAcc(stockAcc); QStringList accList; QList list; QList::iterator acc; m_storage->accountList(list); // build a list of candidates for the input box for (acc = list.begin(); acc != list.end(); ++acc) { // if (((*acc).accountGroup() == Account::Asset) && ((*acc).accountType() != Account::Stock)) accList.append ((*acc).name()); if ((*acc).accountType() == Account::Investment) accList.append((*acc).name()); } //if (accList.isEmpty()) qWarning ("No available accounts"); bool ok = false; while (!ok) { // keep going till we have a valid investment parent QString invAccName = QInputDialog::getItem(0, PACKAGE, i18n("Select parent investment account or enter new name. Stock %1", stockAcc.name()), accList, lastSelected, true, &ok); if (ok) { lastSelected = accList.indexOf(invAccName); // preserve selection for next time for (acc = list.begin(); acc != list.end(); ++acc) { if ((*acc).name() == invAccName) break; } if (acc != list.end()) { // an account was selected invAcc = *acc; } else { // a new account name was entered invAcc.setAccountType(Account::Investment); invAcc.setName(invAccName); invAcc.setCurrencyId(QString("")); invAcc.setParentAccountId(m_storage->asset().id()); m_storage->addAccount(invAcc); ok = true; } if (invAcc.accountType() == Account::Investment) { ok = true; } else { // this code is probably not going to be implemented coz we can't change account types (??) #if 0 QMessageBox mb(PACKAGE, i18n("%1 is not an Investment Account. Do you wish to make it one?", invAcc.name()), QMessageBox::Question, QMessageBox::Yes | QMessageBox::Default, QMessageBox::No | QMessageBox::Escape, Qt::NoButton); switch (mb.exec()) { case QMessageBox::No : ok = false; break; default: // convert it - but what if it has splits??? qWarning("Not yet implemented"); ok = true; break; } #endif switch (KMessageBox::questionYesNo(0, i18n("%1 is not an Investment Account. Do you wish to make it one?", invAcc.name()), PACKAGE)) { case KMessageBox::Yes: // convert it - but what if it has splits??? qWarning("Not yet implemented"); ok = true; break; default: ok = false; break; } } } // end if ok - user pressed Cancel } // end while !ok m_mapIds [invAcc.id()] = invAcc.id(); // so stock account gets parented (again) to investment account later m_storage->addAccount(invAcc, stockAcc); } else { // investment option != 0, 1, 2 qWarning("Invalid investment option %d", m_investmentOption); } } // get the price source for a stock (gnc account) where online quotes are requested void MyMoneyGncReader::getPriceSource(MyMoneySecurity stock, QString gncSource) { // if he wants to use Finance::Quote, no conversion of source name is needed if (m_useFinanceQuote) { stock.setValue("kmm-online-quote-system", "Finance::Quote"); stock.setValue("kmm-online-source", gncSource.toLower()); m_storage->modifySecurity(stock); return; } // first check if we have already asked about this source // (mapSources is initialy empty. We may be able to pre-fill it with some equivalent // sources, if such things do exist. User feedback may help here.) QMap::const_iterator it; for (it = m_mapSources.constBegin(); it != m_mapSources.constEnd(); ++it) { if (it.key() == gncSource) { stock.setValue("kmm-online-source", it.value()); m_storage->modifySecurity(stock); return; } } // not found in map, so ask the user QPointer dlg = new KGncPriceSourceDlg(stock.name(), gncSource); dlg->exec(); QString s = dlg->selectedSource(); if (!s.isEmpty()) { stock.setValue("kmm-online-source", s); m_storage->modifySecurity(stock); } if (dlg->alwaysUse()) m_mapSources[gncSource] = s; delete dlg; return; } // functions to control the progress bar //*********************** setProgressCallback ***************************** void MyMoneyGncReader::setProgressCallback(void(*callback)(int, int, const QString&)) { m_progressCallback = callback; return ; } //************************** signalProgress ******************************* void MyMoneyGncReader::signalProgress(int current, int total, const QString& msg) { if (m_progressCallback != 0) (*m_progressCallback)(current, total, msg); return ; } #endif // _GNCFILEANON diff --git a/kmymoney/dialogs/CMakeLists.txt b/kmymoney/dialogs/CMakeLists.txt index 19fc361d8..49470ca3a 100644 --- a/kmymoney/dialogs/CMakeLists.txt +++ b/kmymoney/dialogs/CMakeLists.txt @@ -1,112 +1,112 @@ add_subdirectory( settings ) ########### next target ############### set(libdialogs_a_SOURCES splitadjustdialog.cpp investactivities.cpp investtransactioneditor.cpp kaccountselectdlg.cpp kbackupdlg.cpp kbalancechartdlg.cpp kbalancewarning.cpp kcategoryreassigndlg.cpp kchooseimportexportdlg.cpp kconfirmmanualenterdlg.cpp kcurrencycalculator.cpp kcurrencyeditdlg.cpp kavailablecurrencydlg.cpp kcurrencyeditordlg.cpp keditscheduledlg.cpp kenterscheduledlg.cpp kequitypriceupdatedlg.cpp kequitypriceupdateconfdlg.cpp kfindtransactiondlg.cpp kgeneratesqldlg.cpp kgncimportoptionsdlg.cpp kgncpricesourcedlg.cpp kgpgkeyselectiondlg.cpp kloadtemplatedlg.cpp kmergetransactionsdlg.cpp kmymoneyfileinfodlg.cpp kmymoneypricedlg.cpp kmymoneysplittable.cpp knewaccountdlg.cpp hierarchyfilterproxymodel.cpp knewbankdlg.cpp knewbudgetdlg.cpp knewequityentrydlg.cpp editpersonaldatadlg.cpp kpayeereassigndlg.cpp ktagreassigndlg.cpp kreportconfigurationfilterdlg.cpp kselectdatabasedlg.cpp kselecttransactionsdlg.cpp ksplittransactiondlg.cpp ktemplateexportdlg.cpp kupdatestockpricedlg.cpp transactioneditor.cpp stdtransactioneditor.cpp transactionmatcher.cpp konlinetransferform.cpp ) set(dialogs_HEADERS splitadjustdialog.h investtransactioneditor.h kcurrencycalculator.h transactioneditor.h stdtransactioneditor.h ) set(dialogs_UI splitadjustdialog.ui kaccountselectdlg.ui kbackupdlg.ui kcategoryreassigndlg.ui kchooseimportexportdlg.ui kconfirmmanualenterdlg.ui kcurrencycalculator.ui kcurrencyeditdlg.ui kavailablecurrencydlg.ui kcurrencyeditordlg.ui keditscheduledlg.ui kenterscheduledlg.ui kequitypriceupdatedlg.ui kequitypriceupdateconfdlg.ui kfindtransactiondlg.ui kgeneratesqldlg.ui kgncimportoptionsdlg.ui kgncpricesourcedlg.ui kloadtemplatedlg.ui kmymoneyfileinfodlg.ui kmymoneypricedlg.ui knewaccountdlg.ui knewbankdlg.ui knewbudgetdlg.ui knewequityentrydlg.ui editpersonaldatadlg.ui kpayeereassigndlg.ui ktagreassigndlg.ui kselectdatabasedlg.ui kselecttransactionsdlg.ui ksortoptiondlg.ui ksplitcorrectiondlg.ui ksplittransactiondlg.ui ktemplateexportdlg.ui kupdatestockpricedlg.ui - ../widgets/kaccounttemplateselector.ui ../widgets/transactionsortoption.ui + ../widgets/kaccounttemplateselectordecl.ui ../widgets/transactionsortoptiondecl.ui konlinetransferform.ui ) ki18n_wrap_ui(libdialogs_a_SOURCES ${dialogs_UI} ) add_library(dialogs STATIC ${libdialogs_a_SOURCES}) target_link_libraries(dialogs PUBLIC KChart KF5::ItemViews KF5::I18n KF5::TextWidgets KF5::Completion Qt5::Widgets Qt5::Sql Alkimia::alkimia kmm_mymoney onlinetask_interfaces kmm_widgets kmm_utils_platformtools ) target_link_libraries(dialogs LINK_PUBLIC kmm_widgets kmm_mymoney onlinetask_interfaces ) ########### install files ############### install(FILES ${dialogs_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/kmymoney COMPONENT Devel) diff --git a/kmymoney/dialogs/investtransactioneditor.cpp b/kmymoney/dialogs/investtransactioneditor.cpp index 9ea8a02f4..0087e336d 100644 --- a/kmymoney/dialogs/investtransactioneditor.cpp +++ b/kmymoney/dialogs/investtransactioneditor.cpp @@ -1,1226 +1,1219 @@ /*************************************************************************** investtransactioneditor.cpp ---------- begin : Fri Dec 15 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "investtransactioneditor.h" #include "transactioneditor_p.h" #include // ---------------------------------------------------------------------------- // QT Includes #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes -#include "kmymoneyreconcilecombo.h" -#include "kmymoneyactivitycombo.h" -#include "kmymoneytagcombo.h" -#include "ktagcontainer.h" -#include "investtransaction.h" -#include "transactioneditorcontainer.h" #include "kmymoneycategory.h" #include "kmymoneydateinput.h" #include "kmymoneyedit.h" #include "kmymoneyaccountselector.h" #include "kmymoneymvccombo.h" #include "mymoneyfile.h" -#include "mymoneysecurity.h" #include "mymoneyprice.h" #include "ksplittransactiondlg.h" #include "kcurrencycalculator.h" #include "kmymoneyglobalsettings.h" #include "investactivities.h" #include "kmymoneycompletion.h" #include "dialogenums.h" using namespace eMyMoney; using namespace KMyMoneyRegister; using namespace KMyMoneyTransactionForm; using namespace Invest; class InvestTransactionEditorPrivate : public TransactionEditorPrivate { Q_DISABLE_COPY(InvestTransactionEditorPrivate) Q_DECLARE_PUBLIC(InvestTransactionEditor) friend class Invest::Activity; public: InvestTransactionEditorPrivate(InvestTransactionEditor* qq) : TransactionEditorPrivate(qq), m_activity(0) { m_phonyAccount = MyMoneyAccount("Phony-ID", MyMoneyAccount()); } ~InvestTransactionEditorPrivate() { delete m_activity; } QWidget* haveWidget(const QString& name) { Q_Q(InvestTransactionEditor); return q->haveWidget(name); } void hideCategory(const QString& name) { if (KMyMoneyCategory* cat = dynamic_cast(haveWidget(name))) { cat->hide(); cat->splitButton()->hide(); } } void activityFactory(eMyMoney::Split::InvestmentTransactionType type) { Q_Q(InvestTransactionEditor); if (!m_activity || type != m_activity->type()) { delete m_activity; switch (type) { default: case eMyMoney::Split::InvestmentTransactionType::BuyShares: m_activity = new Buy(q); break; case eMyMoney::Split::InvestmentTransactionType::SellShares: m_activity = new Sell(q); break; case eMyMoney::Split::InvestmentTransactionType::Dividend: case eMyMoney::Split::InvestmentTransactionType::Yield: m_activity = new Div(q); break; case eMyMoney::Split::InvestmentTransactionType::ReinvestDividend: m_activity = new Reinvest(q); break; case eMyMoney::Split::InvestmentTransactionType::AddShares: m_activity = new Add(q); break; case eMyMoney::Split::InvestmentTransactionType::RemoveShares: m_activity = new Remove(q); break; case eMyMoney::Split::InvestmentTransactionType::SplitShares: m_activity = new Invest::Split(q); break; case eMyMoney::Split::InvestmentTransactionType::InterestIncome: m_activity = new IntInc(q); break; } } } MyMoneyMoney subtotal(const QList& splits) const { QList::const_iterator it_s; MyMoneyMoney sum; for (it_s = splits.begin(); it_s != splits.end(); ++it_s) { sum += (*it_s).value(); } return sum; } /** * This method creates a transaction to be used for the split fee/interest editor. * It has a reference to a phony account and the splits contained in @a splits . */ bool createPseudoTransaction(MyMoneyTransaction& t, const QList& splits) { t.removeSplits(); MyMoneySplit split; split.setAccountId(m_phonyAccount.id()); split.setValue(-subtotal(splits)); split.setShares(split.value()); t.addSplit(split); m_phonySplit = split; QList::const_iterator it_s; for (it_s = splits.begin(); it_s != splits.end(); ++it_s) { split = *it_s; split.clearId(); t.addSplit(split); } return true; } /** * Convenience method used by slotEditInterestSplits() and slotEditFeeSplits(). * * @param categoryWidgetName name of the category widget * @param amountWidgetName name of the amount widget * @param splits the splits that make up the transaction to be edited * @param isIncome @c false for fees, @c true for interest * @param slotEditSplits name of the slot to be connected to the focusIn signal of the * category widget named @p categoryWidgetName in case of multiple splits * in @p splits . */ int editSplits(const QString& categoryWidgetName, const QString& amountWidgetName, QList& splits, bool isIncome, const char* slotEditSplits) { Q_Q(InvestTransactionEditor); int rc = QDialog::Rejected; if (!m_openEditSplits) { // only get in here in a single instance m_openEditSplits = true; // force focus change to update all data KMyMoneyCategory* category = dynamic_cast(m_editWidgets[categoryWidgetName]); QWidget* w = category->splitButton(); if (w) w->setFocus(); kMyMoneyEdit* amount = dynamic_cast(haveWidget(amountWidgetName)); MyMoneyTransaction transaction; transaction.setCommodity(m_currency.id()); if (splits.count() == 0 && !category->selectedItem().isEmpty()) { MyMoneySplit s; s.setAccountId(category->selectedItem()); s.setShares(amount->value()); s.setValue(s.shares()); splits << s; } // use the transactions commodity as the currency indicator for the splits // this is used to allow some useful setting for the fractions in the amount fields try { m_phonyAccount.setCurrencyId(m_transaction.commodity()); m_phonyAccount.fraction(MyMoneyFile::instance()->security(m_transaction.commodity())); } catch (const MyMoneyException &) { qDebug("Unable to setup precision"); } if (createPseudoTransaction(transaction, splits)) { MyMoneyMoney value; QPointer dlg = new KSplitTransactionDlg(transaction, m_phonySplit, m_phonyAccount, false, isIncome, MyMoneyMoney(), m_priceInfo, m_regForm); // q->connect(dlg, SIGNAL(newCategory(MyMoneyAccount&)), q, SIGNAL(newCategory(MyMoneyAccount&))); if ((rc = dlg->exec()) == QDialog::Accepted) { transaction = dlg->transaction(); // collect splits out of the transaction splits.clear(); QList::const_iterator it_s; MyMoneyMoney fees; for (it_s = transaction.splits().constBegin(); it_s != transaction.splits().constEnd(); ++it_s) { if ((*it_s).accountId() == m_phonyAccount.id()) continue; splits << *it_s; fees += (*it_s).shares(); } if (isIncome) fees = -fees; QString categoryId; q->setupCategoryWidget(category, splits, categoryId, slotEditSplits); amount->setValue(fees); q->slotUpdateTotalAmount(); } delete dlg; } // focus jumps into the memo field if ((w = haveWidget("memo")) != 0) { w->setFocus(); } m_openEditSplits = false; } return rc; } void updatePriceMode(const MyMoneySplit& split = MyMoneySplit()) { Q_Q(InvestTransactionEditor); auto label = dynamic_cast(haveWidget("price-label")); if (label) { auto sharesEdit = dynamic_cast(haveWidget("shares")); auto priceEdit = dynamic_cast(haveWidget("price")); MyMoneyMoney price; if (!split.id().isEmpty()) price = split.price().reduce(); else price = priceEdit->value().abs(); if (q->priceMode() == eDialogs::PriceMode::PricePerTransaction) { priceEdit->setPrecision(m_currency.pricePrecision()); label->setText(i18n("Transaction amount")); if (!sharesEdit->value().isZero()) priceEdit->setValue(sharesEdit->value().abs() * price); } else if (q->priceMode() == eDialogs::PriceMode::PricePerShare) { priceEdit->setPrecision(m_security.pricePrecision()); label->setText(i18n("Price/Share")); priceEdit->setValue(price); } else priceEdit->setValue(price); } } // InvestTransactionEditor* q_ptr; Activity* m_activity; MyMoneyAccount m_phonyAccount; MyMoneySplit m_phonySplit; MyMoneySplit m_assetAccountSplit; QList m_interestSplits; QList m_feeSplits; MyMoneySecurity m_security; MyMoneySecurity m_currency; eMyMoney::Split::InvestmentTransactionType m_transactionType; }; InvestTransactionEditor::InvestTransactionEditor() : TransactionEditor(*new InvestTransactionEditorPrivate(this)) { Q_D(InvestTransactionEditor); d->m_transactionType = eMyMoney::Split::InvestmentTransactionType::UnknownTransactionType; } InvestTransactionEditor::~InvestTransactionEditor() { } InvestTransactionEditor::InvestTransactionEditor(TransactionEditorContainer* regForm, KMyMoneyRegister::InvestTransaction* item, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) : TransactionEditor(*new InvestTransactionEditorPrivate(this), regForm, item, list, lastPostDate) { Q_D(InvestTransactionEditor); // after the gometries of the container are updated hide the widgets which are not needed by the current activity connect(d->m_regForm, &TransactionEditorContainer::geometriesUpdated, this, &InvestTransactionEditor::slotTransactionContainerGeometriesUpdated); // dissect the transaction into its type, splits, currency, security etc. KMyMoneyUtils::dissectTransaction(d->m_transaction, d->m_split, d->m_assetAccountSplit, d->m_feeSplits, d->m_interestSplits, d->m_security, d->m_currency, d->m_transactionType); // determine initial activity object d->activityFactory(d->m_transactionType); } void InvestTransactionEditor::createEditWidgets() { Q_D(InvestTransactionEditor); auto activity = new KMyMoneyActivityCombo(); d->m_editWidgets["activity"] = activity; connect(activity, &KMyMoneyActivityCombo::activitySelected, this, &InvestTransactionEditor::slotUpdateActivity); connect(activity, &KMyMoneyActivityCombo::activitySelected, this, &InvestTransactionEditor::slotUpdateButtonState); d->m_editWidgets["postdate"] = new kMyMoneyDateInput; auto security = new KMyMoneySecurity; security->setPlaceholderText(i18n("Security")); d->m_editWidgets["security"] = security; connect(security, &KMyMoneyCombo::itemSelected, this, &InvestTransactionEditor::slotUpdateSecurity); connect(security, &QComboBox::editTextChanged, this, &InvestTransactionEditor::slotUpdateButtonState); connect(security, &KMyMoneyCombo::createItem, this, &InvestTransactionEditor::slotCreateSecurity); connect(security, &KMyMoneyCombo::objectCreation, this, &TransactionEditor::objectCreation); - auto asset = new KMyMoneyCategory(false, nullptr); + auto asset = new KMyMoneyCategory(0, false); asset->setPlaceholderText(i18n("Asset account")); d->m_editWidgets["asset-account"] = asset; connect(asset, &QComboBox::editTextChanged, this, &InvestTransactionEditor::slotUpdateButtonState); connect(asset, &KMyMoneyCombo::objectCreation, this, &TransactionEditor::objectCreation); - auto fees = new KMyMoneyCategory(true, nullptr); + auto fees = new KMyMoneyCategory(0, true); fees->setPlaceholderText(i18n("Fees")); d->m_editWidgets["fee-account"] = fees; connect(fees, &KMyMoneyCombo::itemSelected, this, &InvestTransactionEditor::slotUpdateFeeCategory); connect(fees, &QComboBox::editTextChanged, this, &InvestTransactionEditor::slotUpdateButtonState); connect(fees, &QComboBox::editTextChanged, this, &InvestTransactionEditor::slotUpdateFeeVisibility); connect(fees, &KMyMoneyCombo::createItem, this, &InvestTransactionEditor::slotCreateFeeCategory); connect(fees, &KMyMoneyCombo::objectCreation, this, &TransactionEditor::objectCreation); connect(fees->splitButton(), &QAbstractButton::clicked, this, &InvestTransactionEditor::slotEditFeeSplits); - auto interest = new KMyMoneyCategory(true, nullptr); + auto interest = new KMyMoneyCategory(0, true); interest->setPlaceholderText(i18n("Interest")); d->m_editWidgets["interest-account"] = interest; connect(interest, &KMyMoneyCombo::itemSelected, this, &InvestTransactionEditor::slotUpdateInterestCategory); connect(interest, &QComboBox::editTextChanged, this, &InvestTransactionEditor::slotUpdateButtonState); connect(interest, &QComboBox::editTextChanged, this, &InvestTransactionEditor::slotUpdateInterestVisibility); connect(interest, &KMyMoneyCombo::createItem, this, &InvestTransactionEditor::slotCreateInterestCategory); connect(interest, &KMyMoneyCombo::objectCreation, this, &TransactionEditor::objectCreation); connect(interest->splitButton(), &QAbstractButton::clicked, this, &InvestTransactionEditor::slotEditInterestSplits); auto tag = new KTagContainer; tag->tagCombo()->setPlaceholderText(i18n("Tag")); tag->tagCombo()->setObjectName(QLatin1String("Tag")); d->m_editWidgets["tag"] = tag; connect(tag->tagCombo(), &QComboBox::editTextChanged, this, &InvestTransactionEditor::slotUpdateButtonState); connect(tag->tagCombo(), &KMyMoneyMVCCombo::createItem, this, &TransactionEditor::createTag); connect(tag->tagCombo(), &KMyMoneyMVCCombo::objectCreation, this, &TransactionEditor::objectCreation); auto memo = new KTextEdit; memo->setTabChangesFocus(true); d->m_editWidgets["memo"] = memo; connect(memo, &QTextEdit::textChanged, this, &InvestTransactionEditor::slotUpdateInvestMemoState); connect(memo, &QTextEdit::textChanged, this, &InvestTransactionEditor::slotUpdateButtonState); d->m_activity->memoText().clear(); d->m_activity->memoChanged() = false; kMyMoneyEdit* value = new kMyMoneyEdit; value->setPlaceholderText(i18n("Shares")); value->setResetButtonVisible(false); d->m_editWidgets["shares"] = value; connect(value, &kMyMoneyEdit::textChanged, this, &InvestTransactionEditor::slotUpdateButtonState); connect(value, &kMyMoneyEdit::valueChanged, this, &InvestTransactionEditor::slotUpdateTotalAmount); value = new kMyMoneyEdit; value->setPlaceholderText(i18n("Price")); value->setResetButtonVisible(false); d->m_editWidgets["price"] = value; connect(value, &kMyMoneyEdit::textChanged, this, &InvestTransactionEditor::slotUpdateButtonState); connect(value, &kMyMoneyEdit::valueChanged, this, &InvestTransactionEditor::slotUpdateTotalAmount); value = new kMyMoneyEdit; // TODO once we have the selected transactions as array of Transaction // we can allow multiple splits for fee and interest value->setResetButtonVisible(false); d->m_editWidgets["fee-amount"] = value; connect(value, &kMyMoneyEdit::textChanged, this, &InvestTransactionEditor::slotUpdateButtonState); connect(value, &kMyMoneyEdit::valueChanged, this, &InvestTransactionEditor::slotUpdateTotalAmount); value = new kMyMoneyEdit; // TODO once we have the selected transactions as array of Transaction // we can allow multiple splits for fee and interest value->setResetButtonVisible(false); d->m_editWidgets["interest-amount"] = value; connect(value, &kMyMoneyEdit::textChanged, this, &InvestTransactionEditor::slotUpdateButtonState); connect(value, &kMyMoneyEdit::valueChanged, this, &InvestTransactionEditor::slotUpdateTotalAmount); auto reconcile = new KMyMoneyReconcileCombo; d->m_editWidgets["status"] = reconcile; connect(reconcile, &KMyMoneyMVCCombo::itemSelected, this, &InvestTransactionEditor::slotUpdateButtonState); KMyMoneyRegister::QWidgetContainer::iterator it_w; for (it_w = d->m_editWidgets.begin(); it_w != d->m_editWidgets.end(); ++it_w) { (*it_w)->installEventFilter(this); } QLabel* label; d->m_editWidgets["activity-label"] = label = new QLabel(i18n("Activity")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["postdate-label"] = label = new QLabel(i18n("Date")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["security-label"] = label = new QLabel(i18n("Security")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["shares-label"] = label = new QLabel(i18n("Shares")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["asset-label"] = label = new QLabel(i18n("Account")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["price-label"] = label = new QLabel(i18n("Price/share")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["fee-label"] = label = new QLabel(i18n("Fees")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["fee-amount-label"] = label = new QLabel(""); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["interest-label"] = label = new QLabel(i18n("Interest")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["interest-amount-label"] = label = new QLabel(i18n("Interest")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["memo-label"] = label = new QLabel(i18n("Memo")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["total"] = label = new QLabel(""); label->setAlignment(Qt::AlignVCenter | Qt::AlignRight); d->m_editWidgets["total-label"] = label = new QLabel(i18nc("Total value", "Total")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["status-label"] = label = new QLabel(i18n("Status")); label->setAlignment(Qt::AlignVCenter); // if we don't have more than 1 selected transaction, we don't need // the "don't change" item in some of the combo widgets if (d->m_transactions.count() < 2) { reconcile->removeDontCare(); } } int InvestTransactionEditor::slotEditFeeSplits() { Q_D(InvestTransactionEditor); return d->editSplits("fee-account", "fee-amount", d->m_feeSplits, false, SLOT(slotEditFeeSplits())); } int InvestTransactionEditor::slotEditInterestSplits() { Q_D(InvestTransactionEditor); return d->editSplits("interest-account", "interest-amount", d->m_interestSplits, true, SLOT(slotEditInterestSplits())); } void InvestTransactionEditor::slotCreateSecurity(const QString& name, QString& id) { Q_D(InvestTransactionEditor); MyMoneyAccount acc; QRegExp exp("([^:]+)"); if (exp.indexIn(name) != -1) { acc.setName(exp.cap(1)); emit createSecurity(acc, d->m_account); // return id id = acc.id(); if (!id.isEmpty()) { slotUpdateSecurity(id); } } } void InvestTransactionEditor::slotCreateFeeCategory(const QString& name, QString& id) { MyMoneyAccount acc; acc.setName(name); emit createCategory(acc, MyMoneyFile::instance()->expense()); // return id id = acc.id(); } void InvestTransactionEditor::slotUpdateFeeCategory(const QString& id) { haveWidget("fee-amount")->setDisabled(id.isEmpty()); } void InvestTransactionEditor::slotUpdateFeeVisibility(const QString& txt) { Q_D(InvestTransactionEditor); static const QSet transactionTypesWithoutFee = QSet() << eMyMoney::Split::InvestmentTransactionType::AddShares << eMyMoney::Split::InvestmentTransactionType::RemoveShares << eMyMoney::Split::InvestmentTransactionType::SplitShares; kMyMoneyEdit* feeAmount = dynamic_cast(haveWidget("fee-amount")); feeAmount->setHidden(txt.isEmpty()); QLabel* l = dynamic_cast(haveWidget("fee-amount-label")); KMyMoneyCategory* fee = dynamic_cast(haveWidget("fee-account")); const bool hideFee = txt.isEmpty() || transactionTypesWithoutFee.contains(d->m_activity->type()); // no fee expected so hide if (hideFee) { if (l) { l->setText(""); } feeAmount->hide(); fee->splitButton()->hide(); } else { if (l) { l->setText(i18n("Fee Amount")); } feeAmount->show(); fee->splitButton()->show(); } } void InvestTransactionEditor::slotUpdateInterestCategory(const QString& id) { haveWidget("interest-amount")->setDisabled(id.isEmpty()); } void InvestTransactionEditor::slotUpdateInterestVisibility(const QString& txt) { Q_D(InvestTransactionEditor); static const QSet transactionTypesWithInterest = QSet() << eMyMoney::Split::InvestmentTransactionType::BuyShares << eMyMoney::Split::InvestmentTransactionType::SellShares << eMyMoney::Split::InvestmentTransactionType::Dividend << eMyMoney::Split::InvestmentTransactionType::InterestIncome << eMyMoney::Split::InvestmentTransactionType::Yield; QWidget* w = haveWidget("interest-amount"); w->setHidden(txt.isEmpty()); QLabel* l = dynamic_cast(haveWidget("interest-amount-label")); KMyMoneyCategory* interest = dynamic_cast(haveWidget("interest-account")); const bool showInterest = !txt.isEmpty() && transactionTypesWithInterest.contains(d->m_activity->type()); if (interest && showInterest) { interest->splitButton()->show(); w->show(); if (l) l->setText(i18n("Interest")); } else { if (interest) { interest->splitButton()->hide(); w->hide(); if (l) l->setText(QString()); } } } void InvestTransactionEditor::slotCreateInterestCategory(const QString& name, QString& id) { MyMoneyAccount acc; acc.setName(name); emit createCategory(acc, MyMoneyFile::instance()->income()); id = acc.id(); } void InvestTransactionEditor::slotReloadEditWidgets() { Q_D(InvestTransactionEditor); auto interest = dynamic_cast(haveWidget("interest-account")); auto fees = dynamic_cast(haveWidget("fee-account")); auto security = dynamic_cast(haveWidget("security")); AccountSet aSet; QString id; // interest-account aSet.clear(); aSet.addAccountGroup(Account::Income); aSet.load(interest->selector()); setupCategoryWidget(interest, d->m_interestSplits, id, SLOT(slotEditInterestSplits())); // fee-account aSet.clear(); aSet.addAccountGroup(Account::Expense); aSet.load(fees->selector()); setupCategoryWidget(fees, d->m_feeSplits, id, SLOT(slotEditFeeSplits())); // security aSet.clear(); aSet.load(security->selector(), i18n("Security"), d->m_account.accountList(), true); } void InvestTransactionEditor::loadEditWidgets(KMyMoneyRegister::Action) { loadEditWidgets(); } void InvestTransactionEditor::loadEditWidgets() { Q_D(InvestTransactionEditor); QString id; auto postDate = dynamic_cast(haveWidget("postdate")); auto reconcile = dynamic_cast(haveWidget("status")); auto security = dynamic_cast(haveWidget("security")); auto activity = dynamic_cast(haveWidget("activity")); auto asset = dynamic_cast(haveWidget("asset-account")); auto memo = dynamic_cast(d->m_editWidgets["memo"]); kMyMoneyEdit* value; auto interest = dynamic_cast(haveWidget("interest-account")); auto fees = dynamic_cast(haveWidget("fee-account")); // check if the current transaction has a reference to an equity account bool haveEquityAccount = false; QList::const_iterator it_s; for (it_s = d->m_transaction.splits().constBegin(); !haveEquityAccount && it_s != d->m_transaction.splits().constEnd(); ++it_s) { MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId()); if (acc.accountType() == Account::Equity) haveEquityAccount = true; } // asset-account AccountSet aSet; aSet.clear(); aSet.addAccountType(Account::Checkings); aSet.addAccountType(Account::Savings); aSet.addAccountType(Account::Cash); aSet.addAccountType(Account::Asset); aSet.addAccountType(Account::Currency); aSet.addAccountType(Account::CreditCard); if (KMyMoneyGlobalSettings::expertMode() || haveEquityAccount) aSet.addAccountGroup(Account::Equity); aSet.load(asset->selector()); // security security->setSuppressObjectCreation(false); // allow object creation on the fly aSet.clear(); aSet.load(security->selector(), i18n("Security"), d->m_account.accountList(), true); // memo memo->setText(d->m_split.memo()); d->m_activity->memoText() = d->m_split.memo(); d->m_activity->memoChanged() = false; if (!isMultiSelection()) { // date if (d->m_transaction.postDate().isValid()) postDate->setDate(d->m_transaction.postDate()); else if (d->m_lastPostDate.isValid()) postDate->setDate(d->m_lastPostDate); else postDate->setDate(QDate::currentDate()); // security (but only if it's not the investment account) if (d->m_split.accountId() != d->m_account.id()) { security->completion()->setSelected(d->m_split.accountId()); security->slotItemSelected(d->m_split.accountId()); } // activity activity->setActivity(d->m_activity->type()); slotUpdateActivity(activity->activity()); asset->completion()->setSelected(d->m_assetAccountSplit.accountId()); asset->slotItemSelected(d->m_assetAccountSplit.accountId()); // interest-account aSet.clear(); aSet.addAccountGroup(Account::Income); aSet.load(interest->selector()); setupCategoryWidget(interest, d->m_interestSplits, id, SLOT(slotEditInterestSplits())); slotUpdateInterestVisibility(interest->currentText()); // fee-account aSet.clear(); aSet.addAccountGroup(Account::Expense); aSet.load(fees->selector()); setupCategoryWidget(fees, d->m_feeSplits, id, SLOT(slotEditFeeSplits())); slotUpdateFeeVisibility(fees->currentText()); // shares // don't set the value if the number of shares is zero so that // we can see the hint value = dynamic_cast(haveWidget("shares")); if (typeid(*(d->m_activity)) != typeid(Invest::Split(this))) value->setPrecision(MyMoneyMoney::denomToPrec(d->m_security.smallestAccountFraction())); else value->setPrecision(-1); if (!d->m_split.shares().isZero()) value->setValue(d->m_split.shares().abs()); // price d->updatePriceMode(d->m_split); // fee amount value = dynamic_cast(haveWidget("fee-amount")); value->setValue(d->subtotal(d->m_feeSplits)); // interest amount value = dynamic_cast(haveWidget("interest-amount")); value->setValue(-d->subtotal(d->m_interestSplits)); // total slotUpdateTotalAmount(); // status if (d->m_split.reconcileFlag() == eMyMoney::Split::State::Unknown) d->m_split.setReconcileFlag(eMyMoney::Split::State::NotReconciled); reconcile->setState(d->m_split.reconcileFlag()); } else { postDate->loadDate(QDate()); reconcile->setState(eMyMoney::Split::State::Unknown); // We don't allow to change the activity activity->setActivity(d->m_activity->type()); slotUpdateActivity(activity->activity()); activity->setDisabled(true); // scan the list of selected transactions and check that they have // the same activity. KMyMoneyRegister::SelectedTransactions::iterator it_t = d->m_transactions.begin(); const QString& action = d->m_item->split().action(); bool isNegative = d->m_item->split().shares().isNegative(); bool allSameActivity = true; for (it_t = d->m_transactions.begin(); allSameActivity && (it_t != d->m_transactions.end()); ++it_t) { allSameActivity = (action == (*it_t).split().action() && (*it_t).split().shares().isNegative() == isNegative); } QStringList fields; fields << "shares" << "price" << "fee-amount" << "interest-amount"; QStringList::const_iterator it_f; for (it_f = fields.constBegin(); it_f != fields.constEnd(); ++it_f) { value = dynamic_cast(haveWidget((*it_f))); value->setText(""); value->setAllowEmpty(); } // if we have transactions with different activities, disable some more widgets if (!allSameActivity) { fields << "asset-account" << "fee-account" << "interest-account"; QStringList::const_iterator it_f; for (it_f = fields.constBegin(); it_f != fields.constEnd(); ++it_f) { haveWidget(*it_f)->setDisabled(true); } } } } QWidget* InvestTransactionEditor::firstWidget() const { return nullptr; // let the creator use the first widget in the tab order } bool InvestTransactionEditor::isComplete(QString& reason) const { Q_D(const InvestTransactionEditor); reason.clear(); return d->m_activity->isComplete(reason); } void InvestTransactionEditor::slotUpdateSecurity(const QString& stockId) { Q_D(InvestTransactionEditor); auto file = MyMoneyFile::instance(); MyMoneyAccount stock = file->account(stockId); d->m_security = file->security(stock.currencyId()); d->m_currency = file->security(d->m_security.tradingCurrency()); bool currencyKnown = !d->m_currency.id().isEmpty(); if (!currencyKnown) { d->m_currency.setTradingSymbol("???"); } else { if (typeid(*(d->m_activity)) != typeid(Invest::Split(this))) { dynamic_cast(haveWidget("shares"))->setPrecision(MyMoneyMoney::denomToPrec(d->m_security.smallestAccountFraction())); } else { dynamic_cast(haveWidget("shares"))->setPrecision(-1); } } d->updatePriceMode(); d->m_activity->preloadAssetAccount(); haveWidget("shares")->setEnabled(currencyKnown); haveWidget("price")->setEnabled(currencyKnown); haveWidget("fee-amount")->setEnabled(currencyKnown); haveWidget("interest-amount")->setEnabled(currencyKnown); slotUpdateTotalAmount(); slotUpdateButtonState(); resizeForm(); } bool InvestTransactionEditor::fixTransactionCommodity(const MyMoneyAccount& /* account */) { return true; } void InvestTransactionEditor::totalAmount(MyMoneyMoney& amount) const { auto activityCombo = dynamic_cast(haveWidget("activity")); auto sharesEdit = dynamic_cast(haveWidget("shares")); auto priceEdit = dynamic_cast(haveWidget("price")); auto feesEdit = dynamic_cast(haveWidget("fee-amount")); auto interestEdit = dynamic_cast(haveWidget("interest-amount")); if (priceMode() == eDialogs::PriceMode::PricePerTransaction) amount = priceEdit->value().abs(); else amount = sharesEdit->value().abs() * priceEdit->value().abs(); if (feesEdit->isVisible()) { MyMoneyMoney fee = feesEdit->value(); MyMoneyMoney factor(-1, 1); switch (activityCombo->activity()) { case eMyMoney::Split::InvestmentTransactionType::BuyShares: case eMyMoney::Split::InvestmentTransactionType::ReinvestDividend: factor = MyMoneyMoney::ONE; break; default: break; } amount += (fee * factor); } if (interestEdit->isVisible()) { MyMoneyMoney interest = interestEdit->value(); MyMoneyMoney factor(1, 1); switch (activityCombo->activity()) { case eMyMoney::Split::InvestmentTransactionType::BuyShares: factor = MyMoneyMoney::MINUS_ONE; break; default: break; } amount += (interest * factor); } } void InvestTransactionEditor::slotUpdateTotalAmount() { Q_D(InvestTransactionEditor); QLabel* total = dynamic_cast(haveWidget("total")); if (total && total->isVisible()) { MyMoneyMoney amount; totalAmount(amount); total->setText(amount.convert(d->m_currency.smallestAccountFraction(), static_cast(d->m_security.roundingMethod())) .formatMoney(d->m_currency.tradingSymbol(), MyMoneyMoney::denomToPrec(d->m_currency.smallestAccountFraction()))); } } void InvestTransactionEditor::slotTransactionContainerGeometriesUpdated() { Q_D(InvestTransactionEditor); // when the geometries of the transaction container are updated some edit widgets that were // previously hidden are being shown (see QAbstractItemView::updateEditorGeometries) so we // need to update the activity with the current activity in order to show only the widgets // which are needed by the current activity slotUpdateActivity(d->m_activity->type()); } void InvestTransactionEditor::slotUpdateActivity(eMyMoney::Split::InvestmentTransactionType activity) { Q_D(InvestTransactionEditor); // create new activity object if required d->activityFactory(activity); // hide all dynamic widgets d->hideCategory("interest-account"); d->hideCategory("fee-account"); QStringList dynwidgets; dynwidgets << "total-label" << "asset-label" << "fee-label" << "fee-amount-label" << "interest-label" << "interest-amount-label" << "price-label" << "shares-label"; // hiding labels works by clearing them. hide() does not do the job // as the underlying text in the QTable object will shine through QStringList::const_iterator it_s; for (it_s = dynwidgets.constBegin(); it_s != dynwidgets.constEnd(); ++it_s) { QLabel* w = dynamic_cast(haveWidget(*it_s)); if (w) w->setText(" "); } // real widgets can be hidden dynwidgets.clear(); dynwidgets << "asset-account" << "interest-amount" << "fee-amount" << "shares" << "price" << "total"; for (it_s = dynwidgets.constBegin(); it_s != dynwidgets.constEnd(); ++it_s) { QWidget* w = haveWidget(*it_s); if (w) w->hide(); } d->m_activity->showWidgets(); d->m_activity->preloadAssetAccount(); if (KMyMoneyCategory* cat = dynamic_cast(haveWidget("interest-account"))) { if (cat->parentWidget()->isVisible()) slotUpdateInterestVisibility(cat->currentText()); else cat->splitButton()->hide(); } if (KMyMoneyCategory* cat = dynamic_cast(haveWidget("fee-account"))) { if (cat->parentWidget()->isVisible()) slotUpdateFeeVisibility(cat->currentText()); else cat->splitButton()->hide(); } } eDialogs::PriceMode InvestTransactionEditor::priceMode() const { Q_D(const InvestTransactionEditor); eDialogs::PriceMode mode = static_cast(eDialogs::PriceMode::Price); KMyMoneySecurity* sec = dynamic_cast(d->m_editWidgets["security"]); QString accId; if (!sec->currentText().isEmpty()) { accId = sec->selectedItem(); if (accId.isEmpty()) accId = d->m_account.id(); } while (!accId.isEmpty() && mode == eDialogs::PriceMode::Price) { MyMoneyAccount acc = MyMoneyFile::instance()->account(accId); if (acc.value("priceMode").isEmpty()) accId = acc.parentAccountId(); else mode = static_cast(acc.value("priceMode").toInt()); } // if mode is still then use that if (mode == eDialogs::PriceMode::Price) mode = eDialogs::PriceMode::PricePerShare; return mode; } MyMoneySecurity InvestTransactionEditor::security() const { Q_D(const InvestTransactionEditor); return d->m_security; } QList InvestTransactionEditor::feeSplits() const { Q_D(const InvestTransactionEditor); return d->m_feeSplits; } QList InvestTransactionEditor::interestSplits() const { Q_D(const InvestTransactionEditor); return d->m_interestSplits; } bool InvestTransactionEditor::setupPrice(const MyMoneyTransaction& t, MyMoneySplit& split) { Q_D(InvestTransactionEditor); auto file = MyMoneyFile::instance(); MyMoneyAccount acc = file->account(split.accountId()); MyMoneySecurity toCurrency(file->security(acc.currencyId())); int fract = acc.fraction(); if (acc.currencyId() != t.commodity()) { if (acc.currencyId().isEmpty()) acc.setCurrencyId(t.commodity()); QMap::Iterator it_p; QString key = t.commodity() + '-' + acc.currencyId(); it_p = d->m_priceInfo.find(key); // if it's not found, then collect it from the user first MyMoneyMoney price; if (it_p == d->m_priceInfo.end()) { MyMoneySecurity fromCurrency = file->security(t.commodity()); MyMoneyMoney fromValue, toValue; fromValue = split.value(); const MyMoneyPrice &priceInfo = MyMoneyFile::instance()->price(fromCurrency.id(), toCurrency.id(), t.postDate()); toValue = split.value() * priceInfo.rate(toCurrency.id()); QPointer calc = new KCurrencyCalculator(fromCurrency, toCurrency, fromValue, toValue, t.postDate(), fract, d->m_regForm); if (calc->exec() == QDialog::Rejected) { delete calc; return false; } price = calc->price(); delete calc; d->m_priceInfo[key] = price; } else { price = (*it_p); } // update shares if the transaction commodity is the currency // of the current selected account split.setShares(split.value() * price); } else { split.setShares(split.value()); } return true; } bool InvestTransactionEditor::createTransaction(MyMoneyTransaction& t, const MyMoneyTransaction& torig, const MyMoneySplit& sorig, bool /* skipPriceDialog */) { Q_D(InvestTransactionEditor); auto file = MyMoneyFile::instance(); // we start with the previous values, make sure we can add them later on t = torig; MyMoneySplit s0 = sorig; s0.clearId(); KMyMoneySecurity* sec = dynamic_cast(d->m_editWidgets["security"]); if (!isMultiSelection() || (isMultiSelection() && !sec->currentText().isEmpty())) { QString securityId = sec->selectedItem(); if (!securityId.isEmpty()) { s0.setAccountId(securityId); MyMoneyAccount stockAccount = file->account(securityId); QString currencyId = stockAccount.currencyId(); MyMoneySecurity security = file->security(currencyId); t.setCommodity(security.tradingCurrency()); } else { s0.setAccountId(d->m_account.id()); t.setCommodity(d->m_account.currencyId()); } } // extract price info from original transaction d->m_priceInfo.clear(); QList::const_iterator it_s; if (!torig.id().isEmpty()) { for (it_s = torig.splits().begin(); it_s != torig.splits().end(); ++it_s) { if ((*it_s).id() != sorig.id()) { MyMoneyAccount cat = file->account((*it_s).accountId()); if (cat.currencyId() != d->m_account.currencyId()) { if (cat.currencyId().isEmpty()) cat.setCurrencyId(d->m_account.currencyId()); if (!(*it_s).shares().isZero() && !(*it_s).value().isZero()) { d->m_priceInfo[cat.currencyId()] = ((*it_s).shares() / (*it_s).value()).reduce(); } } } } } t.removeSplits(); kMyMoneyDateInput* postDate = dynamic_cast(d->m_editWidgets["postdate"]); if (postDate->date().isValid()) { t.setPostDate(postDate->date()); } // memo and number field are special: if we have multiple transactions selected // and the edit field is empty, we treat it as "not modified". // FIXME a better approach would be to have a 'dirty' flag with the widgets // which identifies if the originally loaded value has been modified // by the user KTextEdit* memo = dynamic_cast(d->m_editWidgets["memo"]); if (memo) { if (!isMultiSelection() || (isMultiSelection() && d->m_activity->memoChanged())) s0.setMemo(memo->toPlainText()); } MyMoneySplit assetAccountSplit; QList feeSplits; QList interestSplits; MyMoneySecurity security, currency; eMyMoney::Split::InvestmentTransactionType transactionType; // extract the splits from the original transaction KMyMoneyUtils::dissectTransaction(torig, sorig, assetAccountSplit, feeSplits, interestSplits, security, currency, transactionType); // check if the trading currency is the same if the security has changed // in case it differs, check that we have a price (request from user) // and convert all splits // TODO // do the conversions here // TODO // keep the current activity object and create a new one // that can be destroyed later on auto activity = d->m_activity; d->m_activity = 0; // make sure we create a new one d->activityFactory(activity->type()); // if the activity is not set in the combo widget, we keep // the one which is used in the original transaction auto activityCombo = dynamic_cast(haveWidget("activity")); if (activityCombo->activity() == eMyMoney::Split::InvestmentTransactionType::UnknownTransactionType) { d->activityFactory(transactionType); } // if we mark the split reconciled here, we'll use today's date if no reconciliation date is given auto status = dynamic_cast(d->m_editWidgets["status"]); if (status->state() != eMyMoney::Split::State::Unknown) s0.setReconcileFlag(status->state()); if (s0.reconcileFlag() == eMyMoney::Split::State::Reconciled && !s0.reconcileDate().isValid()) s0.setReconcileDate(QDate::currentDate()); // call the creation logic for the current selected activity bool rc = d->m_activity->createTransaction(t, s0, assetAccountSplit, feeSplits, d->m_feeSplits, interestSplits, d->m_interestSplits, security, currency); // now switch back to the original activity delete d->m_activity; d->m_activity = activity; // add the splits to the transaction if (rc) { if (security.name().isEmpty()) // new transaction has no security filled... security = file->security(file->account(s0.accountId()).currencyId()); // ...so fetch it from s0 split QList resultSplits; // concatenates splits for easy processing if (!assetAccountSplit.accountId().isEmpty()) resultSplits.append(assetAccountSplit); if (!feeSplits.isEmpty()) resultSplits.append(feeSplits); if (!interestSplits.isEmpty()) resultSplits.append(interestSplits); AlkValue::RoundingMethod roundingMethod = AlkValue::RoundRound; if (security.roundingMethod() != AlkValue::RoundNever) roundingMethod = security.roundingMethod(); int currencyFraction = currency.smallestAccountFraction(); int securityFraction = security.smallestAccountFraction(); // assuming that all non-stock splits are monetary foreach (auto split, resultSplits) { split.clearId(); split.setShares(split.shares().convertDenominator(currencyFraction, roundingMethod)); split.setValue(split.value().convertDenominator(currencyFraction, roundingMethod)); t.addSplit(split); } s0.setShares(s0.shares().convertDenominator(securityFraction, roundingMethod)); // only shares variable from stock split isn't evaluated in currency s0.setValue(s0.value().convertDenominator(currencyFraction, roundingMethod)); t.addSplit(s0); } return rc; } void InvestTransactionEditor::setupFinalWidgets() { addFinalWidget(haveWidget("memo")); } void InvestTransactionEditor::slotUpdateInvestMemoState() { Q_D(InvestTransactionEditor); auto memo = dynamic_cast(d->m_editWidgets["memo"]); if (memo) { d->m_activity->memoChanged() = (memo->toPlainText() != d->m_activity->memoText()); } } diff --git a/kmymoney/dialogs/kcurrencycalculator.cpp b/kmymoney/dialogs/kcurrencycalculator.cpp index 543b44397..fbcc5d6d1 100644 --- a/kmymoney/dialogs/kcurrencycalculator.cpp +++ b/kmymoney/dialogs/kcurrencycalculator.cpp @@ -1,385 +1,384 @@ /*************************************************************************** kcurrencycalculator.cpp - description ------------------- begin : Thu Apr 8 2004 copyright : (C) 2000-2004 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kcurrencycalculator.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include -#include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_kcurrencycalculator.h" #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "mymoneysecurity.h" #include "kmymoneyedit.h" #include "kmymoneydateinput.h" #include "mymoneyprice.h" #include "mymoneymoney.h" #include "mymoneysplit.h" #include "mymoneytransaction.h" #include "kmymoneyglobalsettings.h" class KCurrencyCalculatorPrivate { Q_DISABLE_COPY(KCurrencyCalculatorPrivate) Q_DECLARE_PUBLIC(KCurrencyCalculator) public: KCurrencyCalculatorPrivate(KCurrencyCalculator *qq, const MyMoneySecurity& from, const MyMoneySecurity& to, const MyMoneyMoney& value, const MyMoneyMoney& shares, const QDate& date, const signed64 resultFraction) : q_ptr(qq), ui(new Ui::KCurrencyCalculator), m_fromCurrency(from), m_toCurrency(to), m_result(shares.abs()), m_value(value.abs()), m_date(date), m_resultFraction(resultFraction) { } ~KCurrencyCalculatorPrivate() { delete ui; } void init() { Q_Q(KCurrencyCalculator); ui->setupUi(q); auto file = MyMoneyFile::instance(); //set main widget of QDialog ui->buttonGroup1->setId(ui->m_amountButton, 0); ui->buttonGroup1->setId(ui->m_rateButton, 1); ui->m_dateFrame->hide(); if (m_date.isValid()) ui->m_dateEdit->setDate(m_date); else ui->m_dateEdit->setDate(QDate::currentDate()); ui->m_fromCurrencyText->setText(QString(MyMoneySecurity::securityTypeToString(m_fromCurrency.securityType()) + ' ' + (m_fromCurrency.isCurrency() ? m_fromCurrency.id() : m_fromCurrency.tradingSymbol()))); ui->m_toCurrencyText->setText(QString(MyMoneySecurity::securityTypeToString(m_toCurrency.securityType()) + ' ' + (m_toCurrency.isCurrency() ? m_toCurrency.id() : m_toCurrency.tradingSymbol()))); //set bold font auto boldFont = ui->m_fromCurrencyText->font(); boldFont.setBold(true); ui->m_fromCurrencyText->setFont(boldFont); boldFont = ui->m_toCurrencyText->font(); boldFont.setBold(true); ui->m_toCurrencyText->setFont(boldFont); ui->m_fromAmount->setText(m_value.formatMoney(QString(), MyMoneyMoney::denomToPrec(m_fromCurrency.smallestAccountFraction()))); ui->m_dateText->setText(QLocale().toString(m_date)); ui->m_updateButton->setChecked(KMyMoneyGlobalSettings::priceHistoryUpdate()); // setup initial result if (m_result == MyMoneyMoney() && !m_value.isZero()) { const MyMoneyPrice &pr = file->price(m_fromCurrency.id(), m_toCurrency.id(), m_date); if (pr.isValid()) { m_result = m_value * pr.rate(m_toCurrency.id()); } } // fill in initial values ui->m_toAmount->loadText(m_result.formatMoney(QString(), MyMoneyMoney::denomToPrec(m_resultFraction))); ui->m_toAmount->setPrecision(MyMoneyMoney::denomToPrec(m_resultFraction)); ui->m_conversionRate->setPrecision(m_fromCurrency.pricePrecision()); q->connect(ui->m_amountButton, &QAbstractButton::clicked, q, &KCurrencyCalculator::slotSetToAmount); q->connect(ui->m_rateButton, &QAbstractButton::clicked, q, &KCurrencyCalculator::slotSetExchangeRate); q->connect(ui->m_toAmount, &kMyMoneyEdit::valueChanged, q, &KCurrencyCalculator::slotUpdateResult); q->connect(ui->m_conversionRate, &kMyMoneyEdit::valueChanged, q, &KCurrencyCalculator::slotUpdateRate); // use this as the default ui->m_amountButton->animateClick(); q->slotUpdateResult(ui->m_toAmount->text()); // If the from security is not a currency, we only allow entering a price if (!m_fromCurrency.isCurrency()) { ui->m_rateButton->animateClick(); ui->m_amountButton->hide(); ui->m_toAmount->hide(); } } void updateExample(const MyMoneyMoney& price) { QString msg; if (price.isZero()) { msg = QString("1 %1 = ? %2").arg(m_fromCurrency.tradingSymbol()) .arg(m_toCurrency.tradingSymbol()); if (m_fromCurrency.isCurrency()) { msg += QString("\n"); msg += QString("1 %1 = ? %2").arg(m_toCurrency.tradingSymbol()) .arg(m_fromCurrency.tradingSymbol()); } } else { msg = QString("1 %1 = %2 %3").arg(m_fromCurrency.tradingSymbol()) .arg(price.formatMoney(QString(), m_fromCurrency.pricePrecision())) .arg(m_toCurrency.tradingSymbol()); if (m_fromCurrency.isCurrency()) { msg += QString("\n"); msg += QString("1 %1 = %2 %3").arg(m_toCurrency.tradingSymbol()) .arg((MyMoneyMoney::ONE / price).formatMoney(QString(), m_toCurrency.pricePrecision())) .arg(m_fromCurrency.tradingSymbol()); } } ui->m_conversionExample->setText(msg); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!price.isZero()); } KCurrencyCalculator *q_ptr; Ui::KCurrencyCalculator *ui; MyMoneySecurity m_fromCurrency; MyMoneySecurity m_toCurrency; MyMoneyMoney m_result; MyMoneyMoney m_value; QDate m_date; signed64 m_resultFraction; }; KCurrencyCalculator::KCurrencyCalculator(const MyMoneySecurity& from, const MyMoneySecurity& to, const MyMoneyMoney& value, const MyMoneyMoney& shares, const QDate& date, const signed64 resultFraction, QWidget *parent) : QDialog(parent), d_ptr(new KCurrencyCalculatorPrivate(this, from, to, value, shares, date, resultFraction)) { Q_D(KCurrencyCalculator); d->init(); } KCurrencyCalculator::~KCurrencyCalculator() { Q_D(KCurrencyCalculator); delete d; } bool KCurrencyCalculator::setupSplitPrice(MyMoneyMoney& shares, const MyMoneyTransaction& t, const MyMoneySplit& s, const QMap& priceInfo, QWidget* parentWidget) { auto rc = true; auto file = MyMoneyFile::instance(); if (!s.value().isZero()) { auto cat = file->account(s.accountId()); MyMoneySecurity toCurrency; toCurrency = file->security(cat.currencyId()); // determine the fraction required for this category/account int fract = cat.fraction(toCurrency); if (cat.currencyId() != t.commodity()) { MyMoneyMoney toValue; auto fromCurrency = file->security(t.commodity()); // display only positive values to the user auto fromValue = s.value().abs(); // if we had a price info in the beginning, we use it here if (priceInfo.find(cat.currencyId()) != priceInfo.end()) { toValue = (fromValue * priceInfo[cat.currencyId()]).convert(fract); } // if the shares are still 0, we need to change that if (toValue.isZero()) { const MyMoneyPrice &price = file->price(fromCurrency.id(), toCurrency.id(), t.postDate()); // if the price is valid calculate the shares. If it is invalid // assume a conversion rate of 1.0 if (price.isValid()) { toValue = (price.rate(toCurrency.id()) * fromValue).convert(fract); } else { toValue = fromValue; } } // now present all that to the user QPointer calc = new KCurrencyCalculator(fromCurrency, toCurrency, fromValue, toValue, t.postDate(), fract, parentWidget); if (calc->exec() == QDialog::Rejected) { rc = false; } else shares = (s.value() * calc->price()).convert(fract); delete calc; } else { shares = s.value().convert(fract); } } else shares = s.value(); return rc; } void KCurrencyCalculator::setupPriceEditor() { Q_D(KCurrencyCalculator); d->ui->m_dateFrame->show(); d->ui->m_amountDateFrame->hide(); d->ui->m_updateButton->setChecked(true); d->ui->m_updateButton->hide(); } void KCurrencyCalculator::slotSetToAmount() { Q_D(KCurrencyCalculator); d->ui->m_rateButton->setChecked(false); d->ui->m_toAmount->setEnabled(true); d->ui->m_conversionRate->setEnabled(false); } void KCurrencyCalculator::slotSetExchangeRate() { Q_D(KCurrencyCalculator); d->ui->m_amountButton->setChecked(false); d->ui->m_toAmount->setEnabled(false); d->ui->m_conversionRate->setEnabled(true); } void KCurrencyCalculator::slotUpdateResult(const QString& /*txt*/) { Q_D(KCurrencyCalculator); MyMoneyMoney result = d->ui->m_toAmount->value(); MyMoneyMoney price(0, 1); if (result.isNegative()) { d->ui->m_toAmount->setValue(-result); slotUpdateResult(QString()); return; } if (!result.isZero()) { price = result / d->m_value; d->ui->m_conversionRate->loadText(price.formatMoney(QString(), d->m_fromCurrency.pricePrecision())); d->m_result = (d->m_value * price).convert(d->m_resultFraction); d->ui->m_toAmount->loadText(d->m_result.formatMoney(d->m_resultFraction)); } d->updateExample(price); } void KCurrencyCalculator::slotUpdateRate(const QString& /*txt*/) { Q_D(KCurrencyCalculator); auto price = d->ui->m_conversionRate->value(); if (price.isNegative()) { d->ui->m_conversionRate->setValue(-price); slotUpdateRate(QString()); return; } if (!price.isZero()) { d->ui->m_conversionRate->loadText(price.formatMoney(QString(), d->m_fromCurrency.pricePrecision())); d->m_result = (d->m_value * price).convert(d->m_resultFraction); d->ui->m_toAmount->loadText(d->m_result.formatMoney(QString(), MyMoneyMoney::denomToPrec(d->m_resultFraction))); } d->updateExample(price); } void KCurrencyCalculator::accept() { Q_D(KCurrencyCalculator); if (d->ui->m_conversionRate->isEnabled()) slotUpdateRate(QString()); else slotUpdateResult(QString()); if (d->ui->m_updateButton->isChecked()) { auto pr = MyMoneyFile::instance()->price(d->m_fromCurrency.id(), d->m_toCurrency.id(), d->ui->m_dateEdit->date()); if (!pr.isValid() || pr.date() != d->ui->m_dateEdit->date() || (pr.date() == d->ui->m_dateEdit->date() && pr.rate(d->m_fromCurrency.id()) != price())) { pr = MyMoneyPrice(d->m_fromCurrency.id(), d->m_toCurrency.id(), d->ui->m_dateEdit->date(), price(), i18n("User")); MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->addPrice(pr); ft.commit(); } catch (const MyMoneyException &) { qDebug("Cannot add price"); } } } // remember setting for next round KMyMoneyGlobalSettings::setPriceHistoryUpdate(d->ui->m_updateButton->isChecked()); QDialog::accept(); } MyMoneyMoney KCurrencyCalculator::price() const { Q_D(const KCurrencyCalculator); // This should fix https://bugs.kde.org/show_bug.cgi?id=205254 and // https://bugs.kde.org/show_bug.cgi?id=325953 as well as // https://bugs.kde.org/show_bug.cgi?id=300965 if (d->ui->m_amountButton->isChecked()) return d->ui->m_toAmount->value().abs() / d->m_value.abs(); else return d->ui->m_conversionRate->value(); } diff --git a/kmymoney/dialogs/keditscheduledlg.cpp b/kmymoney/dialogs/keditscheduledlg.cpp index e81deb356..c83f3e396 100644 --- a/kmymoney/dialogs/keditscheduledlg.cpp +++ b/kmymoney/dialogs/keditscheduledlg.cpp @@ -1,662 +1,661 @@ /*************************************************************************** keditscheduledlg.cpp - description ------------------- begin : Mon Sep 3 2007 copyright : (C) 2007 by Thomas Baumgart email : Thomas Baumgart (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "keditscheduledlg.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_keditscheduledlg.h" #include "mymoneyfile.h" #include "mymoneyschedule.h" -#include "mymoneysplit.h" #include "mymoneytransaction.h" #include "register.h" #include "transactionform.h" #include "transaction.h" #include "transactioneditor.h" #include "kmymoneylineedit.h" #include "kmymoneydateinput.h" #include "kmymoneymvccombo.h" #include "kguiutils.h" #include "kmymoney.h" #include "mymoneyenums.h" using namespace eMyMoney; class KEditScheduleDlgPrivate { Q_DISABLE_COPY(KEditScheduleDlgPrivate) Q_DECLARE_PUBLIC(KEditScheduleDlg) public: KEditScheduleDlgPrivate(KEditScheduleDlg *qq) : q_ptr(qq), ui(new Ui::KEditScheduleDlg) { } ~KEditScheduleDlgPrivate() { delete ui; } void init() { Q_Q(KEditScheduleDlg); ui->setupUi(q); m_requiredFields = new kMandatoryFieldGroup(q); m_requiredFields->setOkButton(ui->buttonBox->button(QDialogButtonBox::Ok)); // button to be enabled when all fields present // make sure, we have a tabbar with the form // insert it after the horizontal line ui->m_paymentInformationLayout->insertWidget(2, ui->m_form->tabBar(ui->m_form->parentWidget())); // we never need to see the register ui->m_register->hide(); // ... setup the form ... ui->m_form->setupForm(m_schedule.account()); // ... and the register ... ui->m_register->clear(); // ... now add the transaction to register and form ... auto t = transaction(); if (m_schedule.transaction().splits().isEmpty()) m_item = KMyMoneyRegister::Register::transactionFactory(ui->m_register, t, MyMoneySplit(), 0); else m_item = KMyMoneyRegister::Register::transactionFactory(ui->m_register, t, m_schedule.transaction().splits().isEmpty() ? MyMoneySplit() : m_schedule.transaction().splits().front(), 0); ui->m_register->selectItem(m_item); // show the account row m_item->setShowRowInForm(0, true); ui->m_form->slotSetTransaction(m_item); // setup widget contents ui->m_nameEdit->setText(m_schedule.name()); ui->m_frequencyEdit->setCurrentItem((int)m_schedule.occurrencePeriod()); if (ui->m_frequencyEdit->currentItem() == Schedule::Occurrence::Any) ui->m_frequencyEdit->setCurrentItem((int)Schedule::Occurrence::Monthly); q->slotFrequencyChanged((int)ui->m_frequencyEdit->currentItem()); ui->m_frequencyNoEdit->setValue(m_schedule.occurrenceMultiplier()); // load option widgets ui->m_paymentMethodEdit->insertItem(i18n("Direct deposit"), (int)Schedule::PaymentType::DirectDeposit); ui->m_paymentMethodEdit->insertItem(i18n("Manual deposit"), (int)Schedule::PaymentType::ManualDeposit); ui->m_paymentMethodEdit->insertItem(i18n("Direct debit"), (int)Schedule::PaymentType::DirectDebit); ui->m_paymentMethodEdit->insertItem(i18n("Standing order"), (int)Schedule::PaymentType::StandingOrder); ui->m_paymentMethodEdit->insertItem(i18n("Bank transfer"), (int)Schedule::PaymentType::BankTransfer); ui->m_paymentMethodEdit->insertItem(i18n("Write check"), (int)Schedule::PaymentType::WriteChecque); ui->m_paymentMethodEdit->insertItem(i18nc("Other payment method", "Other"), (int)Schedule::PaymentType::Other); auto method = m_schedule.paymentType(); if (method == Schedule::PaymentType::Any) method = Schedule::PaymentType::Other; ui->m_paymentMethodEdit->setCurrentItem((int)method); switch (m_schedule.weekendOption()) { case Schedule::WeekendOption::MoveNothing: ui->m_weekendOptionEdit->setCurrentIndex(0); break; case Schedule::WeekendOption::MoveBefore: ui->m_weekendOptionEdit->setCurrentIndex(1); break; case Schedule::WeekendOption::MoveAfter: ui->m_weekendOptionEdit->setCurrentIndex(2); break; } ui->m_estimateEdit->setChecked(!m_schedule.isFixed()); ui->m_lastDayInMonthEdit->setChecked(m_schedule.lastDayInMonth()); ui->m_autoEnterEdit->setChecked(m_schedule.autoEnter()); ui->m_endSeriesEdit->setChecked(m_schedule.willEnd()); ui->m_endOptionsFrame->setEnabled(m_schedule.willEnd()); if (m_schedule.willEnd()) { ui->m_RemainingEdit->setValue(m_schedule.transactionsRemaining()); ui->m_FinalPaymentEdit->setDate(m_schedule.endDate()); } q->connect(ui->m_RemainingEdit, static_cast(&QSpinBox::valueChanged), q, &KEditScheduleDlg::slotRemainingChanged); q->connect(ui->m_FinalPaymentEdit, &kMyMoneyDateInput::dateChanged, q, &KEditScheduleDlg::slotEndDateChanged); q->connect(ui->m_frequencyEdit, &KMyMoneyGeneralCombo::itemSelected, q, &KEditScheduleDlg::slotFrequencyChanged); q->connect(ui->m_frequencyNoEdit, static_cast(&QSpinBox::valueChanged), q, &KEditScheduleDlg::slotOccurrenceMultiplierChanged); q->connect(ui->buttonBox, &QDialogButtonBox::helpRequested, q, &KEditScheduleDlg::slotShowHelp); q->setModal(true); // force the initial height to be as small as possible QTimer::singleShot(0, q, SLOT(slotSetupSize())); // we just hide the variation field for now and enable the logic // once we have a respective member in the MyMoneySchedule object ui->m_variation->hide(); } /** * Helper method to recalculate and update Transactions Remaining * when other values are changed */ void updateTransactionsRemaining() { auto remain = m_schedule.transactionsRemaining(); if (remain != ui->m_RemainingEdit->value()) { ui->m_RemainingEdit->blockSignals(true); ui->m_RemainingEdit->setValue(remain); ui->m_RemainingEdit->blockSignals(false); } } MyMoneyTransaction transaction() const { auto t = m_schedule.transaction(); if (m_editor) { m_editor->createTransaction(t, m_schedule.transaction(), m_schedule.transaction().splits().isEmpty() ? MyMoneySplit() : m_schedule.transaction().splits().front(), false); } t.clearId(); t.setEntryDate(QDate()); return t; } KEditScheduleDlg *q_ptr; Ui::KEditScheduleDlg *ui; MyMoneySchedule m_schedule; KMyMoneyRegister::Transaction* m_item; QWidgetList m_tabOrderWidgets; TransactionEditor* m_editor; kMandatoryFieldGroup* m_requiredFields; }; KEditScheduleDlg::KEditScheduleDlg(const MyMoneySchedule& schedule, QWidget *parent) : QDialog(parent), d_ptr(new KEditScheduleDlgPrivate(this)) { Q_D(KEditScheduleDlg); d->m_schedule = schedule; d->m_editor = 0; d->init(); } KEditScheduleDlg::~KEditScheduleDlg() { Q_D(KEditScheduleDlg); delete d; } void KEditScheduleDlg::slotSetupSize() { resize(width(), minimumSizeHint().height()); } TransactionEditor* KEditScheduleDlg::startEdit() { Q_D(KEditScheduleDlg); KMyMoneyRegister::SelectedTransactions list(d->ui->m_register); TransactionEditor* editor = d->m_item->createEditor(d->ui->m_form, list, QDate()); // check that we use the same transaction commodity in all selected transactions // if not, we need to update this in the editor's list. The user can also bail out // of this operation which means that we have to stop editing here. if (editor && !d->m_schedule.account().id().isEmpty()) { if (!editor->fixTransactionCommodity(d->m_schedule.account())) { // if the user wants to quit, we need to destroy the editor // and bail out delete editor; editor = 0; } } if (editor) { editor->setScheduleInfo(d->ui->m_nameEdit->text()); connect(editor, &TransactionEditor::transactionDataSufficient, d->ui->buttonBox->button(QDialogButtonBox::Ok), &QWidget::setEnabled); connect(editor, &TransactionEditor::escapePressed, d->ui->buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::animateClick); connect(editor, &TransactionEditor::returnPressed, d->ui->buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::animateClick); connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, editor, &TransactionEditor::slotReloadEditWidgets); // connect(editor, SIGNAL(finishEdit(KMyMoneyRegister::SelectedTransactions)), this, SLOT(slotLeaveEditMode(KMyMoneyRegister::SelectedTransactions))); connect(editor, &TransactionEditor::createPayee, kmymoney, static_cast(&KMyMoneyApp::slotPayeeNew)); connect(editor, &TransactionEditor::createTag, kmymoney, static_cast(&KMyMoneyApp::slotTagNew)); connect(editor, &TransactionEditor::createCategory, kmymoney, static_cast(&KMyMoneyApp::slotCategoryNew)); connect(editor, &TransactionEditor::createSecurity, kmymoney, static_cast(&KMyMoneyApp::slotInvestmentNew)); connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, editor, &TransactionEditor::slotReloadEditWidgets); // create the widgets, place them in the parent and load them with data // setup tab order d->m_tabOrderWidgets.clear(); KMyMoneyRegister::Action action = KMyMoneyRegister::ActionWithdrawal; switch (d->m_schedule.type()) { case Schedule::Type::Deposit: action = KMyMoneyRegister::ActionDeposit; break; case Schedule::Type::Bill: action = KMyMoneyRegister::ActionWithdrawal; editor->setPaymentMethod(d->m_schedule.paymentType()); break; case Schedule::Type::Transfer: action = KMyMoneyRegister::ActionTransfer; break; default: // if we end up here, we don't have a known schedule type (yet). in this case, we just glimpse // into the transaction and determine the type. in case we don't have a transaction with splits // we stick with the default action already set up if (d->m_schedule.transaction().splits().count() > 0) { auto isDeposit = false; auto isTransfer = false; auto splits = d->m_schedule.transaction().splits(); foreach (const auto split, splits) { if (split.accountId() == d->m_schedule.account().id()) { isDeposit = !(split.shares().isNegative()); } else { auto acc = MyMoneyFile::instance()->account(split.accountId()); if (acc.isAssetLiability() && d->m_schedule.transaction().splits().count() == 2) { isTransfer = true; } } } if (isTransfer) action = KMyMoneyRegister::ActionTransfer; else if (isDeposit) action = KMyMoneyRegister::ActionDeposit; } break; } editor->setup(d->m_tabOrderWidgets, d->m_schedule.account(), action); // if it's not a check, then we need to clear // a possibly assigned check number if (d->m_schedule.paymentType() != Schedule::PaymentType::WriteChecque) { QWidget* w = editor->haveWidget("number"); if (w) dynamic_cast(w)->loadText(QString()); } Q_ASSERT(!d->m_tabOrderWidgets.isEmpty()); d->m_tabOrderWidgets.push_front(d->ui->m_paymentMethodEdit); // editor->setup() leaves the tabbar as the last widget in the stack, but we // need it as first here. So we move it around. QWidget* w = editor->haveWidget("tabbar"); if (w) { int idx = d->m_tabOrderWidgets.indexOf(w); if (idx != -1) { d->m_tabOrderWidgets.removeAt(idx); d->m_tabOrderWidgets.push_front(w); } } // don't forget our three buttons and additional widgets // make sure to use the correct order d->m_tabOrderWidgets.push_front(d->ui->m_frequencyEdit); d->m_tabOrderWidgets.push_front(d->ui->m_frequencyNoEdit); d->m_tabOrderWidgets.push_front(d->ui->m_nameEdit); d->m_tabOrderWidgets.append(d->ui->m_weekendOptionEdit); d->m_tabOrderWidgets.append(d->ui->m_estimateEdit); d->m_tabOrderWidgets.append(d->ui->m_variation); d->m_tabOrderWidgets.append(d->ui->m_lastDayInMonthEdit); d->m_tabOrderWidgets.append(d->ui->m_autoEnterEdit); d->m_tabOrderWidgets.append(d->ui->m_endSeriesEdit); d->m_tabOrderWidgets.append(d->ui->m_RemainingEdit); d->m_tabOrderWidgets.append(d->ui->m_FinalPaymentEdit); d->m_tabOrderWidgets.append(d->ui->buttonBox->button(QDialogButtonBox::Ok)); d->m_tabOrderWidgets.append(d->ui->buttonBox->button(QDialogButtonBox::Cancel)); d->m_tabOrderWidgets.append(d->ui->buttonBox->button(QDialogButtonBox::Help)); for (auto i = 0; i < d->m_tabOrderWidgets.size(); ++i) { QWidget* w = d->m_tabOrderWidgets.at(i); if (w) { w->installEventFilter(this); w->installEventFilter(editor); } } // connect the postdate modification signal to our update routine kMyMoneyDateInput* dateEdit = dynamic_cast(editor->haveWidget("postdate")); if (dateEdit) connect(dateEdit, &kMyMoneyDateInput::dateChanged, this, &KEditScheduleDlg::slotPostDateChanged); d->ui->m_nameEdit->setFocus(); // add the required fields to the mandatory group d->m_requiredFields->add(d->ui->m_nameEdit); d->m_requiredFields->add(editor->haveWidget("account")); d->m_requiredFields->add(editor->haveWidget("category")); d->m_requiredFields->add(editor->haveWidget("amount")); // fix labels QLabel* label = dynamic_cast(editor->haveWidget("date-label")); if (label) { label->setText(i18n("Next due date")); } d->m_editor = editor; slotSetPaymentMethod((int)d->m_schedule.paymentType()); connect(d->ui->m_paymentMethodEdit, &KMyMoneyGeneralCombo::itemSelected, this, &KEditScheduleDlg::slotSetPaymentMethod); connect(editor, &TransactionEditor::operationTypeChanged, this, &KEditScheduleDlg::slotFilterPaymentType); } return editor; } void KEditScheduleDlg::accept() { Q_D(KEditScheduleDlg); // Force the focus to be on the OK button. This will trigger creation // of any unknown objects (payees, categories etc.) d->ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus(); // only accept if the button is really still enabled. We could end // up here, if the user filled all fields, the focus is on the category // field, but the category is not yet existent. When the user presses the // OK button in this context, he will be asked if he wants to create // the category or not. In case he decides no, we end up here with no // category filled in, so we don't run through the final acceptance. if (d->ui->buttonBox->button(QDialogButtonBox::Ok)->isEnabled()) QDialog::accept(); } const MyMoneySchedule& KEditScheduleDlg::schedule() { Q_D(KEditScheduleDlg); if (d->m_editor) { auto t = d->transaction(); if (d->m_schedule.nextDueDate() != t.postDate()) { d->m_schedule.setNextDueDate(t.postDate()); d->m_schedule.setStartDate(t.postDate()); } d->m_schedule.setTransaction(t); d->m_schedule.setName(d->ui->m_nameEdit->text()); d->m_schedule.setFixed(!d->ui->m_estimateEdit->isChecked()); d->m_schedule.setOccurrencePeriod(static_cast(d->ui->m_frequencyEdit->currentItem())); d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); switch (d->ui->m_weekendOptionEdit->currentIndex()) { case 0: d->m_schedule.setWeekendOption(Schedule::WeekendOption::MoveNothing); break; case 1: d->m_schedule.setWeekendOption(Schedule::WeekendOption::MoveBefore); break; case 2: d->m_schedule.setWeekendOption(Schedule::WeekendOption::MoveAfter); break; } d->m_schedule.setType(Schedule::Type::Bill); KMyMoneyTransactionForm::TabBar* tabbar = dynamic_cast(d->m_editor->haveWidget("tabbar")); if (tabbar) { switch (static_cast(tabbar->currentIndex())) { case KMyMoneyRegister::ActionDeposit: d->m_schedule.setType(Schedule::Type::Deposit); break; default: case KMyMoneyRegister::ActionWithdrawal: d->m_schedule.setType(Schedule::Type::Bill); break; case KMyMoneyRegister::ActionTransfer: d->m_schedule.setType(Schedule::Type::Transfer); break; } } else { qDebug("No tabbar found in KEditScheduleDlg::schedule(). Defaulting type to BILL"); } if(d->ui->m_lastDayInMonthEdit->isEnabled()) d->m_schedule.setLastDayInMonth(d->ui->m_lastDayInMonthEdit->isChecked()); else d->m_schedule.setLastDayInMonth(false); d->m_schedule.setAutoEnter(d->ui->m_autoEnterEdit->isChecked()); d->m_schedule.setPaymentType(static_cast(d->ui->m_paymentMethodEdit->currentItem())); if (d->ui->m_endSeriesEdit->isEnabled() && d->ui->m_endSeriesEdit->isChecked()) { d->m_schedule.setEndDate(d->ui->m_FinalPaymentEdit->date()); } else { d->m_schedule.setEndDate(QDate()); } } return d->m_schedule; } bool KEditScheduleDlg::focusNextPrevChild(bool next) { Q_D(KEditScheduleDlg); auto rc = false; auto w = qApp->focusWidget(); auto currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w); while (w && currentWidgetIndex == -1) { // qDebug("'%s' not in list, use parent", qPrintable(w->objectName())); w = w->parentWidget(); currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w); } if (currentWidgetIndex != -1) { do { // if(w) qDebug("tab order is at '%s (%d/%d)'", qPrintable(w->objectName()), currentWidgetIndex, d->m_tabOrderWidgets.size()); currentWidgetIndex += next ? 1 : -1; if (currentWidgetIndex < 0) currentWidgetIndex = d->m_tabOrderWidgets.size() - 1; else if (currentWidgetIndex >= d->m_tabOrderWidgets.size()) currentWidgetIndex = 0; w = d->m_tabOrderWidgets[currentWidgetIndex]; // qDebug("currentWidgetIndex = %d, w = %p", currentWidgetIndex, w); if (((w->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) && w->isVisible() && w->isEnabled()) { // qDebug("Selecting '%s' as focus", qPrintable(w->objectName())); w->setFocus(); rc = true; } } while (rc == false); } return rc; } void KEditScheduleDlg::resizeEvent(QResizeEvent* ev) { Q_D(KEditScheduleDlg); d->ui->m_register->resize(KMyMoneyRegister::DetailColumn); d->ui->m_form->resize(KMyMoneyTransactionForm::ValueColumn1); QDialog::resizeEvent(ev); } void KEditScheduleDlg::slotRemainingChanged(int value) { Q_D(KEditScheduleDlg); // Make sure the required fields are set auto dateEdit = dynamic_cast(d->m_editor->haveWidget("postdate")); d->m_schedule.setNextDueDate(dateEdit->date()); d->m_schedule.setOccurrencePeriod(static_cast(d->ui->m_frequencyEdit->currentItem())); d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); if (d->m_schedule.transactionsRemaining() != value) { d->ui->m_FinalPaymentEdit->blockSignals(true); d->ui->m_FinalPaymentEdit->setDate(d->m_schedule.dateAfter(value)); d->ui->m_FinalPaymentEdit->blockSignals(false); } } void KEditScheduleDlg::slotEndDateChanged(const QDate& date) { Q_D(KEditScheduleDlg); // Make sure the required fields are set auto dateEdit = dynamic_cast(d->m_editor->haveWidget("postdate")); d->m_schedule.setNextDueDate(dateEdit->date()); d->m_schedule.setOccurrencePeriod(static_cast(d->ui->m_frequencyEdit->currentItem())); d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); if (d->m_schedule.endDate() != date) { d->m_schedule.setEndDate(date); d->updateTransactionsRemaining(); } } void KEditScheduleDlg::slotPostDateChanged(const QDate& date) { Q_D(KEditScheduleDlg); if (d->m_schedule.nextDueDate() != date) { if (d->ui->m_endOptionsFrame->isEnabled()) { d->m_schedule.setNextDueDate(date); d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); d->m_schedule.setOccurrencePeriod(static_cast(d->ui->m_frequencyEdit->currentItem())); d->m_schedule.setEndDate(d->ui->m_FinalPaymentEdit->date()); d->updateTransactionsRemaining(); } } } void KEditScheduleDlg::slotSetPaymentMethod(int item) { Q_D(KEditScheduleDlg); auto dateEdit = dynamic_cast(d->m_editor->haveWidget("number")); if (dateEdit) { dateEdit->setVisible(item == (int)Schedule::PaymentType::WriteChecque); // hiding the label does not work, because the label underneath will shine // through. So we either write the label or a blank QLabel* label = dynamic_cast(d->m_editor->haveWidget("number-label")); if (label) { label->setText((item == (int)Schedule::PaymentType::WriteChecque) ? i18n("Number") : " "); } } } void KEditScheduleDlg::slotFrequencyChanged(int item) { Q_D(KEditScheduleDlg); d->ui->m_endSeriesEdit->setEnabled(item != (int)Schedule::Occurrence::Once); bool isEndSeries = d->ui->m_endSeriesEdit->isChecked(); if (isEndSeries) d->ui->m_endOptionsFrame->setEnabled(item != (int)Schedule::Occurrence::Once); switch (item) { case (int)Schedule::Occurrence::Daily: case (int)Schedule::Occurrence::Weekly: d->ui->m_frequencyNoEdit->setEnabled(true); d->ui->m_lastDayInMonthEdit->setEnabled(false); break; case (int)Schedule::Occurrence::EveryHalfMonth: case (int)Schedule::Occurrence::Monthly: case (int)Schedule::Occurrence::Yearly: // Supports Frequency Number d->ui->m_frequencyNoEdit->setEnabled(true); d->ui->m_lastDayInMonthEdit->setEnabled(true); break; default: // Multiplier is always 1 d->ui->m_frequencyNoEdit->setEnabled(false); d->ui->m_frequencyNoEdit->setValue(1); d->ui->m_lastDayInMonthEdit->setEnabled(true); break; } if (isEndSeries && (item != (int)Schedule::Occurrence::Once)) { // Changing the frequency changes the number // of remaining transactions kMyMoneyDateInput* dateEdit = dynamic_cast(d->m_editor->haveWidget("postdate")); d->m_schedule.setNextDueDate(dateEdit->date()); d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); d->m_schedule.setOccurrencePeriod(static_cast(item)); d->m_schedule.setEndDate(d->ui->m_FinalPaymentEdit->date()); d->updateTransactionsRemaining(); } } void KEditScheduleDlg::slotOccurrenceMultiplierChanged(int multiplier) { Q_D(KEditScheduleDlg); // Make sure the required fields are set auto oldOccurrenceMultiplier = d->m_schedule.occurrenceMultiplier(); if (multiplier != oldOccurrenceMultiplier) { if (d->ui->m_endOptionsFrame->isEnabled()) { kMyMoneyDateInput* dateEdit = dynamic_cast(d->m_editor->haveWidget("postdate")); d->m_schedule.setNextDueDate(dateEdit->date()); d->m_schedule.setOccurrenceMultiplier(multiplier); d->m_schedule.setOccurrencePeriod(static_cast(d->ui->m_frequencyEdit->currentItem())); d->m_schedule.setEndDate(d->ui->m_FinalPaymentEdit->date()); d->updateTransactionsRemaining(); } } } void KEditScheduleDlg::slotShowHelp() { KHelpClient::invokeHelp("details.schedules.intro"); } void KEditScheduleDlg::slotFilterPaymentType(int index) { Q_D(KEditScheduleDlg); //save selected item to reload if possible auto selectedId = d->ui->m_paymentMethodEdit->itemData(d->ui->m_paymentMethodEdit->currentIndex(), Qt::UserRole).toInt(); //clear and reload the widget with the correct items d->ui->m_paymentMethodEdit->clear(); // load option widgets KMyMoneyRegister::Action action = static_cast(index); if (action != KMyMoneyRegister::ActionWithdrawal) { d->ui->m_paymentMethodEdit->insertItem(i18n("Direct deposit"), (int)Schedule::PaymentType::DirectDeposit); d->ui->m_paymentMethodEdit->insertItem(i18n("Manual deposit"), (int)Schedule::PaymentType::ManualDeposit); } if (action != KMyMoneyRegister::ActionDeposit) { d->ui->m_paymentMethodEdit->insertItem(i18n("Direct debit"), (int)Schedule::PaymentType::DirectDebit); d->ui->m_paymentMethodEdit->insertItem(i18n("Write check"), (int)Schedule::PaymentType::WriteChecque); } d->ui->m_paymentMethodEdit->insertItem(i18n("Standing order"), (int)Schedule::PaymentType::StandingOrder); d->ui->m_paymentMethodEdit->insertItem(i18n("Bank transfer"), (int)Schedule::PaymentType::BankTransfer); d->ui->m_paymentMethodEdit->insertItem(i18nc("Other payment method", "Other"), (int)Schedule::PaymentType::Other); auto newIndex = d->ui->m_paymentMethodEdit->findData(QVariant(selectedId), Qt::UserRole, Qt::MatchExactly); if (newIndex > -1) { d->ui->m_paymentMethodEdit->setCurrentIndex(newIndex); } else { d->ui->m_paymentMethodEdit->setCurrentIndex(0); } } diff --git a/kmymoney/dialogs/keditscheduledlg.ui b/kmymoney/dialogs/keditscheduledlg.ui index 673b56eab..0b686493f 100644 --- a/kmymoney/dialogs/keditscheduledlg.ui +++ b/kmymoney/dialogs/keditscheduledlg.ui @@ -1,469 +1,469 @@ KEditScheduleDlg 0 0 798 640 Edit Scheduled transaction true Schedule name: false Qt::StrongFocus Frequency: false Qt::StrongFocus Number of selected periods between entries 1 999 Payment information Payment method false 0 0 Qt::Horizontal QSizePolicy::Expanding 61 21 QFrame::HLine QFrame::Sunken 0 12 5 1 Options Do nothing Change the date to the previous processing day Change the date to the next processing day If this schedule occurs on a non-processing day: false The amount is an estimate because it varies for each payment false 32767 10 Qt::Horizontal QSizePolicy::Expanding 21 20 Process this schedule always at the last day of a month Enter this schedule into the register automatically when it is due This schedule will end at some time QFrame::NoFrame QFrame::Raised 0 0 0 0 Qt::Horizontal QSizePolicy::Fixed 20 20 Number of transactions remaining: false 0 9999 Date of final transaction: false Qt::StrongFocus Qt::Vertical QSizePolicy::Expanding 20 16 QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok KComboBox QComboBox
kcombobox.h
KLineEdit QLineEdit
klineedit.h
KMyMoneyRegister::Register QWidget
register.h
KMyMoneyGeneralCombo QWidget -
kmymoneygeneralcombo.h
+
kmymoneymvccombo.h
1
KMyMoneyOccurrencePeriodCombo QWidget -
kmymoneyoccurrenceperiodcombo.h
+
kmymoneymvccombo.h
1
KMyMoneyTransactionForm::TransactionForm QWidget
transactionform.h
kMyMoneyDateInput QWidget
kmymoneydateinput.h
m_endSeriesEdit toggled(bool) m_endOptionsFrame setEnabled(bool) 20 20 20 20 m_estimateEdit toggled(bool) m_variation setEnabled(bool) 20 20 20 20 buttonBox accepted() KEditScheduleDlg accept() 398 616 398 319 buttonBox rejected() KEditScheduleDlg reject() 398 616 398 319
diff --git a/kmymoney/dialogs/kenterscheduledlg.cpp b/kmymoney/dialogs/kenterscheduledlg.cpp index a574bc883..8fe3f430a 100644 --- a/kmymoney/dialogs/kenterscheduledlg.cpp +++ b/kmymoney/dialogs/kenterscheduledlg.cpp @@ -1,382 +1,379 @@ /*************************************************************************** kenterscheduledlg.cpp ------------------- begin : Sat Apr 7 2007 copyright : (C) 2007 by Thomas Baumgart email : Thomas Baumgart (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kenterscheduledlg.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_kenterscheduledlg.h" - -#include "mymoneysplit.h" -#include "mymoneytransaction.h" #include "mymoneyfile.h" #include "mymoneyschedule.h" #include "register.h" #include "transactionform.h" #include "transaction.h" #include "transactioneditor.h" #include "kmymoneyutils.h" #include "kmymoneylineedit.h" #include "kmymoneydateinput.h" #include "kmymoney.h" #include "icons/icons.h" #include "mymoneyenums.h" #include "dialogenums.h" using namespace Icons; class KEnterScheduleDlgPrivate { Q_DISABLE_COPY(KEnterScheduleDlgPrivate) public: KEnterScheduleDlgPrivate() : ui(new Ui::KEnterScheduleDlg), m_item(nullptr), m_showWarningOnce(true) { } ~KEnterScheduleDlgPrivate() { delete ui; } Ui::KEnterScheduleDlg *ui; MyMoneySchedule m_schedule; KMyMoneyRegister::Transaction* m_item; QWidgetList m_tabOrderWidgets; bool m_showWarningOnce; eDialogs::ScheduleResultCode m_extendedReturnCode; }; KEnterScheduleDlg::KEnterScheduleDlg(QWidget *parent, const MyMoneySchedule& schedule) : QDialog(parent), d_ptr(new KEnterScheduleDlgPrivate) { Q_D(KEnterScheduleDlg); d->ui->setupUi(this); d->m_schedule = schedule; d->m_extendedReturnCode = eDialogs::ScheduleResultCode::Enter; d->ui->buttonOk->setIcon(QIcon::fromTheme(g_Icons[Icon::KeyEnter])); d->ui->buttonSkip->setIcon(QIcon::fromTheme(g_Icons[Icon::MediaSeekForward])); KGuiItem::assign(d->ui->buttonCancel, KStandardGuiItem::cancel()); KGuiItem::assign(d->ui->buttonHelp, KStandardGuiItem::help()); d->ui->buttonIgnore->setHidden(true); d->ui->buttonSkip->setHidden(true); // make sure, we have a tabbar with the form KMyMoneyTransactionForm::TabBar* tabbar = d->ui->m_form->tabBar(d->ui->m_form->parentWidget()); // we never need to see the register d->ui->m_register->hide(); // ... setup the form ... d->ui->m_form->setupForm(d->m_schedule.account()); // ... and the register ... d->ui->m_register->clear(); // ... now add the transaction to register and form ... MyMoneyTransaction t = transaction(); d->m_item = KMyMoneyRegister::Register::transactionFactory(d->ui->m_register, t, d->m_schedule.transaction().splits().isEmpty() ? MyMoneySplit() : d->m_schedule.transaction().splits().front(), 0); d->ui->m_register->selectItem(d->m_item); // show the account row d->m_item->setShowRowInForm(0, true); d->ui->m_form->slotSetTransaction(d->m_item); // no need to see the tabbar tabbar->hide(); // setup name and type d->ui->m_scheduleName->setText(d->m_schedule.name()); d->ui->m_type->setText(KMyMoneyUtils::scheduleTypeToString(d->m_schedule.type())); connect(d->ui->buttonHelp, &QAbstractButton::clicked, this, &KEnterScheduleDlg::slotShowHelp); connect(d->ui->buttonIgnore, &QAbstractButton::clicked, this, &KEnterScheduleDlg::slotIgnore); connect(d->ui->buttonSkip, &QAbstractButton::clicked, this, &KEnterScheduleDlg::slotSkip); } KEnterScheduleDlg::~KEnterScheduleDlg() { Q_D(KEnterScheduleDlg); delete d; } eDialogs::ScheduleResultCode KEnterScheduleDlg::resultCode() const { Q_D(const KEnterScheduleDlg); if (result() == QDialog::Accepted) return d->m_extendedReturnCode; return eDialogs::ScheduleResultCode::Cancel; } void KEnterScheduleDlg::showExtendedKeys(bool visible) { Q_D(KEnterScheduleDlg); d->ui->buttonIgnore->setVisible(visible); d->ui->buttonSkip->setVisible(visible); } void KEnterScheduleDlg::slotIgnore() { Q_D(KEnterScheduleDlg); d->m_extendedReturnCode = eDialogs::ScheduleResultCode::Ignore; accept(); } void KEnterScheduleDlg::slotSkip() { Q_D(KEnterScheduleDlg); d->m_extendedReturnCode = eDialogs::ScheduleResultCode::Skip; accept(); } MyMoneyTransaction KEnterScheduleDlg::transaction() { Q_D(KEnterScheduleDlg); auto t = d->m_schedule.transaction(); try { if (d->m_schedule.type() == eMyMoney::Schedule::Type::LoanPayment) { KMyMoneyUtils::calculateAutoLoan(d->m_schedule, t, QMap()); } } catch (const MyMoneyException &e) { KMessageBox::detailedError(this, i18n("Unable to load schedule details"), e.what()); } t.clearId(); t.setEntryDate(QDate()); return t; } QDate KEnterScheduleDlg::date(const QDate& _date) const { Q_D(const KEnterScheduleDlg); auto date(_date); return d->m_schedule.adjustedDate(date, d->m_schedule.weekendOption()); } void KEnterScheduleDlg::resizeEvent(QResizeEvent* ev) { Q_UNUSED(ev) Q_D(KEnterScheduleDlg); d->ui->m_register->resize(KMyMoneyRegister::DetailColumn); d->ui->m_form->resize(KMyMoneyTransactionForm::ValueColumn1); QDialog::resizeEvent(ev); } void KEnterScheduleDlg::slotSetupSize() { resize(width(), minimumSizeHint().height()); } int KEnterScheduleDlg::exec() { Q_D(KEnterScheduleDlg); if (d->m_showWarningOnce) { d->m_showWarningOnce = false; KMessageBox::information(this, QString("") + i18n("

Please check that all the details in the following dialog are correct and press OK.

Editable data can be changed and can either be applied to just this occurrence or for all subsequent occurrences for this schedule. (You will be asked what you intend after pressing OK in the following dialog)

") + QString("
"), i18n("Enter scheduled transaction"), "EnterScheduleDlgInfo"); } // force the initial height to be as small as possible QTimer::singleShot(0, this, SLOT(slotSetupSize())); return QDialog::exec(); } TransactionEditor* KEnterScheduleDlg::startEdit() { Q_D(KEnterScheduleDlg); KMyMoneyRegister::SelectedTransactions list(d->ui->m_register); TransactionEditor* editor = d->m_item->createEditor(d->ui->m_form, list, QDate()); editor->setScheduleInfo(d->m_schedule.name()); editor->setPaymentMethod(d->m_schedule.paymentType()); // check that we use the same transaction commodity in all selected transactions // if not, we need to update this in the editor's list. The user can also bail out // of this operation which means that we have to stop editing here. if (editor) { if (!editor->fixTransactionCommodity(d->m_schedule.account())) { // if the user wants to quit, we need to destroy the editor // and bail out delete editor; editor = 0; } } if (editor) { connect(editor, &TransactionEditor::transactionDataSufficient, d->ui->buttonOk, &QWidget::setEnabled); connect(editor, &TransactionEditor::escapePressed, d->ui->buttonCancel, &QAbstractButton::animateClick); connect(editor, &TransactionEditor::returnPressed, d->ui->buttonOk, &QAbstractButton::animateClick); connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, editor, &TransactionEditor::slotReloadEditWidgets); // connect(editor, SIGNAL(finishEdit(KMyMoneyRegister::SelectedTransactions)), this, SLOT(slotLeaveEditMode(KMyMoneyRegister::SelectedTransactions))); connect(editor, &TransactionEditor::createPayee, kmymoney, static_cast(&KMyMoneyApp::slotPayeeNew)); connect(editor, &TransactionEditor::createTag, kmymoney, static_cast(&KMyMoneyApp::slotTagNew)); connect(editor, &TransactionEditor::createCategory, kmymoney, static_cast(&KMyMoneyApp::slotCategoryNew)); connect(editor, &TransactionEditor::createSecurity, kmymoney, static_cast(&KMyMoneyApp::slotInvestmentNew)); connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, editor, &TransactionEditor::slotReloadEditWidgets); // create the widgets, place them in the parent and load them with data // setup tab order d->m_tabOrderWidgets.clear(); KMyMoneyRegister::Action action = KMyMoneyRegister::ActionWithdrawal; switch (d->m_schedule.type()) { case eMyMoney::Schedule::Type::Transfer: action = KMyMoneyRegister::ActionTransfer; break; case eMyMoney::Schedule::Type::Deposit: action = KMyMoneyRegister::ActionDeposit; break; case eMyMoney::Schedule::Type::LoanPayment: switch (d->m_schedule.paymentType()) { case eMyMoney::Schedule::PaymentType::DirectDeposit: case eMyMoney::Schedule::PaymentType::ManualDeposit: action = KMyMoneyRegister::ActionDeposit; break; default: break; } break; default: break; } editor->setup(d->m_tabOrderWidgets, d->m_schedule.account(), action); MyMoneyTransaction t = d->m_schedule.transaction(); QString num = t.splits().first().number(); QWidget* w = editor->haveWidget("number"); if (d->m_schedule.paymentType() == eMyMoney::Schedule::PaymentType::WriteChecque) { auto file = MyMoneyFile::instance(); if (file->checkNoUsed(d->m_schedule.account().id(), num)) { // increment and try again num = KMyMoneyUtils::getAdjacentNumber(num); } num = KMyMoneyUtils::nextCheckNumber(d->m_schedule.account()); KMyMoneyUtils::updateLastNumberUsed(d->m_schedule.account(), num); d->m_schedule.account().setValue("lastNumberUsed", num); if (w) { dynamic_cast(w)->loadText(num); } } else { // if it's not a check, then we need to clear // a possibly assigned check number if (w) dynamic_cast(w)->loadText(QString()); } Q_ASSERT(!d->m_tabOrderWidgets.isEmpty()); // editor->setup() leaves the tabbar as the last widget in the stack, but we // need it as first here. So we move it around. w = editor->haveWidget("tabbar"); if (w) { int idx = d->m_tabOrderWidgets.indexOf(w); if (idx != -1) { d->m_tabOrderWidgets.removeAt(idx); d->m_tabOrderWidgets.push_front(w); } } // don't forget our three buttons d->m_tabOrderWidgets.append(d->ui->buttonOk); d->m_tabOrderWidgets.append(d->ui->buttonCancel); d->m_tabOrderWidgets.append(d->ui->buttonHelp); for (auto i = 0; i < d->m_tabOrderWidgets.size(); ++i) { QWidget* w = d->m_tabOrderWidgets.at(i); if (w) { w->installEventFilter(this); w->installEventFilter(editor); } } // Check if the editor has some preference on where to set the focus // If not, set the focus to the first widget in the tab order QWidget* focusWidget = editor->firstWidget(); if (!focusWidget) focusWidget = d->m_tabOrderWidgets.first(); focusWidget->setFocus(); // Make sure, we use the adjusted date kMyMoneyDateInput* dateEdit = dynamic_cast(editor->haveWidget("postdate")); if (dateEdit) { dateEdit->setDate(d->m_schedule.adjustedNextDueDate()); } } return editor; } bool KEnterScheduleDlg::focusNextPrevChild(bool next) { Q_D(KEnterScheduleDlg); auto rc = false; auto w = qApp->focusWidget(); int currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w); while (w && currentWidgetIndex == -1) { // qDebug("'%s' not in list, use parent", w->className()); w = w->parentWidget(); currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w); } if (currentWidgetIndex != -1) { // if(w) qDebug("tab order is at '%s'", w->className()); currentWidgetIndex += next ? 1 : -1; if (currentWidgetIndex < 0) currentWidgetIndex = d->m_tabOrderWidgets.size() - 1; else if (currentWidgetIndex >= d->m_tabOrderWidgets.size()) currentWidgetIndex = 0; w = d->m_tabOrderWidgets[currentWidgetIndex]; // qDebug("currentWidgetIndex = %d, w = %p", currentWidgetIndex, w); if (((w->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) && w->isVisible() && w->isEnabled()) { // qDebug("Selecting '%s' as focus", w->className()); w->setFocus(); rc = true; } } return rc; } void KEnterScheduleDlg::slotShowHelp() { KHelpClient::invokeHelp("details.schedules.entering"); } diff --git a/kmymoney/dialogs/kfindtransactiondlg_p.h b/kmymoney/dialogs/kfindtransactiondlg_p.h index 6e0ebf6f8..30e9ef7a8 100644 --- a/kmymoney/dialogs/kfindtransactiondlg_p.h +++ b/kmymoney/dialogs/kfindtransactiondlg_p.h @@ -1,631 +1,627 @@ /*************************************************************************** kfindtransactiondlg.cpp ------------------- copyright : (C) 2003, 2007 by Thomas Baumgart email : ipwizard@users.sourceforge.net (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * q program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KFINDTRANSACTIONDLG_P_H #define KFINDTRANSACTIONDLG_P_H #include "kfindtransactiondlg.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyedit.h" -#include "mymoneyaccount.h" #include "mymoneyfile.h" #include "mymoneypayee.h" #include "mymoneytag.h" #include "kmymoneyglobalsettings.h" #include "register.h" #include "transaction.h" #include "daterangedlg.h" -#include "mymoneysplit.h" -#include "mymoneytransaction.h" -#include "mymoneytransactionfilter.h" #include "ui_kfindtransactiondlg.h" class KFindTransactionDlgPrivate { Q_DISABLE_COPY(KFindTransactionDlgPrivate) Q_DECLARE_PUBLIC(KFindTransactionDlg) public: enum opTypeE { addAccountToFilter = 0, addCategoryToFilter, addPayeeToFilter, addTagToFilter }; KFindTransactionDlgPrivate(KFindTransactionDlg *qq) : q_ptr(qq), ui(new Ui::KFindTransactionDlg) { } ~KFindTransactionDlgPrivate() { delete ui; } void init(bool withEquityAccounts) { Q_Q(KFindTransactionDlg); m_needReload = false; ui->setupUi(q); m_dateRange = new DateRangeDlg; ui->dateRangeLayout->insertWidget(0, m_dateRange); ui->ButtonGroup1->setId(ui->m_amountButton, 0); ui->ButtonGroup1->setId(ui->m_amountRangeButton, 1); ui->m_register->installEventFilter(q); ui->m_tabWidget->setTabEnabled(ui->m_tabWidget->indexOf(ui->m_resultPage), false); // 'cause we don't have a separate setupTextPage q->connect(ui->m_textEdit, &QLineEdit::textChanged, q, &KFindTransactionDlg::slotUpdateSelections); // if return is pressed trigger a search (slotSearch checks if it's possible to perform the search) q->connect(ui->m_textEdit, &KLineEdit::returnPressed, q, &KFindTransactionDlg::slotSearch); // in case the date selection changes, we update the selection q->connect(m_dateRange, &DateRangeDlg::rangeChanged, q, &KFindTransactionDlg::slotUpdateSelections); setupAccountsPage(withEquityAccounts); setupCategoriesPage(); setupAmountPage(); setupPayeesPage(); setupTagsPage(); setupDetailsPage(); // We don't need to add the default into the list (see ::slotShowHelp() why) // m_helpAnchor[m_ui->m_textTab] = QLatin1String("details.search"); m_helpAnchor[ui->m_accountTab] = QLatin1String("details.search.account"); m_helpAnchor[ui->m_dateTab] = QLatin1String("details.search.date"); m_helpAnchor[ui->m_amountTab] = QLatin1String("details.search.amount"); m_helpAnchor[ui->m_categoryTab] = QLatin1String("details.search.category"); m_helpAnchor[ui->m_payeeTab] = QLatin1String("details.search.payee"); m_helpAnchor[ui->m_tagTab] = QLatin1String("details.search.tag"); //FIXME-ALEX update Help m_helpAnchor[ui->m_detailsTab] = QLatin1String("details.search.details"); // setup the register QList cols { KMyMoneyRegister::DateColumn, KMyMoneyRegister::AccountColumn, KMyMoneyRegister::DetailColumn, KMyMoneyRegister::ReconcileFlagColumn, KMyMoneyRegister::PaymentColumn, KMyMoneyRegister::DepositColumn}; ui->m_register->setupRegister(MyMoneyAccount(), cols); ui->m_register->setSelectionMode(QTableWidget::SingleSelection); q->connect(ui->m_register, &KMyMoneyRegister::Register::editTransaction, q, &KFindTransactionDlg::slotSelectTransaction); q->connect(ui->m_register->horizontalHeader(), &QWidget::customContextMenuRequested, q, &KFindTransactionDlg::slotSortOptions); q->slotUpdateSelections(); // setup the connections q->connect(ui->buttonBox->button(QDialogButtonBox::Apply), &QAbstractButton::clicked, q, &KFindTransactionDlg::slotSearch); q->connect(ui->buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, q, &KFindTransactionDlg::slotReset); q->connect(ui->buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, ui->m_accountsView, &kMyMoneyAccountSelector::slotSelectAllAccounts); q->connect(ui->buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, ui->m_categoriesView, &kMyMoneyAccountSelector::slotSelectAllAccounts); q->connect(ui->buttonBox->button(QDialogButtonBox::Close), &QAbstractButton::clicked, q, &QObject::deleteLater); q->connect(ui->buttonBox->button(QDialogButtonBox::Help), &QAbstractButton::clicked, q, &KFindTransactionDlg::slotShowHelp); // only allow searches when a selection has been made ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); KGuiItem::assign(ui->buttonBox->button(QDialogButtonBox::Apply), KStandardGuiItem::find()); ui->buttonBox->button(QDialogButtonBox::Apply)->setToolTip(i18nc("@info:tooltip for find transaction apply button", "Search transactions")); q->connect(q, &KFindTransactionDlg::selectionNotEmpty, ui->buttonBox->button(QDialogButtonBox::Apply), &QWidget::setEnabled); // get signal about engine changes q->connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KFindTransactionDlg::slotRefreshView); q->slotUpdateSelections(); ui->m_textEdit->setFocus(); } /** * q method returns information about the selection state * of the items in the m_accountsView. * * @param view pointer to the listview to scan * * @retval true if all items in the view are marked * @retval false if at least one item is not marked * * @note If the view contains no items the method returns @p true. */ bool allItemsSelected(const QTreeWidgetItem *item) const { QTreeWidgetItem* it_v; for (auto i = 0; i < item->childCount(); ++i) { it_v = item->child(i); if (!(it_v->checkState(0) == Qt::Checked && allItemsSelected(it_v))) { return false; } } return true; } bool allItemsSelected(const QTreeWidget* view) const { QTreeWidgetItem* it_v; for (int i = 0; i < view->invisibleRootItem()->childCount(); ++i) { it_v = view->invisibleRootItem()->child(i); if (it_v->flags() & Qt::ItemIsUserCheckable) { if (!(it_v->checkState(0) == Qt::Checked && allItemsSelected(it_v))) { return false; } else { if (!allItemsSelected(it_v)) return false; } } } return true; } void addItemToFilter(const opTypeE op, const QString& id) { switch (op) { case addAccountToFilter: m_filter.addAccount(id); break; case addCategoryToFilter: m_filter.addCategory(id); break; case addPayeeToFilter: m_filter.addPayee(id); break; case addTagToFilter: m_filter.addTag(id); break; } } void scanCheckListItems(const QTreeWidgetItem* item, const opTypeE op) { QTreeWidgetItem* it_v; for (auto i = 0; i < item->childCount(); ++i) { it_v = item->child(i); QVariant idData = it_v->data(0, Qt::UserRole); if (it_v->flags() & Qt::ItemIsUserCheckable) { if (it_v->checkState(0) == Qt::Checked) addItemToFilter(op, idData.toString()); } scanCheckListItems(it_v, op); } } void scanCheckListItems(const QTreeWidget* view, const opTypeE op) { QTreeWidgetItem* it_v; for (auto i = 0; i < view->invisibleRootItem()->childCount(); ++i) { it_v = view->invisibleRootItem()->child(i); QVariant idData = it_v->data(0, Qt::UserRole); if (it_v->flags() & Qt::ItemIsUserCheckable) { if (it_v->checkState(0) == Qt::Checked) { addItemToFilter(op, idData.toString()); } } scanCheckListItems(it_v, op); } } void selectAllItems(QTreeWidget* view, const bool state) { QTreeWidgetItem* it_v; for (int i = 0; i < view->invisibleRootItem()->childCount(); ++i) { it_v = view->invisibleRootItem()->child(i); if (it_v->flags() & Qt::ItemIsUserCheckable) { it_v->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); } selectAllSubItems(it_v, state); } Q_Q(KFindTransactionDlg); q->slotUpdateSelections(); } void selectItems(QTreeWidget* view, const QStringList& list, const bool state) { QTreeWidgetItem* it_v; for (int i = 0; i < view->invisibleRootItem()->childCount(); ++i) { it_v = view->invisibleRootItem()->child(i); QVariant idData = it_v->data(0, Qt::UserRole); if (it_v->flags() & Qt::ItemIsUserCheckable && list.contains(idData.toString())) { it_v->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); } selectSubItems(it_v, list, state); } Q_Q(KFindTransactionDlg); q->slotUpdateSelections(); } void selectAllSubItems(QTreeWidgetItem* item, const bool state) { QTreeWidgetItem* it_v; for (int i = 0; i < item->childCount(); ++i) { it_v = item->child(i); it_v->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); selectAllSubItems(it_v, state); } } void selectSubItems(QTreeWidgetItem* item, const QStringList& list, const bool state) { QTreeWidgetItem* it_v; for (int i = 0; i < item->childCount(); ++i) { it_v = item->child(i); QVariant idData = it_v->data(0, Qt::UserRole); if (list.contains(idData.toString())) it_v->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); selectSubItems(it_v, list, state); } } /** * q method loads the register with the matching transactions */ void loadView() { // setup sort order ui->m_register->setSortOrder(KMyMoneyGlobalSettings::sortSearchView()); // clear out old data ui->m_register->clear(); // retrieve the list from the engine MyMoneyFile::instance()->transactionList(m_transactionList, m_filter); // create the elements for the register QList >::const_iterator it; QMapuniqueMap; MyMoneyMoney deposit, payment; int splitCount = 0; for (it = m_transactionList.constBegin(); it != m_transactionList.constEnd(); ++it) { const MyMoneySplit& split = (*it).second; MyMoneyAccount acc = MyMoneyFile::instance()->account(split.accountId()); ++splitCount; uniqueMap[(*it).first.id()]++; KMyMoneyRegister::Register::transactionFactory(ui->m_register, (*it).first, (*it).second, uniqueMap[(*it).first.id()]); { // debug stuff if (split.shares().isNegative()) { payment += split.shares().abs(); } else { deposit += split.shares().abs(); } } } // add the group markers ui->m_register->addGroupMarkers(); // sort the transactions according to the sort setting ui->m_register->sortItems(); // remove trailing and adjacent markers ui->m_register->removeUnwantedGroupMarkers(); // turn on the ledger lens for the register ui->m_register->setLedgerLensForced(); ui->m_register->updateRegister(true); ui->m_register->setFocusToTop(); ui->m_register->selectItem(ui->m_register->focusItem()); #ifdef KMM_DEBUG ui->m_foundText->setText(i18np("Found %1 matching transaction (D %2 / P %3 = %4)", "Found %1 matching transactions (D %2 / P %3 = %4)", splitCount, deposit.formatMoney("", 2), payment.formatMoney("", 2), (deposit - payment).formatMoney("", 2))); #else ui->m_foundText->setText(i18np("Found %1 matching transaction", "Found %1 matching transactions", splitCount)); #endif ui->m_tabWidget->setTabEnabled(ui->m_tabWidget->indexOf(ui->m_resultPage), true); ui->m_tabWidget->setCurrentIndex(ui->m_tabWidget->indexOf(ui->m_resultPage)); Q_Q(KFindTransactionDlg); QTimer::singleShot(10, q, SLOT(slotRightSize())); } /** * q method loads the m_tagsView with the tags name * found in the engine. */ void loadTags() { MyMoneyFile* file = MyMoneyFile::instance(); QList list; QList::Iterator it_l; list = file->tagList(); // load view for (it_l = list.begin(); it_l != list.end(); ++it_l) { auto item = new QTreeWidgetItem(ui->m_tagsView); item->setText(0, (*it_l).name()); item->setData(0, Qt::UserRole, (*it_l).id()); item->setCheckState(0, Qt::Checked); } } /** * q method loads the m_payeesView with the payees name * found in the engine. */ void loadPayees() { MyMoneyFile* file = MyMoneyFile::instance(); QList list; QList::Iterator it_l; list = file->payeeList(); // load view for (it_l = list.begin(); it_l != list.end(); ++it_l) { auto item = new QTreeWidgetItem(ui->m_payeesView); item->setText(0, (*it_l).name()); item->setData(0, Qt::UserRole, (*it_l).id()); item->setCheckState(0, Qt::Checked); } } void setupFilter() { m_filter.clear(); // Text tab if (!ui->m_textEdit->text().isEmpty()) { QRegExp exp(ui->m_textEdit->text(), ui->m_caseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive, !ui->m_regExp->isChecked() ? QRegExp::Wildcard : QRegExp::RegExp); m_filter.setTextFilter(exp, ui->m_textNegate->currentIndex() != 0); } // Account tab if (!ui->m_accountsView->allItemsSelected()) { // retrieve a list of selected accounts QStringList list; ui->m_accountsView->selectedItems(list); // if we're not in expert mode, we need to make sure // that all stock accounts for the selected investment // account are also selected if (!KMyMoneyGlobalSettings::expertMode()) { QStringList missing; QStringList::const_iterator it_a, it_b; for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) { MyMoneyAccount acc = MyMoneyFile::instance()->account(*it_a); if (acc.accountType() == eMyMoney::Account::Investment) { for (it_b = acc.accountList().constBegin(); it_b != acc.accountList().constEnd(); ++it_b) { if (!list.contains(*it_b)) { missing.append(*it_b); } } } } list += missing; } m_filter.addAccount(list); } // Date tab if ((int)m_dateRange->dateRange() != 0) { m_filter.setDateFilter(m_dateRange->fromDate(), m_dateRange->toDate()); } // Amount tab if ((ui->m_amountButton->isChecked() && ui->m_amountEdit->isValid())) { m_filter.setAmountFilter(ui->m_amountEdit->value(), ui->m_amountEdit->value()); } else if ((ui->m_amountRangeButton->isChecked() && (ui->m_amountFromEdit->isValid() || ui->m_amountToEdit->isValid()))) { MyMoneyMoney from(MyMoneyMoney::minValue), to(MyMoneyMoney::maxValue); if (ui->m_amountFromEdit->isValid()) from = ui->m_amountFromEdit->value(); if (ui->m_amountToEdit->isValid()) to = ui->m_amountToEdit->value(); m_filter.setAmountFilter(from, to); } // Categories tab if (!ui->m_categoriesView->allItemsSelected()) { m_filter.addCategory(ui->m_categoriesView->selectedItems()); } // Tags tab if (ui->m_emptyTagsButton->isChecked()) { m_filter.addTag(QString()); } else if (!allItemsSelected(ui->m_tagsView)) { scanCheckListItems(ui->m_tagsView, addTagToFilter); } // Payees tab if (ui->m_emptyPayeesButton->isChecked()) { m_filter.addPayee(QString()); } else if (!allItemsSelected(ui->m_payeesView)) { scanCheckListItems(ui->m_payeesView, addPayeeToFilter); } // Details tab if (ui->m_typeBox->currentIndex() != 0) m_filter.addType(ui->m_typeBox->currentIndex()); if (ui->m_stateBox->currentIndex() != 0) m_filter.addState(ui->m_stateBox->currentIndex()); if (ui->m_validityBox->currentIndex() != 0) m_filter.addValidity(ui->m_validityBox->currentIndex()); if (ui->m_nrButton->isChecked() && !ui->m_nrEdit->text().isEmpty()) m_filter.setNumberFilter(ui->m_nrEdit->text(), ui->m_nrEdit->text()); if (ui->m_nrRangeButton->isChecked() && (!ui->m_nrFromEdit->text().isEmpty() || !ui->m_nrToEdit->text().isEmpty())) { m_filter.setNumberFilter(ui->m_nrFromEdit->text(), ui->m_nrToEdit->text()); } } void setupDetailsPage() { Q_Q(KFindTransactionDlg); q->connect(ui->m_typeBox, static_cast(&QComboBox::activated), q, &KFindTransactionDlg::slotUpdateSelections); q->connect(ui->m_stateBox, static_cast(&QComboBox::activated), q, &KFindTransactionDlg::slotUpdateSelections); q->connect(ui->m_validityBox, static_cast(&QComboBox::activated), q, &KFindTransactionDlg::slotUpdateSelections); q->connect(ui->m_nrButton, &QAbstractButton::clicked, q, &KFindTransactionDlg::slotNrSelected); q->connect(ui->m_nrRangeButton, &QAbstractButton::clicked, q, &KFindTransactionDlg::slotNrRangeSelected); q->connect(ui->m_nrEdit, &QLineEdit::textChanged, q, &KFindTransactionDlg::slotUpdateSelections); q->connect(ui->m_nrFromEdit, &QLineEdit::textChanged, q, &KFindTransactionDlg::slotUpdateSelections); q->connect(ui->m_nrToEdit, &QLineEdit::textChanged, q, &KFindTransactionDlg::slotUpdateSelections); ui->m_nrButton->setChecked(true); q->slotNrSelected(); } void setupTagsPage() { Q_Q(KFindTransactionDlg); ui->m_tagsView->setSelectionMode(QAbstractItemView::SingleSelection); ui->m_tagsView->header()->hide(); ui->m_tagsView->setAlternatingRowColors(true); loadTags(); ui->m_tagsView->sortItems(0, Qt::AscendingOrder); ui->m_emptyTagsButton->setCheckState(Qt::Unchecked); q->connect(ui->m_allTagsButton, &QAbstractButton::clicked, q, &KFindTransactionDlg::slotSelectAllTags); q->connect(ui->m_clearTagsButton, &QAbstractButton::clicked, q, &KFindTransactionDlg::slotDeselectAllTags); q->connect(ui->m_emptyTagsButton, &QCheckBox::stateChanged, q, &KFindTransactionDlg::slotUpdateSelections); q->connect(ui->m_tagsView, &QTreeWidget::itemChanged, q, &KFindTransactionDlg::slotUpdateSelections); } void setupPayeesPage() { Q_Q(KFindTransactionDlg); ui->m_payeesView->setSelectionMode(QAbstractItemView::SingleSelection); ui->m_payeesView->header()->hide(); ui->m_payeesView->setAlternatingRowColors(true); loadPayees(); ui->m_payeesView->sortItems(0, Qt::AscendingOrder); ui->m_emptyPayeesButton->setCheckState(Qt::Unchecked); q->connect(ui->m_allPayeesButton, &QAbstractButton::clicked, q, &KFindTransactionDlg::slotSelectAllPayees); q->connect(ui->m_clearPayeesButton, &QAbstractButton::clicked, q, &KFindTransactionDlg::slotDeselectAllPayees); q->connect(ui->m_emptyPayeesButton, &QCheckBox::stateChanged, q, &KFindTransactionDlg::slotUpdateSelections); q->connect(ui->m_payeesView, &QTreeWidget::itemChanged, q, &KFindTransactionDlg::slotUpdateSelections); } void setupAmountPage() { Q_Q(KFindTransactionDlg); q->connect(ui->m_amountButton, &QAbstractButton::clicked, q, &KFindTransactionDlg::slotAmountSelected); q->connect(ui->m_amountRangeButton, &QAbstractButton::clicked, q, &KFindTransactionDlg::slotAmountRangeSelected); q->connect(ui->m_amountEdit, &kMyMoneyEdit::textChanged, q, &KFindTransactionDlg::slotUpdateSelections); q->connect(ui->m_amountFromEdit, &kMyMoneyEdit::textChanged, q, &KFindTransactionDlg::slotUpdateSelections); q->connect(ui->m_amountToEdit, &kMyMoneyEdit::textChanged, q, &KFindTransactionDlg::slotUpdateSelections); ui->m_amountButton->setChecked(true); q->slotAmountSelected(); } void setupCategoriesPage() { Q_Q(KFindTransactionDlg); ui->m_categoriesView->setSelectionMode(QTreeWidget::MultiSelection); AccountSet categorySet; categorySet.addAccountGroup(eMyMoney::Account::Income); categorySet.addAccountGroup(eMyMoney::Account::Expense); categorySet.load(ui->m_categoriesView); q->connect(ui->m_categoriesView, &kMyMoneyAccountSelector::stateChanged, q, &KFindTransactionDlg::slotUpdateSelections); } void setupAccountsPage(bool withEquityAccounts) { Q_Q(KFindTransactionDlg); ui->m_accountsView->setSelectionMode(QTreeWidget::MultiSelection); AccountSet accountSet; accountSet.addAccountGroup(eMyMoney::Account::Asset); accountSet.addAccountGroup(eMyMoney::Account::Liability); if (withEquityAccounts) accountSet.addAccountGroup(eMyMoney::Account::Equity); //set the accountset to show closed account if the settings say so accountSet.setHideClosedAccounts(KMyMoneyGlobalSettings::hideClosedAccounts()); accountSet.load(ui->m_accountsView); q->connect(ui->m_accountsView, &kMyMoneyAccountSelector::stateChanged, q, &KFindTransactionDlg::slotUpdateSelections); } KFindTransactionDlg *q_ptr; Ui::KFindTransactionDlg *ui; QDate m_startDates[(int)eMyMoney::TransactionFilter::Date::LastDateItem]; QDate m_endDates[(int)eMyMoney::TransactionFilter::Date::LastDateItem]; /** * q member holds a list of all transactions matching the filter criteria */ QList > m_transactionList; MyMoneyTransactionFilter m_filter; QMap m_helpAnchor; bool m_needReload; DateRangeDlg *m_dateRange; }; #endif diff --git a/kmymoney/dialogs/knewaccountdlg.ui b/kmymoney/dialogs/knewaccountdlg.ui index e5e2828f8..98648e07b 100644 --- a/kmymoney/dialogs/knewaccountdlg.ui +++ b/kmymoney/dialogs/knewaccountdlg.ui @@ -1,906 +1,906 @@ KNewAccountDlg 0 0 670 518 New Account Dialog true 0 General 60 0 60 32767 Name: false Opening information 6 6 6 6 6 Currency: false true 1 0 Type: false 0 0 Date: false Balance: false Options Qt::Horizontal QSizePolicy::Expanding 140 20 Last check number: false 0 0 32767 32767 Notes: Qt::AlignTop false No auto-VAT-assignment Price entry false false 1 0 Preferred Account Cost Center assignment required true <html><head/><body><p>Flag this account to be an opening balance account</p></body></html> Opening Balance Account Qt::Vertical QSizePolicy::Expanding 20 16 Institution Institution 6 0 0 0 0 0 0 New... 60 0 60 32767 Number: false BIC false IBAN false false Qt::Vertical QSizePolicy::Expanding 21 30 Hierarchy Subaccount false Limits This page allows to setup certain limits. KMyMoney will warn you when the account balance reaches a limit. true Type false false Absolute limit false Maximum credit limit false false false false Minimum balance false QFrame::HLine QFrame::Sunken Early warning false Qt::Vertical QSizePolicy::Expanding 20 30 Tax VAT details VAT category QFrame::NoFrame QFrame::Plain 0 0 0 0 VAT percentage false Qt::Horizontal QSizePolicy::Expanding 21 20 Qt::Vertical QSizePolicy::Expanding 20 40 QFrame::NoFrame QFrame::Plain 0 0 0 0 Enable auto VAT assignment 0 0 32767 32767 Amount entered QFrame::StyledPanel QFrame::Sunken Gross amount true m_amountGroup Net a&mount m_amountGroup Qt::Vertical QSizePolicy::Expanding 20 21 Include on Tax Reports QFrame::HLine QFrame::Sunken QDialogButtonBox::Cancel|QDialogButtonBox::Ok KLineEdit QLineEdit
klineedit.h
KComboBox QComboBox
kcombobox.h
KTextEdit QTextEdit
ktextedit.h
kMyMoneyEdit QWidget
../widgets/kmymoneyedit.h
1
KMyMoneyGeneralCombo QWidget -
kmymoneygeneralcombo.h
+
kmymoneymvccombo.h
1
kMyMoneyDateInput QWidget
kmymoneydateinput.h
1
KMyMoneySecuritySelector KComboBox
kmymoneycurrencyselector.h
1
KMyMoneyAccountTreeView QTreeView
kmymoneyaccounttreeview.h
kMyMoneyAccountSelector QWidget
../widgets/kmymoneyaccountselector.h
m_tab accountNameEdit typeCombo m_currency m_openingDateEdit m_openingBalanceEdit m_lastCheckNumberUsed descriptionEdit m_qcheckboxPreferred m_qcheckboxNoVat m_qcomboboxInstitutions m_qbuttonNew ibanEdit accountNoEdit m_minBalanceEarlyEdit m_minBalanceAbsoluteEdit m_maxCreditEarlyEdit m_maxCreditAbsoluteEdit m_vatCategory m_vatRate m_vatAssignment m_grossAmount m_qcheckboxTax
diff --git a/kmymoney/dialogs/kpayeereassigndlg.ui b/kmymoney/dialogs/kpayeereassigndlg.ui index 1c7888e9d..6924cb0cd 100644 --- a/kmymoney/dialogs/kpayeereassigndlg.ui +++ b/kmymoney/dialogs/kpayeereassigndlg.ui @@ -1,157 +1,157 @@ KPayeeReassignDlg 0 0 558 312 Reassign payees false true 300 0 Qt::AlignJustify|Qt::AlignTop true Qt::Vertical QSizePolicy::Fixed 20 16 Available payees: false Qt::Vertical QSizePolicy::Expanding 20 20 Assign deleted names to the above selected payee's matching list true QFrame::HLine QFrame::Sunken QDialogButtonBox::Cancel|QDialogButtonBox::Ok KMyMoneyPayeeCombo QWidget -
kmymoneypayeecombo.h
+
kmymoneymvccombo.h
1
buttonBox accepted() KPayeeReassignDlg accept() 239 283 117 309 buttonBox rejected() KPayeeReassignDlg reject() 456 292 418 308
diff --git a/kmymoney/dialogs/kselecttransactionsdlg.cpp b/kmymoney/dialogs/kselecttransactionsdlg.cpp index 409ee347d..ac94e5df0 100644 --- a/kmymoney/dialogs/kselecttransactionsdlg.cpp +++ b/kmymoney/dialogs/kselecttransactionsdlg.cpp @@ -1,203 +1,202 @@ /*************************************************************************** kselecttransactionsdlg.cpp ------------------- begin : Wed May 16 2007 copyright : (C) 2007 by Thomas Baumgart email : ipwizard@users.sourceforge.net (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kselecttransactionsdlg.h" #include "kselecttransactionsdlg_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_kselecttransactionsdlg.h" #include "mymoneyaccount.h" -#include "selectedtransactions.h" -#include "mymoneysplit.h" +#include "selectedtransaction.h" #include "mymoneytransaction.h" #include "kmymoneyglobalsettings.h" KSelectTransactionsDlg::KSelectTransactionsDlg(const MyMoneyAccount& _account, QWidget* parent) : QDialog(parent), d_ptr(new KSelectTransactionsDlgPrivate) { Q_D(KSelectTransactionsDlg); d->m_account = _account; d->ui->setupUi(this); // setup descriptive texts setWindowTitle(i18n("Select Transaction")); d->ui->m_description->setText(i18n("Select a transaction and press the OK button or use Cancel to select none.")); // clear current register contents d->ui->m_register->clear(); // no selection possible d->ui->m_register->setSelectionMode(QTableWidget::SingleSelection); // setup header font auto font = KMyMoneyGlobalSettings::listHeaderFont(); QFontMetrics fm(font); auto height = fm.lineSpacing() + 6; d->ui->m_register->horizontalHeader()->setMinimumHeight(height); d->ui->m_register->horizontalHeader()->setMaximumHeight(height); d->ui->m_register->horizontalHeader()->setFont(font); // setup cell font font = KMyMoneyGlobalSettings::listCellFont(); d->ui->m_register->setFont(font); // ... setup the register columns ... d->ui->m_register->setupRegister(d->m_account); // setup buttons // default is to need at least one transaction selected d->ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true); // catch some events from the register d->ui->m_register->installEventFilter(this); connect(d->ui->m_register, &KMyMoneyRegister::Register::transactionsSelected, this, &KSelectTransactionsDlg::slotEnableOk); connect(d->ui->m_register, &KMyMoneyRegister::Register::editTransaction, this, &QDialog::accept); connect(d->ui->buttonBox, &QDialogButtonBox::helpRequested, this, &KSelectTransactionsDlg::slotHelp); } KSelectTransactionsDlg::~KSelectTransactionsDlg() { Q_D(KSelectTransactionsDlg); delete d; } void KSelectTransactionsDlg::slotEnableOk(const KMyMoneyRegister::SelectedTransactions& list) { Q_D(KSelectTransactionsDlg); d->ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(list.count() != 0); } void KSelectTransactionsDlg::addTransaction(const MyMoneyTransaction& t) { Q_D(KSelectTransactionsDlg); QList::const_iterator it_s; for (it_s = t.splits().begin(); it_s != t.splits().end(); ++it_s) { if ((*it_s).accountId() == d->m_account.id()) { KMyMoneyRegister::Transaction* tr = KMyMoneyRegister::Register::transactionFactory(d->ui->m_register, t, (*it_s), 0); // force full detail display tr->setNumRowsRegister(tr->numRowsRegister(true)); break; } } } int KSelectTransactionsDlg::exec() { Q_D(KSelectTransactionsDlg); d->ui->m_register->updateRegister(true); d->ui->m_register->update(); d->ui->m_register->setFocus(); return QDialog::exec(); } void KSelectTransactionsDlg::slotHelp() { // KHelpClient::invokeHelp("details.ledgers.match"); } void KSelectTransactionsDlg::showEvent(QShowEvent* event) { Q_D(KSelectTransactionsDlg); QDialog::showEvent(event); d->ui->m_register->resize(KMyMoneyRegister::DetailColumn, true); } void KSelectTransactionsDlg::resizeEvent(QResizeEvent* ev) { Q_D(KSelectTransactionsDlg); // don't forget the resizer QDialog::resizeEvent(ev); // resize the register d->ui->m_register->resize(KMyMoneyRegister::DetailColumn, true); } MyMoneyTransaction KSelectTransactionsDlg::transaction() const { Q_D(const KSelectTransactionsDlg); MyMoneyTransaction t; QList list; list = d->ui->m_register->selectedItems(); if (list.count()) { KMyMoneyRegister::Transaction* _t = dynamic_cast(list[0]); if (_t) t = _t->transaction(); } return t; } KMyMoneyRegister::Register* KSelectTransactionsDlg::getRegister() { Q_D(KSelectTransactionsDlg); return d->ui->m_register; } bool KSelectTransactionsDlg::eventFilter(QObject* o, QEvent* e) { Q_D(KSelectTransactionsDlg); auto rc = false; QKeyEvent* k; if (o == d->ui->m_register) { switch (e->type()) { case QEvent::KeyPress: k = dynamic_cast(e); if ((k->modifiers() & Qt::KeyboardModifierMask) == 0 || (k->modifiers() & Qt::KeypadModifier) != 0) { switch (k->key()) { case Qt::Key_Return: case Qt::Key_Enter: if (d->ui->buttonBox->button(QDialogButtonBox::Ok)->isEnabled()) { accept(); rc = true; } // tricky fall through here default: break; } } // tricky fall through here default: break; } } return rc; } diff --git a/kmymoney/dialogs/ksortoptiondlg.ui b/kmymoney/dialogs/ksortoptiondlg.ui index a1220554d..8db113738 100644 --- a/kmymoney/dialogs/ksortoptiondlg.ui +++ b/kmymoney/dialogs/ksortoptiondlg.ui @@ -1,98 +1,98 @@ KSortOptionDlg 0 0 511 348 Sort options true Use default QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok TransactionSortOption QWidget -
transactionsortoption.h
+
transactionsortoptionimpl.h
m_useDefault toggled(bool) m_sortOption setDisabled(bool) 20 20 29 55 m_buttonBox accepted() KSortOptionDlg accept() 116 327 78 344 m_buttonBox rejected() KSortOptionDlg reject() 345 326 274 345
diff --git a/kmymoney/dialogs/ktagreassigndlg.ui b/kmymoney/dialogs/ktagreassigndlg.ui index 89d0a6917..8d25aeeb9 100644 --- a/kmymoney/dialogs/ktagreassigndlg.ui +++ b/kmymoney/dialogs/ktagreassigndlg.ui @@ -1,150 +1,150 @@ KTagReassignDlg 0 0 558 312 Reassign tags false true 300 0 The transactions associated with the selected tags need to be re-assigned to a different tag before the selected tags can be deleted. Please select a tag from the list below. Qt::AlignJustify|Qt::AlignTop true Qt::Vertical QSizePolicy::Fixed 20 16 Available tags: false Qt::Vertical QSizePolicy::Expanding 20 20 QFrame::HLine QFrame::Sunken QDialogButtonBox::Cancel|QDialogButtonBox::Ok KMyMoneyTagCombo QWidget -
kmymoneytagcombo.h
+
kmymoneymvccombo.h
1
buttonBox accepted() KTagReassignDlg accept() 313 286 76 308 buttonBox rejected() KTagReassignDlg reject() 462 291 392 308
diff --git a/kmymoney/dialogs/settings/ksettingsregister.ui b/kmymoney/dialogs/settings/ksettingsregister.ui index 8030f44ce..be5a9ad9f 100644 --- a/kmymoney/dialogs/settings/ksettingsregister.ui +++ b/kmymoney/dialogs/settings/ksettingsregister.ui @@ -1,506 +1,506 @@ KSettingsRegister 0 0 632 403 Register settings Display Show a grid in the register Show all register entries in full detail Use the ledger lens Using the ledger lens shows the details for the transaction that has focus in the ledger. Usually, when using the transaction form, only a one line summary is displayed for each transaction as the details are shown in the form. Show transaction form Always show a No. field Show group header between transactions Draws a larger header above each group of transactions. The grouping depends on the current sort order. Show header for the previous and current fiscal year 20 40 QSizePolicy::Expanding Qt::Vertical Sorting Normal view Reconciliation view Search view Use the <i>left</i> and <i>right</i> buttons to add and remove sort options. Use the <i>up</i> and <i>down</i> buttons to modify the sort order. Double-Click a selected entry to toggle the sort order between <i>ascending</i> and <i>descending</i>. true Data entry Insert transaction type into No. field for new transactions Auto increment check number Keep changes when selecting a different transaction/split Use Enter to move between fields Match names from start Mark this option, if you always want to match names e.g. for payees from the start. If unset, any substring is matched. Automatic reconciliation After entering the reconciliation data automatically detect the transactions that match that data (in some cases it might be not possible to do that). Default reconciliation state false Default reconciliation state for transactions entered during reconciliation of an account Not reconciled Cleared Reconciled 31 20 QSizePolicy::Expanding Qt::Horizontal Autofill -1 No Autofill Do not auto-fill transaction data at all. Same transaction if amount differs less than Collect all transactions for the given payee. Treat all transactions that refer to the same category and have an amount with +/- X % as identical. If more than one transaction is found, a list of them is presented to the user. Selecting 0% will list all transactions. 100 10 Two transactions are usually treated identical for autofill, if they refer the same accounts. They are treated as different transactions though, when their amount varies by more than the percentage given here. percent. false With previously most often used transaction for the payee The data of the last transaction assigned to the category used most often for this payee is autofilled into the transaction editor. Use memos from previous transaction If this option is checked the memos from the previous transaction will be used otherwise the memos will not be considered when the transaction is autofilled. 20 20 QSizePolicy::Expanding Qt::Vertical Import Match transactions within days false UpDownArrows 99 0 4 Search for matching transactions within the range of the posting date of the imported transaction +/- the number of given days. 61 20 QSizePolicy::Expanding Qt::Horizontal Ask for a new payee's default category Whenever a new payee is detected during import of a statement, the user will be asked to assign a default category for this user when this option is selected. 20 60 QSizePolicy::Expanding Qt::Vertical QSpinBox QSpinBox
knuminput.h
TransactionSortOption QWidget -
transactionsortoption.h
+
transactionsortoptionimpl.h
KComboBox QComboBox
kcombobox.h
QTabWidget QTabWidget
ktabwidget.h
diff --git a/kmymoney/dialogs/stdtransactioneditor.cpp b/kmymoney/dialogs/stdtransactioneditor.cpp index ddfe78625..eb25b4d4e 100644 --- a/kmymoney/dialogs/stdtransactioneditor.cpp +++ b/kmymoney/dialogs/stdtransactioneditor.cpp @@ -1,1620 +1,1614 @@ /*************************************************************************** stdtransactioneditor.cpp ---------- begin : Wed Jun 07 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "stdtransactioneditor.h" #include "transactioneditor_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes -#include "kmymoneyreconcilecombo.h" -#include "kmymoneycashflowcombo.h" -#include "kmymoneypayeecombo.h" -#include "kmymoneytagcombo.h" -#include "ktagcontainer.h" #include "kmymoneycategory.h" #include "kmymoneymvccombo.h" #include "kmymoneydateinput.h" #include "kmymoneyedit.h" #include "kmymoneylineedit.h" #include "kmymoneyaccountselector.h" #include "mymoneyfile.h" #include "mymoneypayee.h" #include "mymoneytag.h" #include "kmymoneyutils.h" #include "kmymoneycompletion.h" #include "transactionform.h" -#include "mymoneytransactionfilter.h" #include "kmymoneyglobalsettings.h" #include "transactioneditorcontainer.h" #include "ksplittransactiondlg.h" #include "kcurrencycalculator.h" #include "kselecttransactionsdlg.h" using namespace KMyMoneyRegister; using namespace KMyMoneyTransactionForm; class StdTransactionEditorPrivate : public TransactionEditorPrivate { Q_DISABLE_COPY(StdTransactionEditorPrivate) public: StdTransactionEditorPrivate(StdTransactionEditor *qq) : TransactionEditorPrivate(qq), m_inUpdateVat(false) { } ~StdTransactionEditorPrivate() { } MyMoneyMoney m_shares; bool m_inUpdateVat; }; StdTransactionEditor::StdTransactionEditor() : TransactionEditor(*new StdTransactionEditorPrivate(this)) { } StdTransactionEditor::StdTransactionEditor(TransactionEditorContainer* regForm, KMyMoneyRegister::Transaction* item, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) : TransactionEditor(*new StdTransactionEditorPrivate(this), regForm, item, list, lastPostDate) { } StdTransactionEditor::~StdTransactionEditor() { } void StdTransactionEditor::createEditWidgets() { Q_D(StdTransactionEditor); // we only create the account widget in case it is needed // to avoid confusion in the tab order later on. if (d->m_item->showRowInForm(0)) { auto account = new KMyMoneyCategory; account->setPlaceholderText(i18n("Account")); account->setObjectName(QLatin1String("Account")); d->m_editWidgets["account"] = account; connect(account, &QComboBox::editTextChanged, this, &StdTransactionEditor::slotUpdateButtonState); connect(account, &KMyMoneyCombo::itemSelected, this, &StdTransactionEditor::slotUpdateAccount); } auto payee = new KMyMoneyPayeeCombo; payee->setPlaceholderText(i18n("Payer/Receiver")); payee->setObjectName(QLatin1String("Payee")); d->m_editWidgets["payee"] = payee; connect(payee, &KMyMoneyMVCCombo::createItem, this, &TransactionEditor::createPayee); connect(payee, &KMyMoneyMVCCombo::objectCreation, this, &TransactionEditor::objectCreation); connect(payee, &KMyMoneyMVCCombo::itemSelected, this, &StdTransactionEditor::slotUpdatePayee); connect(payee, &QComboBox::editTextChanged, this, &StdTransactionEditor::slotUpdateButtonState); - auto category = new KMyMoneyCategory(true, nullptr); + auto category = new KMyMoneyCategory(0, true); category->setPlaceholderText(i18n("Category/Account")); category->setObjectName(QLatin1String("Category/Account")); d->m_editWidgets["category"] = category; connect(category, &KMyMoneyCombo::itemSelected, this, &StdTransactionEditor::slotUpdateCategory); connect(category, &QComboBox::editTextChanged, this, &StdTransactionEditor::slotUpdateButtonState); connect(category, &KMyMoneyCombo::createItem, this, &StdTransactionEditor::slotCreateCategory); connect(category, &KMyMoneyCombo::objectCreation, this, &TransactionEditor::objectCreation); connect(category->splitButton(), &QAbstractButton::clicked, this, &StdTransactionEditor::slotEditSplits); // initially disable the split button since we don't have an account set if (category->splitButton()) category->splitButton()->setDisabled(d->m_account.id().isEmpty()); auto tag = new KTagContainer; tag->tagCombo()->setPlaceholderText(i18n("Tag")); tag->tagCombo()->setObjectName(QLatin1String("Tag")); d->m_editWidgets["tag"] = tag; connect(tag->tagCombo(), &KMyMoneyMVCCombo::createItem, this, &TransactionEditor::createTag); connect(tag->tagCombo(), &KMyMoneyMVCCombo::objectCreation, this, &TransactionEditor::objectCreation); auto memo = new KTextEdit; memo->setObjectName(QLatin1String("Memo")); memo->setTabChangesFocus(true); connect(memo, &QTextEdit::textChanged, this, &StdTransactionEditor::slotUpdateMemoState); connect(memo, &QTextEdit::textChanged, this, &StdTransactionEditor::slotUpdateButtonState); d->m_editWidgets["memo"] = memo; d->m_memoText.clear(); d->m_memoChanged = false; bool showNumberField = true; switch (d->m_account.accountType()) { case eMyMoney::Account::Savings: case eMyMoney::Account::Cash: case eMyMoney::Account::Loan: case eMyMoney::Account::AssetLoan: case eMyMoney::Account::Asset: case eMyMoney::Account::Liability: case eMyMoney::Account::Equity: showNumberField = KMyMoneyGlobalSettings::alwaysShowNrField(); break; case eMyMoney::Account::Income: case eMyMoney::Account::Expense: showNumberField = false; break; default: break; } if (showNumberField) { auto number = new kMyMoneyLineEdit; number->setPlaceholderText(i18n("Number")); number->setObjectName(QLatin1String("Number")); d->m_editWidgets["number"] = number; connect(number, &kMyMoneyLineEdit::lineChanged, this, &StdTransactionEditor::slotNumberChanged); // number->installEventFilter(this); } auto postDate = new kMyMoneyDateInput; d->m_editWidgets["postdate"] = postDate; postDate->setObjectName(QLatin1String("PostDate")); connect(postDate, &kMyMoneyDateInput::dateChanged, this, &StdTransactionEditor::slotUpdateButtonState); postDate->setDate(QDate()); auto value = new kMyMoneyEdit; d->m_editWidgets["amount"] = value; value->setObjectName(QLatin1String("Amount")); value->setResetButtonVisible(false); connect(value, &kMyMoneyEdit::valueChanged, this, &StdTransactionEditor::slotUpdateAmount); connect(value, &kMyMoneyEdit::textChanged, this, &StdTransactionEditor::slotUpdateButtonState); value = new kMyMoneyEdit; d->m_editWidgets["payment"] = value; value->setObjectName(QLatin1String("Payment")); value->setResetButtonVisible(false); connect(value, &kMyMoneyEdit::valueChanged, this, &StdTransactionEditor::slotUpdatePayment); connect(value, &kMyMoneyEdit::textChanged, this, &StdTransactionEditor::slotUpdateButtonState); value = new kMyMoneyEdit; d->m_editWidgets["deposit"] = value; value->setObjectName(QLatin1String("Deposit")); value->setResetButtonVisible(false); connect(value, &kMyMoneyEdit::valueChanged, this, &StdTransactionEditor::slotUpdateDeposit); connect(value, &kMyMoneyEdit::textChanged, this, &StdTransactionEditor::slotUpdateButtonState); - auto cashflow = new KMyMoneyCashFlowCombo(d->m_account.accountGroup(), nullptr); + auto cashflow = new KMyMoneyCashFlowCombo(0, d->m_account.accountGroup()); d->m_editWidgets["cashflow"] = cashflow; cashflow->setObjectName(QLatin1String("Cashflow")); connect(cashflow, &KMyMoneyCashFlowCombo::directionSelected, this, &StdTransactionEditor::slotUpdateCashFlow); auto reconcile = new KMyMoneyReconcileCombo; d->m_editWidgets["status"] = reconcile; reconcile->setObjectName(QLatin1String("Reconcile")); KMyMoneyRegister::QWidgetContainer::iterator it_w; for (it_w = d->m_editWidgets.begin(); it_w != d->m_editWidgets.end(); ++it_w) { (*it_w)->installEventFilter(this); } // if we don't have more than 1 selected transaction, we don't need // the "don't change" item in some of the combo widgets if (!isMultiSelection()) { reconcile->removeDontCare(); cashflow->removeDontCare(); } QLabel* label; d->m_editWidgets["account-label"] = label = new QLabel(i18n("Account")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["category-label"] = label = new QLabel(i18n("Category")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["tag-label"] = label = new QLabel(i18n("Tags")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["memo-label"] = label = new QLabel(i18n("Memo")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["number-label"] = label = new QLabel(i18n("Number")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["date-label"] = label = new QLabel(i18n("Date")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["amount-label"] = label = new QLabel(i18n("Amount")); label->setAlignment(Qt::AlignVCenter); d->m_editWidgets["status-label"] = label = new QLabel(i18n("Status")); label->setAlignment(Qt::AlignVCenter); // create a copy of tabbar above the form (if we are created for a form) KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast(d->m_regForm); if (form) { KMyMoneyTransactionForm::TabBar* tabbar = new KMyMoneyTransactionForm::TabBar; d->m_editWidgets["tabbar"] = tabbar; tabbar->setObjectName(QLatin1String("TabBar")); tabbar->copyTabs(form->tabBar()); connect(tabbar, &TabBar::tabCurrentChanged, this, &StdTransactionEditor::slotUpdateAction); connect(tabbar, &TabBar::tabCurrentChanged, this, &TransactionEditor::operationTypeChanged); } setupPrecision(); } void StdTransactionEditor::setupCategoryWidget(QString& categoryId) { Q_D(StdTransactionEditor); TransactionEditor::setupCategoryWidget(dynamic_cast(d->m_editWidgets["category"]), d->m_splits, categoryId, SLOT(slotEditSplits())); if (d->m_splits.count() == 1) d->m_shares = d->m_splits[0].shares(); } bool StdTransactionEditor::isTransfer(const QString& accId1, const QString& accId2) const { if (accId1.isEmpty() || accId2.isEmpty()) return false; return MyMoneyFile::instance()->account(accId1).isIncomeExpense() == MyMoneyFile::instance()->account(accId2).isIncomeExpense(); } void StdTransactionEditor::loadEditWidgets(KMyMoneyRegister::Action action) { Q_D(StdTransactionEditor); // don't kick off VAT processing from here d->m_inUpdateVat = true; QMap::const_iterator it_w; QWidget* w; AccountSet aSet; // load the account widget KMyMoneyCategory* account = dynamic_cast(haveWidget("account")); if (account) { aSet.addAccountGroup(eMyMoney::Account::Asset); aSet.addAccountGroup(eMyMoney::Account::Liability); aSet.removeAccountType(eMyMoney::Account::AssetLoan); aSet.removeAccountType(eMyMoney::Account::CertificateDep); aSet.removeAccountType(eMyMoney::Account::Investment); aSet.removeAccountType(eMyMoney::Account::Stock); aSet.removeAccountType(eMyMoney::Account::MoneyMarket); aSet.removeAccountType(eMyMoney::Account::Loan); aSet.load(account->selector()); account->completion()->setSelected(d->m_account.id()); account->slotItemSelected(d->m_account.id()); } // load the payee widget auto payee = dynamic_cast(d->m_editWidgets["payee"]); payee->loadPayees(MyMoneyFile::instance()->payeeList()); // load the category widget auto category = dynamic_cast(d->m_editWidgets["category"]); disconnect(category, &KMyMoneyCategory::focusIn, this, &StdTransactionEditor::slotEditSplits); // load the tag widget //auto tag = dynamic_cast(m_editWidgets["tag"]); auto tag = dynamic_cast(d->m_editWidgets["tag"]); tag->loadTags(MyMoneyFile::instance()->tagList()); // check if the current transaction has a reference to an equity account bool haveEquityAccount = false; QList::const_iterator it_s; for (it_s = d->m_transaction.splits().constBegin(); !haveEquityAccount && it_s != d->m_transaction.splits().constEnd(); ++it_s) { MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId()); if (acc.accountType() == eMyMoney::Account::Equity) haveEquityAccount = true; } aSet.clear(); aSet.addAccountGroup(eMyMoney::Account::Asset); aSet.addAccountGroup(eMyMoney::Account::Liability); aSet.addAccountGroup(eMyMoney::Account::Income); aSet.addAccountGroup(eMyMoney::Account::Expense); if (KMyMoneyGlobalSettings::expertMode() || haveEquityAccount) aSet.addAccountGroup(eMyMoney::Account::Equity); aSet.removeAccountType(eMyMoney::Account::CertificateDep); aSet.removeAccountType(eMyMoney::Account::Investment); aSet.removeAccountType(eMyMoney::Account::Stock); aSet.removeAccountType(eMyMoney::Account::MoneyMarket); aSet.load(category->selector()); // if an account is specified then remove it from the widget so that the user // cannot create a transfer with from and to account being the same account if (!d->m_account.id().isEmpty()) category->selector()->removeItem(d->m_account.id()); // also show memo text if isMultiSelection() dynamic_cast(d->m_editWidgets["memo"])->setText(d->m_split.memo()); // need to know if it changed d->m_memoText = d->m_split.memo(); d->m_memoChanged = false; if (!isMultiSelection()) { if (d->m_transaction.postDate().isValid()) dynamic_cast(d->m_editWidgets["postdate"])->setDate(d->m_transaction.postDate()); else if (d->m_lastPostDate.isValid()) dynamic_cast(d->m_editWidgets["postdate"])->setDate(d->m_lastPostDate); else dynamic_cast(d->m_editWidgets["postdate"])->setDate(QDate::currentDate()); if ((w = haveWidget("number")) != 0) { dynamic_cast(w)->loadText(d->m_split.number()); if (d->m_transaction.id().isEmpty() // new transaction && dynamic_cast(w)->text().isEmpty() // no number filled in && d->m_account.accountType() == eMyMoney::Account::Checkings // checkings account && KMyMoneyGlobalSettings::autoIncCheckNumber() // and auto inc number turned on? && action != KMyMoneyRegister::ActionDeposit // only transfers or withdrawals && d->m_paymentMethod == eMyMoney::Schedule::PaymentType::WriteChecque) {// only for WriteChecque assignNextNumber(); } } dynamic_cast(d->m_editWidgets["status"])->setState(d->m_split.reconcileFlag()); QString payeeId = d->m_split.payeeId(); if (!payeeId.isEmpty()) { payee->setSelectedItem(payeeId); } QList t = d->m_split.tagIdList(); if (!t.isEmpty()) { for (auto i = 0; i < t.size(); i++) tag->addTagWidget(t[i]); } d->m_splits.clear(); if (d->m_transaction.splitCount() < 2) { category->completion()->setSelected(QString()); } else { QList::const_iterator it_s; for (it_s = d->m_transaction.splits().constBegin(); it_s != d->m_transaction.splits().constEnd(); ++it_s) { if ((*it_s) == d->m_split) continue; d->m_splits.append(*it_s); } } QString categoryId; setupCategoryWidget(categoryId); if ((w = haveWidget("cashflow")) != 0) { KMyMoneyCashFlowCombo* cashflow = dynamic_cast(w); cashflow->setDirection(!d->m_split.value().isPositive() ? KMyMoneyRegister::Payment : KMyMoneyRegister::Deposit); // include isZero case } if ((w = haveWidget("category-label")) != 0) { QLabel *categoryLabel = dynamic_cast(w); if (isTransfer(d->m_split.accountId(), categoryId)) { if (d->m_split.value().isPositive()) categoryLabel->setText(i18n("Transfer from")); else categoryLabel->setText(i18n("Transfer to")); } } MyMoneyMoney value = d->m_split.shares(); if (haveWidget("deposit")) { if (d->m_split.shares().isNegative()) { dynamic_cast(d->m_editWidgets["deposit"])->loadText(""); dynamic_cast(d->m_editWidgets["payment"])->setValue(value.abs()); } else { dynamic_cast(d->m_editWidgets["deposit"])->setValue(value.abs()); dynamic_cast(d->m_editWidgets["payment"])->loadText(""); } } if ((w = haveWidget("amount")) != 0) { dynamic_cast(w)->setValue(value.abs()); } slotUpdateCategory(categoryId); // try to preset for specific action if a new transaction is being started if (d->m_transaction.id().isEmpty()) { if ((w = haveWidget("category-label")) != 0) { TabBar* tabbar = dynamic_cast(haveWidget("tabbar")); if (action == KMyMoneyRegister::ActionNone) { if (tabbar) { action = static_cast(tabbar->currentIndex()); } } if (action != KMyMoneyRegister::ActionNone) { QLabel *categoryLabel = dynamic_cast(w); if (action == KMyMoneyRegister::ActionTransfer) { if (d->m_split.value().isPositive()) categoryLabel->setText(i18n("Transfer from")); else categoryLabel->setText(i18n("Transfer to")); } if ((w = haveWidget("cashflow")) != 0) { KMyMoneyCashFlowCombo* cashflow = dynamic_cast(w); if (action == KMyMoneyRegister::ActionDeposit || (action == KMyMoneyRegister::ActionTransfer && d->m_split.value().isPositive())) cashflow->setDirection(KMyMoneyRegister::Deposit); else cashflow->setDirection(KMyMoneyRegister::Payment); } if (tabbar) { tabbar->setCurrentIndex(action); } } } } else { TabBar* tabbar = dynamic_cast(haveWidget("tabbar")); if (tabbar) { if (!isTransfer(d->m_split.accountId(), categoryId)) { tabbar->setCurrentIndex(d->m_split.value().isNegative() ? KMyMoneyRegister::ActionWithdrawal : KMyMoneyRegister::ActionDeposit); } else { tabbar->setCurrentIndex(KMyMoneyRegister::ActionTransfer); } } } } else { // isMultiSelection() dynamic_cast(d->m_editWidgets["postdate"])->loadDate(QDate()); dynamic_cast(d->m_editWidgets["status"])->setState(eMyMoney::Split::State::Unknown); if (haveWidget("deposit")) { dynamic_cast(d->m_editWidgets["deposit"])->loadText(""); dynamic_cast(d->m_editWidgets["deposit"])->setAllowEmpty(); dynamic_cast(d->m_editWidgets["payment"])->loadText(""); dynamic_cast(d->m_editWidgets["payment"])->setAllowEmpty(); } if ((w = haveWidget("amount")) != 0) { dynamic_cast(w)->loadText(""); dynamic_cast(w)->setAllowEmpty(); } slotUpdateAction(action); if ((w = haveWidget("tabbar")) != 0) { w->setEnabled(false); } category->completion()->setSelected(QString()); } // allow kick off VAT processing again d->m_inUpdateVat = false; } void StdTransactionEditor::loadEditWidgets() { loadEditWidgets(KMyMoneyRegister::ActionNone); } QWidget* StdTransactionEditor::firstWidget() const { Q_D(const StdTransactionEditor); QWidget* w = 0; if (d->m_initialAction != KMyMoneyRegister::ActionNone) { w = haveWidget("payee"); } return w; } void StdTransactionEditor::slotReloadEditWidgets() { Q_D(StdTransactionEditor); // reload category widget KMyMoneyCategory* category = dynamic_cast(d->m_editWidgets["category"]); QString categoryId = category->selectedItem(); AccountSet aSet; aSet.addAccountGroup(eMyMoney::Account::Asset); aSet.addAccountGroup(eMyMoney::Account::Liability); aSet.addAccountGroup(eMyMoney::Account::Income); aSet.addAccountGroup(eMyMoney::Account::Expense); if (KMyMoneyGlobalSettings::expertMode()) aSet.addAccountGroup(eMyMoney::Account::Equity); aSet.load(category->selector()); // if an account is specified then remove it from the widget so that the user // cannot create a transfer with from and to account being the same account if (!d->m_account.id().isEmpty()) category->selector()->removeItem(d->m_account.id()); if (!categoryId.isEmpty()) category->setSelectedItem(categoryId); // reload payee widget KMyMoneyPayeeCombo* payee = dynamic_cast(d->m_editWidgets["payee"]); QString payeeId = payee->selectedItem(); payee->loadPayees(MyMoneyFile::instance()->payeeList()); if (!payeeId.isEmpty()) { payee->setSelectedItem(payeeId); } // reload tag widget KTagContainer* tag = dynamic_cast(d->m_editWidgets["tag"]); QString tagId = tag->tagCombo()->selectedItem(); tag->loadTags(MyMoneyFile::instance()->tagList()); if (!tagId.isEmpty()) { tag->RemoveAllTagWidgets(); tag->addTagWidget(tagId); } } void StdTransactionEditor::slotUpdatePayee(const QString& payeeId) { Q_D(StdTransactionEditor); // we have a new payee assigned to this transaction. // in case there is no category assigned, no value entered and no // memo available, we search for the last transaction of this payee // in the account. if (d->m_transaction.id().isEmpty() && d->m_splits.count() == 0 && KMyMoneyGlobalSettings::autoFillTransaction() != 0) { // check if category is empty KMyMoneyCategory* category = dynamic_cast(d->m_editWidgets["category"]); QStringList list; category->selectedItems(list); if (!list.isEmpty()) return; // check if memo is empty KTextEdit* memo = dynamic_cast(d->m_editWidgets["memo"]); if (memo && !memo->toPlainText().isEmpty()) return; // check if all value fields are empty kMyMoneyEdit* amount; QStringList fields; fields << "amount" << "payment" << "deposit"; QStringList::const_iterator it_f; for (it_f = fields.constBegin(); it_f != fields.constEnd(); ++it_f) { amount = dynamic_cast(haveWidget(*it_f)); if (amount && !amount->value().isZero()) return; } #if 0 // Tony mentioned, that autofill does not work when he changed the date. Well, // that certainly makes sense when you enter transactions in register mode as // opposed to form mode, because the date field is located prior to the date // field in the tab order of the widgets and the user might have already // changed it. // // So I commented out the code that checks the date but left it in for reference. // (ipwizard, 2008-04-07) // check if date has been altered by user kMyMoneyDateInput* postDate = dynamic_cast(m_editWidgets["postdate"]); if ((m_lastPostDate.isValid() && (postDate->date() != m_lastPostDate)) || (!m_lastPostDate.isValid() && (postDate->date() != QDate::currentDate()))) return; #endif // if we got here, we have to autofill autoFill(payeeId); } // If payee has associated default account (category), set that now. const MyMoneyPayee& payeeObj = MyMoneyFile::instance()->payee(payeeId); if (payeeObj.defaultAccountEnabled()) { KMyMoneyCategory* category = dynamic_cast(d->m_editWidgets["category"]); category->slotItemSelected(payeeObj.defaultAccountId()); } } MyMoneyMoney StdTransactionEditor::shares(const MyMoneyTransaction& t) const { Q_D(const StdTransactionEditor); MyMoneyMoney result; QList::const_iterator it_s; for (it_s = t.splits().begin(); it_s != t.splits().end(); ++it_s) { if ((*it_s).accountId() == d->m_account.id()) { result += (*it_s).shares(); } } return result; } struct uniqTransaction { const MyMoneyTransaction* t; int cnt; }; void StdTransactionEditor::autoFill(const QString& payeeId) { Q_D(StdTransactionEditor); QList > list; MyMoneyTransactionFilter filter(d->m_account.id()); filter.addPayee(payeeId); MyMoneyFile::instance()->transactionList(list, filter); if (!list.empty()) { // ok, we found at least one previous transaction. now we clear out // what we have collected so far and add those splits from // the previous transaction. QList >::const_iterator it_t; QMap uniqList; // collect the transactions and see if we have any duplicates for (it_t = list.constBegin(); it_t != list.constEnd(); ++it_t) { QString key = (*it_t).first.accountSignature(); int cnt = 0; QMap::iterator it_u; do { QString ukey = QString("%1-%2").arg(key).arg(cnt); it_u = uniqList.find(ukey); if (it_u == uniqList.end()) { uniqList[ukey].t = &((*it_t).first); uniqList[ukey].cnt = 1; } else if (KMyMoneyGlobalSettings::autoFillTransaction() == 1) { // we already have a transaction with this signature. we must // now check, if we should really treat it as a duplicate according // to the value comparison delta. MyMoneyMoney s1 = shares(*((*it_u).t)); MyMoneyMoney s2 = shares((*it_t).first); if (s2.abs() > s1.abs()) { MyMoneyMoney t(s1); s1 = s2; s2 = t; } MyMoneyMoney diff; if (s2.isZero()) diff = s1.abs(); else diff = ((s1 - s2) / s2).convert(10000); if (diff.isPositive() && diff <= MyMoneyMoney(KMyMoneyGlobalSettings::autoFillDifference(), 100)) { uniqList[ukey].t = &((*it_t).first); break; // end while loop } } else if (KMyMoneyGlobalSettings::autoFillTransaction() == 2) { (*it_u).cnt++; break; // end while loop } ++cnt; } while (it_u != uniqList.end()); } MyMoneyTransaction t; if (KMyMoneyGlobalSettings::autoFillTransaction() != 2) { #if 0 // I removed this code to allow cancellation of an autofill if // it does not match even if there is only a single matching // transaction for the payee in question. In case, we want to revert // to the old behavior, don't forget to uncomment the closing // brace further down in the code as well. (ipwizard 2009-01-16) if (uniqList.count() == 1) { t = list.last().first; } else { #endif QPointer dlg = new KSelectTransactionsDlg(d->m_account, d->m_regForm); dlg->setWindowTitle(i18n("Select autofill transaction")); QMap::const_iterator it_u; for (it_u = uniqList.constBegin(); it_u != uniqList.constEnd(); ++it_u) { dlg->addTransaction(*(*it_u).t); } auto tRegister = dlg->getRegister(); // setup sort order tRegister->setSortOrder("1,-9,-4"); // sort the transactions according to the sort setting tRegister->sortItems(); // and select the last item if (tRegister->lastItem()) tRegister->selectItem(tRegister->lastItem()); if (dlg->exec() == QDialog::Accepted) { t = dlg->transaction(); } #if 0 } #endif } else { int maxCnt = 0; QMap::const_iterator it_u; for (it_u = uniqList.constBegin(); it_u != uniqList.constEnd(); ++it_u) { if ((*it_u).cnt > maxCnt) { t = *(*it_u).t; maxCnt = (*it_u).cnt; } } } if (t != MyMoneyTransaction()) { d->m_transaction.removeSplits(); d->m_split = MyMoneySplit(); MyMoneySplit otherSplit; QList::ConstIterator it; for (it = t.splits().constBegin(); it != t.splits().constEnd(); ++it) { MyMoneySplit s(*it); s.setReconcileFlag(eMyMoney::Split::State::NotReconciled); s.setReconcileDate(QDate()); s.clearId(); s.setBankID(QString()); // older versions of KMyMoney used to set the action // we don't need this anymore if (s.action() != MyMoneySplit::ActionAmortization && s.action() != MyMoneySplit::ActionInterest) { s.setAction(QString()); } // FIXME update check number. The old comment contained // // // If a check number is already specified by the user it is // used. If the input field is empty and the previous transaction // contains a checknumber, the next usable check number will be assigned // to the transaction. // kMyMoneyLineEdit* editNr = dynamic_cast(haveWidget("number")); if (editNr && !editNr->text().isEmpty()) { s.setNumber(editNr->text()); } else if (!s.number().isEmpty()) { s.setNumber(KMyMoneyUtils::nextCheckNumber(d->m_account)); } // if the memos should not be used with autofill or // if the transaction has exactly two splits, remove // the memo text of the split that does not reference // the current account. This allows the user to change // the autofilled memo text which will then also be used // in this split. See createTransaction() for this logic. if ((s.accountId() != d->m_account.id() && t.splitCount() == 2) || !KMyMoneyGlobalSettings::autoFillUseMemos()) s.setMemo(QString()); d->m_transaction.addSplit(s); if (s.accountId() == d->m_account.id() && d->m_split == MyMoneySplit()) { d->m_split = s; } else { otherSplit = s; } } // make sure to extract the right action KMyMoneyRegister::Action action; action = d->m_split.shares().isNegative() ? KMyMoneyRegister::ActionWithdrawal : KMyMoneyRegister::ActionDeposit; if (d->m_transaction.splitCount() == 2) { MyMoneyAccount acc = MyMoneyFile::instance()->account(otherSplit.accountId()); if (acc.isAssetLiability()) action = KMyMoneyRegister::ActionTransfer; } // now setup the widgets with the new data but keep the date QDate date = dynamic_cast(d->m_editWidgets["postdate"])->date(); loadEditWidgets(action); dynamic_cast(d->m_editWidgets["postdate"])->setDate(date); } } // focus jumps into the category field QWidget* w; if ((w = haveWidget("payee")) != 0) { w->setFocus(); } } void StdTransactionEditor::slotUpdateAction(int action) { Q_D(StdTransactionEditor); TabBar* tabbar = dynamic_cast(haveWidget("tabbar")); if (tabbar) { QLabel* categoryLabel = dynamic_cast(haveWidget("category-label")); KMyMoneyCashFlowCombo* cashflow = dynamic_cast(d->m_editWidgets["cashflow"]); switch (action) { case KMyMoneyRegister::ActionDeposit: categoryLabel->setText(i18n("Category")); cashflow->setDirection(KMyMoneyRegister::Deposit); break; case KMyMoneyRegister::ActionTransfer: if (d->m_split.shares().isNegative()) { cashflow->setDirection(KMyMoneyRegister::Payment); categoryLabel->setText(i18n("Transfer to")); } else { cashflow->setDirection(KMyMoneyRegister::Deposit); categoryLabel->setText(i18n("Transfer from")); } tabbar->setCurrentIndex(KMyMoneyRegister::ActionTransfer); slotUpdateCashFlow(cashflow->direction()); break; case KMyMoneyRegister::ActionWithdrawal: categoryLabel->setText(i18n("Category")); cashflow->setDirection(KMyMoneyRegister::Payment); break; } resizeForm(); } } void StdTransactionEditor::slotUpdateCashFlow(KMyMoneyRegister::CashFlowDirection dir) { QLabel* categoryLabel = dynamic_cast(haveWidget("category-label")); KMyMoneyCashFlowCombo* cashflow = dynamic_cast(haveWidget("cashflow")); cashflow->setDirection(dir); // qDebug("Update cashflow to %d", dir); if (categoryLabel) { TabBar* tabbar = dynamic_cast(haveWidget("tabbar")); if (!tabbar) return; // no transaction form if (categoryLabel->text() != i18n("Category")) { tabbar->setCurrentIndex(KMyMoneyRegister::ActionTransfer); if (dir == KMyMoneyRegister::Deposit) { categoryLabel->setText(i18n("Transfer from")); } else { categoryLabel->setText(i18n("Transfer to")); } resizeForm(); } else { if (dir == KMyMoneyRegister::Deposit) tabbar->setCurrentIndex(KMyMoneyRegister::ActionDeposit); else tabbar->setCurrentIndex(KMyMoneyRegister::ActionWithdrawal); } } } void StdTransactionEditor::slotUpdateCategory(const QString& id) { Q_D(StdTransactionEditor); QLabel *categoryLabel = dynamic_cast(haveWidget("category-label")); // qDebug("Update category to %s", qPrintable(id)); if (categoryLabel) { TabBar* tabbar = dynamic_cast(haveWidget("tabbar")); kMyMoneyEdit* amount = dynamic_cast(d->m_editWidgets["amount"]); MyMoneyMoney val = amount->value(); if (categoryLabel->text() == i18n("Transfer from")) { val = -val; } else { val = val.abs(); } if (tabbar) { tabbar->setTabEnabled(KMyMoneyRegister::ActionTransfer, true); tabbar->setTabEnabled(KMyMoneyRegister::ActionDeposit, true); tabbar->setTabEnabled(KMyMoneyRegister::ActionWithdrawal, true); } bool disableTransferTab = false; if (!id.isEmpty()) { MyMoneyAccount acc = MyMoneyFile::instance()->account(id); if (acc.isAssetLiability() || acc.accountGroup() == eMyMoney::Account::Equity) { if (tabbar) { tabbar->setCurrentIndex(KMyMoneyRegister::ActionTransfer); tabbar->setTabEnabled(KMyMoneyRegister::ActionDeposit, false); tabbar->setTabEnabled(KMyMoneyRegister::ActionWithdrawal, false); } KMyMoneyCashFlowCombo* cashflow = dynamic_cast(d->m_editWidgets["cashflow"]); if (val.isZero()) { if (cashflow && (cashflow->direction() == KMyMoneyRegister::Deposit)) { categoryLabel->setText(i18n("Transfer from")); } else { categoryLabel->setText(i18n("Transfer to")); } } else if (val.isNegative()) { categoryLabel->setText(i18n("Transfer from")); cashflow->setDirection(KMyMoneyRegister::Deposit); } else categoryLabel->setText(i18n("Transfer to")); } else { disableTransferTab = true; categoryLabel->setText(i18n("Category")); } updateAmount(val); } else { //id.isEmpty() KMyMoneyCategory* category = dynamic_cast(d->m_editWidgets["category"]); disableTransferTab = !category->currentText().isEmpty(); categoryLabel->setText(i18n("Category")); } if (tabbar) { if (disableTransferTab) { // set the proper tab before disabling the currently active tab if (tabbar->currentIndex() == KMyMoneyRegister::ActionTransfer) { tabbar->setCurrentIndex(val.isPositive() ? KMyMoneyRegister::ActionWithdrawal : KMyMoneyRegister::ActionDeposit); } tabbar->setTabEnabled(KMyMoneyRegister::ActionTransfer, false); } tabbar->update(); } resizeForm(); } updateVAT(false); } void StdTransactionEditor::slotUpdatePayment(const QString& txt) { Q_D(StdTransactionEditor); MyMoneyMoney val(txt); if (val.isNegative()) { dynamic_cast(d->m_editWidgets["deposit"])->setValue(val.abs()); dynamic_cast(d->m_editWidgets["payment"])->clearText(); } else { dynamic_cast(d->m_editWidgets["deposit"])->clearText(); } updateVAT(); } void StdTransactionEditor::slotUpdateDeposit(const QString& txt) { Q_D(StdTransactionEditor); MyMoneyMoney val(txt); if (val.isNegative()) { dynamic_cast(d->m_editWidgets["payment"])->setValue(val.abs()); dynamic_cast(d->m_editWidgets["deposit"])->clearText(); } else { dynamic_cast(d->m_editWidgets["payment"])->clearText(); } updateVAT(); } void StdTransactionEditor::slotUpdateAmount(const QString& txt) { // qDebug("Update amount to %s", qPrintable(txt)); MyMoneyMoney val(txt); updateAmount(val); updateVAT(true); } void StdTransactionEditor::updateAmount(const MyMoneyMoney& val) { // we don't do anything if we have multiple transactions selected if (isMultiSelection()) return; Q_D(StdTransactionEditor); QLabel *categoryLabel = dynamic_cast(haveWidget("category-label")); if (categoryLabel) { KMyMoneyCashFlowCombo* cashflow = dynamic_cast(d->m_editWidgets["cashflow"]); if (!val.isPositive()) { // fixes BUG321317 if (categoryLabel->text() != i18n("Category")) { if (cashflow->direction() == KMyMoneyRegister::Payment) { categoryLabel->setText(i18n("Transfer to")); } } else { slotUpdateCashFlow(cashflow->direction()); } dynamic_cast(d->m_editWidgets["amount"])->setValue(val.abs()); } else { if (categoryLabel->text() != i18n("Category")) { if (cashflow->direction() == KMyMoneyRegister::Payment) { categoryLabel->setText(i18n("Transfer to")); } else { categoryLabel->setText(i18n("Transfer from")); cashflow->setDirection(KMyMoneyRegister::Deposit); // editing with +ve shows 'from' not 'pay to' } } dynamic_cast(d->m_editWidgets["amount"])->setValue(val.abs()); } } } void StdTransactionEditor::updateVAT(bool amountChanged) { Q_D(StdTransactionEditor); // make sure that we don't do this recursively if (d->m_inUpdateVat) return; // we don't do anything if we have multiple transactions selected if (isMultiSelection()) return; // if auto vat assignment for this account is turned off // we don't care about taxes if (d->m_account.value("NoVat") == "Yes") return; // more splits than category and tax are not supported if (d->m_splits.count() > 2) return; // in order to do anything, we need an amount MyMoneyMoney amount, newAmount; bool amountOk; amount = amountFromWidget(&amountOk); if (!amountOk) return; // If the transaction has a tax and a category split, remove the tax split if (d->m_splits.count() == 2) { newAmount = removeVatSplit(); if (d->m_splits.count() == 2) // not removed? return; } else { // otherwise, we need a category KMyMoneyCategory* category = dynamic_cast(d->m_editWidgets["category"]); if (category->selectedItem().isEmpty()) return; // if no VAT account is associated with this category/account, then we bail out MyMoneyAccount cat = MyMoneyFile::instance()->account(category->selectedItem()); if (cat.value("VatAccount").isEmpty()) return; newAmount = amount; } // seems we have everything we need if (amountChanged) newAmount = amount; MyMoneyTransaction transaction; if (createTransaction(transaction, d->m_transaction, d->m_split)) { if (addVatSplit(transaction, newAmount)) { d->m_transaction = transaction; if (!d->m_transaction.splits().isEmpty()) d->m_split = d->m_transaction.splits().front(); loadEditWidgets(); // if we made this a split transaction, then move the // focus to the memo field if (qApp->focusWidget() == haveWidget("category")) { QWidget* w = haveWidget("memo"); if (w) w->setFocus(); } } } } bool StdTransactionEditor::addVatSplit(MyMoneyTransaction& tr, const MyMoneyMoney& amount) { if (tr.splitCount() != 2) return false; Q_D(StdTransactionEditor); auto file = MyMoneyFile::instance(); // extract the category split from the transaction MyMoneyAccount category = file->account(tr.splitByAccount(d->m_account.id(), false).accountId()); return file->addVATSplit(tr, d->m_account, category, amount); } MyMoneyMoney StdTransactionEditor::removeVatSplit() { Q_D(StdTransactionEditor); // we only deal with splits that have three splits if (d->m_splits.count() != 2) return amountFromWidget(); MyMoneySplit c; // category split MyMoneySplit t; // tax split bool netValue = false; QList::const_iterator it_s; for (it_s = d->m_splits.constBegin(); it_s != d->m_splits.constEnd(); ++it_s) { MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId()); if (!acc.value("VatAccount").isEmpty()) { netValue = (acc.value("VatAmount").toLower() == "net"); c = (*it_s); } else if (!acc.value("VatRate").isEmpty()) { t = (*it_s); } } // bail out if not all splits are setup if (c.id().isEmpty() || t.id().isEmpty()) return amountFromWidget(); MyMoneyMoney amount; // reduce the splits if (netValue) { amount = -c.shares(); } else { amount = -(c.shares() + t.shares()); } // remove tax split from the list, ... d->m_splits.clear(); d->m_splits.append(c); // ... make sure that the widget is updated ... // block the signals to avoid popping up the split editor dialog // for nothing d->m_editWidgets["category"]->blockSignals(true); QString id; setupCategoryWidget(id); d->m_editWidgets["category"]->blockSignals(false); // ... and return the updated amount return amount; } bool StdTransactionEditor::isComplete(QString& reason) const { Q_D(const StdTransactionEditor); reason.clear(); QMap::const_iterator it_w; kMyMoneyDateInput* postDate = dynamic_cast(d->m_editWidgets["postdate"]); if (postDate) { QDate accountOpeningDate = d->m_account.openingDate(); for (QList::const_iterator it_s = d->m_splits.constBegin(); it_s != d->m_splits.constEnd(); ++it_s) { const MyMoneyAccount& acc = MyMoneyFile::instance()->account((*it_s).accountId()); // compute the newest opening date of all accounts involved in the transaction if (acc.openingDate() > accountOpeningDate) accountOpeningDate = acc.openingDate(); } // check the selected category in case m_splits hasn't been updated yet KMyMoneyCategory* category = dynamic_cast(d->m_editWidgets["category"]); if (category && !category->selectedItem().isEmpty()) { MyMoneyAccount cat = MyMoneyFile::instance()->account(category->selectedItem()); if (cat.openingDate() > accountOpeningDate) accountOpeningDate = cat.openingDate(); } if (postDate->date().isValid() && (postDate->date() < accountOpeningDate)) { postDate->markAsBadDate(true, KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative)); reason = i18n("Cannot enter transaction with postdate prior to account's opening date."); postDate->setToolTip(reason); return false; } postDate->markAsBadDate(); postDate->setToolTip(""); } for (it_w = d->m_editWidgets.begin(); it_w != d->m_editWidgets.end(); ++it_w) { KMyMoneyPayeeCombo* payee = dynamic_cast(*it_w); KTagContainer* tagContainer = dynamic_cast(*it_w); KMyMoneyCategory* category = dynamic_cast(*it_w); kMyMoneyEdit* amount = dynamic_cast(*it_w); KMyMoneyReconcileCombo* reconcile = dynamic_cast(*it_w); KMyMoneyCashFlowCombo* cashflow = dynamic_cast(*it_w); KTextEdit* memo = dynamic_cast(*it_w); if (payee && !(payee->currentText().isEmpty())) break; if (category && !category->lineEdit()->text().isEmpty()) break; if (amount && !(amount->value().isZero())) break; // the following widgets are only checked if we are editing multiple transactions if (isMultiSelection()) { TabBar* tabbar = dynamic_cast(haveWidget("tabbar")); if (tabbar) { tabbar->setEnabled(true); } if (reconcile && reconcile->state() != eMyMoney::Split::State::Unknown) break; if (cashflow && cashflow->direction() != KMyMoneyRegister::Unknown) break; if (postDate->date().isValid() && (postDate->date() >= d->m_account.openingDate())) break; if (memo && d->m_memoChanged) break; if (tagContainer && !(tagContainer->selectedTags().isEmpty())) // Tag is optional field break; } } return it_w != d->m_editWidgets.end(); } void StdTransactionEditor::slotCreateCategory(const QString& name, QString& id) { Q_D(StdTransactionEditor); MyMoneyAccount acc, parent; acc.setName(name); KMyMoneyCashFlowCombo* cashflow = dynamic_cast(haveWidget("cashflow")); if (cashflow) { // form based input if (cashflow->direction() == KMyMoneyRegister::Deposit) parent = MyMoneyFile::instance()->income(); else parent = MyMoneyFile::instance()->expense(); } else if (haveWidget("deposit")) { // register based input kMyMoneyEdit* deposit = dynamic_cast(d->m_editWidgets["deposit"]); if (deposit->value().isPositive()) parent = MyMoneyFile::instance()->income(); else parent = MyMoneyFile::instance()->expense(); } else parent = MyMoneyFile::instance()->expense(); // TODO extract possible first part of a hierarchy and check if it is one // of our top categories. If so, remove it and select the parent // according to this information. emit createCategory(acc, parent); // return id id = acc.id(); } int StdTransactionEditor::slotEditSplits() { Q_D(StdTransactionEditor); int rc = QDialog::Rejected; if (!d->m_openEditSplits) { // only get in here in a single instance d->m_openEditSplits = true; // force focus change to update all data QWidget* w = dynamic_cast(d->m_editWidgets["category"])->splitButton(); if (w) w->setFocus(); kMyMoneyEdit* amount = dynamic_cast(haveWidget("amount")); kMyMoneyEdit* deposit = dynamic_cast(haveWidget("deposit")); kMyMoneyEdit* payment = dynamic_cast(haveWidget("payment")); KMyMoneyCashFlowCombo* cashflow = 0; KMyMoneyRegister::CashFlowDirection dir = KMyMoneyRegister::Unknown; bool isValidAmount = false; if (amount) { isValidAmount = amount->lineedit()->text().length() != 0; cashflow = dynamic_cast(haveWidget("cashflow")); if (cashflow) dir = cashflow->direction(); } else { if (deposit) { if (deposit->lineedit()->text().length() != 0) { isValidAmount = true; dir = KMyMoneyRegister::Deposit; } } if (payment) { if (payment->lineedit()->text().length() != 0) { isValidAmount = true; dir = KMyMoneyRegister::Payment; } } if (!deposit || !payment) { qDebug("Internal error: deposit(%p) & payment(%p) widgets not found but required", deposit, payment); return rc; } } if (dir == KMyMoneyRegister::Unknown) dir = KMyMoneyRegister::Payment; MyMoneyTransaction transaction; if (createTransaction(transaction, d->m_transaction, d->m_split)) { MyMoneyMoney value; QPointer dlg = new KSplitTransactionDlg(transaction, transaction.splits().isEmpty() ? MyMoneySplit() : transaction.splits().front(), d->m_account, isValidAmount, dir == KMyMoneyRegister::Deposit, MyMoneyMoney(), d->m_priceInfo, d->m_regForm); connect(dlg, SIGNAL(objectCreation(bool)), this, SIGNAL(objectCreation(bool))); connect(dlg, SIGNAL(createCategory(MyMoneyAccount&,MyMoneyAccount)), this, SIGNAL(createCategory(MyMoneyAccount&,MyMoneyAccount))); if ((rc = dlg->exec()) == QDialog::Accepted) { d->m_transaction = dlg->transaction(); if (!d->m_transaction.splits().isEmpty()) d->m_split = d->m_transaction.splits().front(); loadEditWidgets(); } delete dlg; } // focus jumps into the tag field if ((w = haveWidget("tag")) != 0) { w->setFocus(); } d->m_openEditSplits = false; } return rc; } void StdTransactionEditor::checkPayeeInSplit(MyMoneySplit& s, const QString& payeeId) { if (s.accountId().isEmpty()) return; MyMoneyAccount acc = MyMoneyFile::instance()->account(s.accountId()); if (acc.isIncomeExpense()) { s.setPayeeId(payeeId); } else { if (s.payeeId().isEmpty()) s.setPayeeId(payeeId); } } MyMoneyMoney StdTransactionEditor::amountFromWidget(bool* update) const { Q_D(const StdTransactionEditor); bool updateValue = false; MyMoneyMoney value; auto cashflow = dynamic_cast(haveWidget("cashflow")); if (cashflow) { // form based input auto amount = dynamic_cast(d->m_editWidgets["amount"]); // if both fields do not contain changes -> no need to update if (cashflow->direction() != KMyMoneyRegister::Unknown && !amount->lineedit()->text().isEmpty()) updateValue = true; value = amount->value(); if (cashflow->direction() == KMyMoneyRegister::Payment) value = -value; } else if (haveWidget("deposit")) { // register based input auto deposit = dynamic_cast(d->m_editWidgets["deposit"]); auto payment = dynamic_cast(d->m_editWidgets["payment"]); // if both fields do not contain text -> no need to update if (!(deposit->lineedit()->text().isEmpty() && payment->lineedit()->text().isEmpty())) updateValue = true; if (deposit->value().isPositive()) value = deposit->value(); else value = -(payment->value()); } if (update) *update = updateValue; // determine the max fraction for this account and // adjust the value accordingly return value.convert(d->m_account.fraction()); } bool StdTransactionEditor::createTransaction(MyMoneyTransaction& t, const MyMoneyTransaction& torig, const MyMoneySplit& sorig, bool skipPriceDialog) { Q_D(StdTransactionEditor); // extract price info from original transaction d->m_priceInfo.clear(); QList::const_iterator it_s; if (!torig.id().isEmpty()) { for (it_s = torig.splits().begin(); it_s != torig.splits().end(); ++it_s) { if ((*it_s).id() != sorig.id()) { MyMoneyAccount cat = MyMoneyFile::instance()->account((*it_s).accountId()); if (cat.currencyId() != d->m_account.currencyId()) { if (!(*it_s).shares().isZero() && !(*it_s).value().isZero()) { d->m_priceInfo[cat.currencyId()] = ((*it_s).shares() / (*it_s).value()).reduce(); } } } } } t = torig; t.removeSplits(); t.setCommodity(d->m_account.currencyId()); kMyMoneyDateInput* postDate = dynamic_cast(d->m_editWidgets["postdate"]); if (postDate->date().isValid()) { t.setPostDate(postDate->date()); } // we start with the previous values, make sure we can add them later on MyMoneySplit s0 = sorig; s0.clearId(); // make sure we reference this account here s0.setAccountId(d->m_account.id()); // memo and number field are special: if we have multiple transactions selected // and the edit field is empty, we treat it as "not modified". // FIXME a better approach would be to have a 'dirty' flag with the widgets // which identifies if the originally loaded value has been modified // by the user KTextEdit* memo = dynamic_cast(d->m_editWidgets["memo"]); if (memo) { if (!isMultiSelection() || (isMultiSelection() && d->m_memoChanged)) s0.setMemo(memo->toPlainText()); } kMyMoneyLineEdit* number = dynamic_cast(haveWidget("number")); if (number) { if (!isMultiSelection() || (isMultiSelection() && !number->text().isEmpty())) s0.setNumber(number->text()); } KMyMoneyPayeeCombo* payee = dynamic_cast(d->m_editWidgets["payee"]); QString payeeId; if (!isMultiSelection() || (isMultiSelection() && !payee->currentText().isEmpty())) { payeeId = payee->selectedItem(); s0.setPayeeId(payeeId); } //KMyMoneyTagCombo* tag = dynamic_cast(m_editWidgets["tag"]); KTagContainer* tag = dynamic_cast(d->m_editWidgets["tag"]); if (!isMultiSelection() || (isMultiSelection() && !tag->selectedTags().isEmpty())) { s0.setTagIdList(tag->selectedTags()); } bool updateValue; MyMoneyMoney value = amountFromWidget(&updateValue); if (updateValue) { // for this account, the shares and value is the same s0.setValue(value); s0.setShares(value); } else { value = s0.value(); } // if we mark the split reconciled here, we'll use today's date if no reconciliation date is given KMyMoneyReconcileCombo* status = dynamic_cast(d->m_editWidgets["status"]); if (status->state() != eMyMoney::Split::State::Unknown) s0.setReconcileFlag(status->state()); if (s0.reconcileFlag() == eMyMoney::Split::State::Reconciled && !s0.reconcileDate().isValid()) s0.setReconcileDate(QDate::currentDate()); checkPayeeInSplit(s0, payeeId); // add the split to the transaction t.addSplit(s0); // if we have no other split we create it // if we have none or only one other split, we reconstruct it here // if we have more than one other split, we take them as they are // make sure to perform all those changes on a local copy QList splits = d->m_splits; MyMoneySplit s1; if (splits.isEmpty()) { s1.setMemo(s0.memo()); splits.append(s1); // make sure we will fill the value and share fields later on updateValue = true; } // FIXME in multiSelection we currently only support transactions with one // or two splits. So we check the original transaction and extract the other // split or create it if (isMultiSelection()) { if (torig.splitCount() == 2) { QList::const_iterator it_s; for (it_s = torig.splits().begin(); it_s != torig.splits().end(); ++it_s) { if ((*it_s).id() == sorig.id()) continue; s1 = *it_s; s1.clearId(); break; } } } else { if (splits.count() == 1) { s1 = splits[0]; s1.clearId(); } } if (isMultiSelection() || splits.count() == 1) { KMyMoneyCategory* category = dynamic_cast(d->m_editWidgets["category"]); if (!isMultiSelection() || (isMultiSelection() && !category->currentText().isEmpty())) { s1.setAccountId(category->selectedItem()); } // if the first split has a memo but the second split is empty, // we just copy the memo text over if (memo) { if (!isMultiSelection() || (isMultiSelection() && !memo->toPlainText().isEmpty())) { // if the memo is filled, we check if the // account referenced by s1 is a regular account or a category. // in case of a regular account, we just leave the memo as is // in case of a category we simply copy the new value over the old. // in case we don't even have an account id, we just skip because // the split will be removed later on anyway. if (!s1.memo().isEmpty() && s1.memo() != s0.memo()) { if (!s1.accountId().isEmpty()) { MyMoneyAccount acc = MyMoneyFile::instance()->account(s1.accountId()); if (acc.isIncomeExpense()) s1.setMemo(s0.memo()); else if (KMessageBox::questionYesNo(d->m_regForm, i18n("Do you want to replace memo

%1

with memo

%2

in the other split?", s1.memo(), s0.memo()), i18n("Copy memo"), KStandardGuiItem::yes(), KStandardGuiItem::no(), QStringLiteral("CopyMemoOver")) == KMessageBox::Yes) s1.setMemo(s0.memo()); } } else { s1.setMemo(s0.memo()); } } } if (updateValue && !s1.accountId().isEmpty()) { s1.setValue(-value); MyMoneyMoney shares; if (!skipPriceDialog) { if (!KCurrencyCalculator::setupSplitPrice(shares, t, s1, d->m_priceInfo, d->m_regForm)) return false; } else { MyMoneyAccount cat = MyMoneyFile::instance()->account(s1.accountId()); if (d->m_priceInfo.find(cat.currencyId()) != d->m_priceInfo.end()) { shares = (s1.value() * d->m_priceInfo[cat.currencyId()]).reduce().convert(cat.fraction()); } else shares = s1.value(); } s1.setShares(shares); } checkPayeeInSplit(s1, payeeId); if (!s1.accountId().isEmpty()) t.addSplit(s1); } else { QList::iterator it_s; for (it_s = splits.begin(); it_s != splits.end(); ++it_s) { s1 = *it_s; s1.clearId(); checkPayeeInSplit(s1, payeeId); t.addSplit(s1); } } return true; } void StdTransactionEditor::setupFinalWidgets() { addFinalWidget(haveWidget("deposit")); addFinalWidget(haveWidget("payment")); addFinalWidget(haveWidget("amount")); addFinalWidget(haveWidget("status")); } void StdTransactionEditor::slotUpdateAccount(const QString& id) { Q_D(StdTransactionEditor); TransactionEditor::slotUpdateAccount(id); KMyMoneyCategory* category = dynamic_cast(d->m_editWidgets["category"]); if (category && category->splitButton()) { category->splitButton()->setDisabled(id.isEmpty()); } } diff --git a/kmymoney/dialogs/transactioneditor.cpp b/kmymoney/dialogs/transactioneditor.cpp index 021f0a63f..ef85fb9a7 100644 --- a/kmymoney/dialogs/transactioneditor.cpp +++ b/kmymoney/dialogs/transactioneditor.cpp @@ -1,828 +1,825 @@ /*************************************************************************** transactioneditor.cpp ---------- begin : Wed Jun 07 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "transactioneditor.h" #include "transactioneditor_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include -#include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes -#include "kmymoneytagcombo.h" -#include "ktagcontainer.h" #include "mymoneyutils.h" #include "kmymoneycategory.h" #include "kmymoneymvccombo.h" #include "kmymoneyedit.h" #include "kmymoneylineedit.h" #include "mymoneyfile.h" #include "mymoneyprice.h" #include "mymoneysecurity.h" #include "kmymoneyutils.h" #include "kmymoneycompletion.h" #include "transactionform.h" #include "kmymoneyglobalsettings.h" #include "transactioneditorcontainer.h" #include "kcurrencycalculator.h" #include "icons.h" using namespace KMyMoneyRegister; using namespace KMyMoneyTransactionForm; using namespace Icons; TransactionEditor::TransactionEditor() : d_ptr(new TransactionEditorPrivate(this)) { Q_D(TransactionEditor); d->init(); } TransactionEditor::TransactionEditor(TransactionEditorPrivate &dd, TransactionEditorContainer* regForm, KMyMoneyRegister::Transaction* item, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) : d_ptr(&dd) // d_ptr(new TransactionEditorPrivate) { Q_D(TransactionEditor); d->m_paymentMethod = eMyMoney::Schedule::PaymentType::Any; d->m_transactions = list; d->m_regForm = regForm; d->m_item = item; d->m_transaction = item->transaction(); d->m_split = item->split(); d->m_lastPostDate = lastPostDate; d->m_initialAction = ActionNone; d->m_openEditSplits = false; d->m_memoChanged = false; d->m_item->startEditMode(); connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, this, static_cast(&TransactionEditor::slotUpdateAccount)); } TransactionEditor::TransactionEditor(TransactionEditorPrivate &dd) : d_ptr(&dd) { Q_D(TransactionEditor); d->init(); } TransactionEditor::~TransactionEditor() { Q_D(TransactionEditor); // Make sure the widgets do not send out signals to the editor anymore // After all, the editor is about to die //disconnect first tagCombo: KTagContainer *w = dynamic_cast(haveWidget("tag")); if (w && w->tagCombo()) { w->tagCombo()->disconnect(this); } QMap::iterator it_w; for (it_w = d->m_editWidgets.begin(); it_w != d->m_editWidgets.end(); ++it_w) { (*it_w)->disconnect(this); } d->m_regForm->removeEditWidgets(d->m_editWidgets); d->m_item->leaveEditMode(); emit finishEdit(d->m_transactions); } void TransactionEditor::slotUpdateAccount(const QString& id) { Q_D(TransactionEditor); d->m_account = MyMoneyFile::instance()->account(id); setupPrecision(); } void TransactionEditor::slotUpdateAccount() { Q_D(TransactionEditor); // reload m_account as it might have been changed d->m_account = MyMoneyFile::instance()->account(d->m_account.id()); setupPrecision(); } void TransactionEditor::setupPrecision() { Q_D(TransactionEditor); const int prec = (d->m_account.id().isEmpty()) ? 2 : MyMoneyMoney::denomToPrec(d->m_account.fraction()); QStringList widgets = QString("amount,deposit,payment").split(','); QStringList::const_iterator it_w; for (it_w = widgets.constBegin(); it_w != widgets.constEnd(); ++it_w) { QWidget * w; if ((w = haveWidget(*it_w)) != 0) { dynamic_cast(w)->setPrecision(prec); } } } void TransactionEditor::setup(QWidgetList& tabOrderWidgets, const MyMoneyAccount& account, KMyMoneyRegister::Action action) { Q_D(TransactionEditor); d->m_account = account; d->m_initialAction = action; createEditWidgets(); d->m_regForm->arrangeEditWidgets(d->m_editWidgets, d->m_item); d->m_regForm->tabOrder(tabOrderWidgets, d->m_item); QWidget* w = haveWidget("tabbar"); if (w) { tabOrderWidgets.append(w); TabBar* tabbar = dynamic_cast(w); if ((tabbar) && (action == KMyMoneyRegister::ActionNone)) { action = static_cast(tabbar->currentIndex()); } } loadEditWidgets(action); // remove all unused widgets and don't forget to remove them // from the tab order list as well d->m_editWidgets.removeOrphans(); QWidgetList::iterator it_w; const QWidgetList editWidgets(d->m_editWidgets.values()); for (it_w = tabOrderWidgets.begin(); it_w != tabOrderWidgets.end();) { if (editWidgets.contains(*it_w)) { ++it_w; } else { // before we remove the widget, we make sure it's not a part of a known one. // these could be a direct child in case of KMyMoneyDateInput and KMyMoneyEdit // where we store the pointer to the surrounding frame in editWidgets // or the parent is called "KMyMoneyCategoryFrame" if (*it_w) { if (editWidgets.contains((*it_w)->parentWidget()) || ((*it_w)->parentWidget() && (*it_w)->parentWidget()->objectName() == QLatin1String("KMyMoneyCategoryFrame"))) { ++it_w; } else { // qDebug("Remove '%s' from taborder", qPrintable((*it_w)->objectName())); it_w = tabOrderWidgets.erase(it_w); } } else { it_w = tabOrderWidgets.erase(it_w); } } } clearFinalWidgets(); setupFinalWidgets(); slotUpdateButtonState(); } void TransactionEditor::setup(QWidgetList& tabOrderWidgets, const MyMoneyAccount& account) { setup(tabOrderWidgets, account, KMyMoneyRegister::ActionNone); } void TransactionEditor::setup(QWidgetList& tabOrderWidgets) { setup(tabOrderWidgets, MyMoneyAccount(), KMyMoneyRegister::ActionNone); } MyMoneyAccount TransactionEditor::account() const { Q_D(const TransactionEditor); return d->m_account; } void TransactionEditor::setScheduleInfo(const QString& si) { Q_D(TransactionEditor); d->m_scheduleInfo = si; } void TransactionEditor::setPaymentMethod(eMyMoney::Schedule::PaymentType pm) { Q_D(TransactionEditor); d->m_paymentMethod = pm; } void TransactionEditor::clearFinalWidgets() { Q_D(TransactionEditor); d->m_finalEditWidgets.clear(); } void TransactionEditor::addFinalWidget(const QWidget* w) { Q_D(TransactionEditor); if (w) { d->m_finalEditWidgets << w; } } void TransactionEditor::slotReloadEditWidgets() { } bool TransactionEditor::eventFilter(QObject* o, QEvent* e) { Q_D(TransactionEditor); bool rc = false; if (o == haveWidget("number")) { if (e->type() == QEvent::MouseButtonDblClick) { emit assignNumber(); rc = true; } } // if the object is a widget, the event is a key press event and // the object is one of our edit widgets, then .... if (o->isWidgetType() && (e->type() == QEvent::KeyPress) && d->m_editWidgets.values().contains(dynamic_cast(o))) { QKeyEvent* k = dynamic_cast(e); if ((k->modifiers() & Qt::KeyboardModifierMask) == 0 || (k->modifiers() & Qt::KeypadModifier) != 0) { bool isFinal = false; QList::const_iterator it_w; switch (k->key()) { case Qt::Key_Return: case Qt::Key_Enter: // we check, if the object is one of the m_finalEditWidgets and if it's // a kMyMoneyEdit object that the value is not 0. If any of that is the // case, it's the final object. In other cases, we convert the enter // key into a TAB key to move between the fields. Of course, we only need // to do this as long as the appropriate option is set. In all other cases, // we treat the return/enter key as such. if (KMyMoneyGlobalSettings::enterMovesBetweenFields()) { for (it_w = d->m_finalEditWidgets.constBegin(); !isFinal && it_w != d->m_finalEditWidgets.constEnd(); ++it_w) { if (*it_w == o) { if (dynamic_cast(*it_w)) { isFinal = !(dynamic_cast(*it_w)->value().isZero()); } else isFinal = true; } } } else isFinal = true; // for the non-final objects, we treat the return key as a TAB if (!isFinal) { QKeyEvent evt(e->type(), Qt::Key_Tab, k->modifiers(), QString(), k->isAutoRepeat(), k->count()); QApplication::sendEvent(o, &evt); // in case of a category item and the split button is visible // send a second event so that we get passed the button. if (dynamic_cast(o) && dynamic_cast(o)->splitButton()) QApplication::sendEvent(o, &evt); } else { QTimer::singleShot(0, this, SIGNAL(returnPressed())); } // don't process any further rc = true; break; case Qt::Key_Escape: QTimer::singleShot(0, this, SIGNAL(escapePressed())); break; } } } return rc; } void TransactionEditor::slotNumberChanged(const QString& txt) { Q_D(TransactionEditor); auto next = txt; kMyMoneyLineEdit* number = dynamic_cast(haveWidget("number")); QString schedInfo; if (!d->m_scheduleInfo.isEmpty()) { schedInfo = i18n("
Processing schedule for %1.
", d->m_scheduleInfo); } while (MyMoneyFile::instance()->checkNoUsed(d->m_account.id(), next)) { if (KMessageBox::questionYesNo(d->m_regForm, QString("") + schedInfo + i18n("
Check number %1 has already been used in account %2.
" "
Do you want to replace it with the next available number?
", next, d->m_account.name()) + QString("
"), i18n("Duplicate number")) == KMessageBox::Yes) { assignNextNumber(); next = KMyMoneyUtils::nextCheckNumber(d->m_account); } else { number->setText(QString()); break; } } } void TransactionEditor::slotUpdateMemoState() { Q_D(TransactionEditor); KTextEdit* memo = dynamic_cast(d->m_editWidgets["memo"]); if (memo) { d->m_memoChanged = (memo->toPlainText() != d->m_memoText); } } void TransactionEditor::slotUpdateButtonState() { QString reason; emit transactionDataSufficient(isComplete(reason)); } QWidget* TransactionEditor::haveWidget(const QString& name) const { Q_D(const TransactionEditor); return d->m_editWidgets.haveWidget(name); } int TransactionEditor::slotEditSplits() { return QDialog::Rejected; } void TransactionEditor::setTransaction(const MyMoneyTransaction& t, const MyMoneySplit& s) { Q_D(TransactionEditor); d->m_transaction = t; d->m_split = s; loadEditWidgets(); } bool TransactionEditor::isMultiSelection() const { Q_D(const TransactionEditor); return d->m_transactions.count() > 1; } bool TransactionEditor::fixTransactionCommodity(const MyMoneyAccount& account) { Q_D(TransactionEditor); bool rc = true; bool firstTimeMultiCurrency = true; d->m_account = account; auto file = MyMoneyFile::instance(); // determine the max fraction for this account MyMoneySecurity sec = file->security(d->m_account.currencyId()); int fract = d->m_account.fraction(); // scan the list of selected transactions KMyMoneyRegister::SelectedTransactions::iterator it_t; for (it_t = d->m_transactions.begin(); (rc == true) && (it_t != d->m_transactions.end()); ++it_t) { // there was a time when the schedule editor did not setup the transaction commodity // let's give a helping hand here for those old schedules if ((*it_t).transaction().commodity().isEmpty()) (*it_t).transaction().setCommodity(d->m_account.currencyId()); // we need to check things only if a different commodity is used if (d->m_account.currencyId() != (*it_t).transaction().commodity()) { MyMoneySecurity osec = file->security((*it_t).transaction().commodity()); switch ((*it_t).transaction().splitCount()) { case 0: // new transaction, guess nothing's here yet ;) break; case 1: try { // make sure, that the value is equal to the shares, don't forget our own copy MyMoneySplit& splitB = (*it_t).split(); // reference usage wanted here if (d->m_split == splitB) d->m_split.setValue(splitB.shares()); splitB.setValue(splitB.shares()); (*it_t).transaction().modifySplit(splitB); } catch (const MyMoneyException &e) { qDebug("Unable to update commodity to second splits currency in %s: '%s'", qPrintable((*it_t).transaction().id()), qPrintable(e.what())); } break; case 2: // If we deal with multiple currencies we make sure, that for // transactions with two splits, the transaction's commodity is the // currency of the currently selected account. This saves us from a // lot of grieve later on. We just have to switch the // transactions commodity. Let's assume the following scenario: // - transactions commodity is CA // - splitB and account's currencyId is CB // - splitA is of course in CA (otherwise we have a real problem) // - Value is V in both splits // - Shares in splitB is SB // - Shares in splitA is SA (and equal to V) // // We do the following: // - change transactions commodity to CB // - set V in both splits to SB // - modify the splits in the transaction try { // retrieve the splits MyMoneySplit& splitB = (*it_t).split(); // reference usage wanted here MyMoneySplit splitA = (*it_t).transaction().splitByAccount(d->m_account.id(), false); // - set V in both splits to SB. Don't forget our own copy if (d->m_split == splitB) { d->m_split.setValue(splitB.shares()); } splitB.setValue(splitB.shares()); splitA.setValue(-splitB.shares()); (*it_t).transaction().modifySplit(splitA); (*it_t).transaction().modifySplit(splitB); } catch (const MyMoneyException &e) { qDebug("Unable to update commodity to second splits currency in %s: '%s'", qPrintable((*it_t).transaction().id()), qPrintable(e.what())); } break; default: // TODO: use new logic by adjusting all splits by the price // extracted from the selected split. Inform the user that // this will happen and allow him to stop the processing (rc = false) try { QString msg; if (firstTimeMultiCurrency) { firstTimeMultiCurrency = false; if (!isMultiSelection()) { msg = i18n("This transaction has more than two splits and is originally based on a different currency (%1). Using this account to modify the transaction may result in rounding errors. Do you want to continue?", osec.name()); } else { msg = i18n("At least one of the selected transactions has more than two splits and is originally based on a different currency (%1). Using this account to modify the transactions may result in rounding errors. Do you want to continue?", osec.name()); } if (KMessageBox::warningContinueCancel(0, QString("%1").arg(msg)) == KMessageBox::Cancel) { rc = false; } } if (rc == true) { MyMoneyMoney price; if (!(*it_t).split().shares().isZero() && !(*it_t).split().value().isZero()) price = (*it_t).split().shares() / (*it_t).split().value(); QList::iterator it_s; MyMoneySplit& mySplit = (*it_t).split(); for (it_s = (*it_t).transaction().splits().begin(); it_s != (*it_t).transaction().splits().end(); ++it_s) { MyMoneySplit s = (*it_s); if (s == mySplit) { s.setValue(s.shares()); if (mySplit == d->m_split) { d->m_split = s; } mySplit = s; } else { s.setValue((s.value() * price).convert(fract)); } (*it_t).transaction().modifySplit(s); } } } catch (const MyMoneyException &e) { qDebug("Unable to update commodity of split currency in %s: '%s'", qPrintable((*it_t).transaction().id()), qPrintable(e.what())); } break; } // set the transaction's ommodity to this account's currency (*it_t).transaction().setCommodity(d->m_account.currencyId()); // update our copy of the transaction that has the focus if ((*it_t).transaction().id() == d->m_transaction.id()) { d->m_transaction = (*it_t).transaction(); } } } return rc; } void TransactionEditor::assignNextNumber() { Q_D(TransactionEditor); if (canAssignNumber()) { kMyMoneyLineEdit* number = dynamic_cast(haveWidget("number")); QString num = KMyMoneyUtils::nextCheckNumber(d->m_account); bool showMessage = true; int rc = KMessageBox::No; QString schedInfo; if (!d->m_scheduleInfo.isEmpty()) { schedInfo = i18n("
Processing schedule for %1.
", d->m_scheduleInfo); } while (MyMoneyFile::instance()->checkNoUsed(d->m_account.id(), num)) { if (showMessage) { rc = KMessageBox::questionYesNo(d->m_regForm, QString("") + schedInfo + i18n("Check number %1 has already been used in account %2." "
Do you want to replace it with the next available number?
", num, d->m_account.name()) + QString("
"), i18n("Duplicate number")); showMessage = false; } if (rc == KMessageBox::Yes) { num = KMyMoneyUtils::nextCheckNumber(d->m_account); KMyMoneyUtils::updateLastNumberUsed(d->m_account, num); d->m_account.setValue("lastNumberUsed", num); number->loadText(num); } else { num = QString(); break; } } number->setText(num); } } bool TransactionEditor::canAssignNumber() const { auto number = dynamic_cast(haveWidget("number")); return (number != 0); } void TransactionEditor::setupCategoryWidget(KMyMoneyCategory* category, const QList& splits, QString& categoryId, const char* splitEditSlot, bool /* allowObjectCreation */) { disconnect(category, SIGNAL(focusIn()), this, splitEditSlot); #if 0 // FIXME must deal with the logic that suppressObjectCreation is // automatically turned off when the createItem() signal is connected if (allowObjectCreation) category->setSuppressObjectCreation(false); #endif switch (splits.count()) { case 0: categoryId.clear(); if (!category->currentText().isEmpty()) { // category->clearEditText(); // don't clear as could be from another widget - Bug 322768 // make sure, we don't see the selector category->completion()->hide(); } category->completion()->setSelected(QString()); break; case 1: categoryId = splits[0].accountId(); category->completion()->setSelected(categoryId); category->slotItemSelected(categoryId); break; default: categoryId.clear(); category->setSplitTransaction(); connect(category, SIGNAL(focusIn()), this, splitEditSlot); #if 0 // FIXME must deal with the logic that suppressObjectCreation is // automatically turned off when the createItem() signal is connected if (allowObjectCreation) category->setSuppressObjectCreation(true); #endif break; } } bool TransactionEditor::enterTransactions(QString& newId, bool askForSchedule, bool suppressBalanceWarnings) { Q_D(TransactionEditor); newId.clear(); auto file = MyMoneyFile::instance(); // make sure to run through all stuff that is tied to 'focusout events'. d->m_regForm->parentWidget()->setFocus(); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 10); // we don't need to update our widgets anymore, so we just disconnect the signal disconnect(file, &MyMoneyFile::dataChanged, this, &TransactionEditor::slotReloadEditWidgets); KMyMoneyRegister::SelectedTransactions::iterator it_t; MyMoneyTransaction t; bool newTransactionCreated = false; // make sure, that only a single new transaction can be created. // we need to update m_transactions to contain the new transaction // which is then stored in the variable t when we leave the loop. // m_transactions will be sent out in finishEdit() and forces // the new transaction to be selected in the ledger view // collect the transactions to be stored in the engine in a local // list first, so that the user has a chance to interrupt the storage // process QList list; auto storeTransactions = true; // collect transactions for (it_t = d->m_transactions.begin(); storeTransactions && !newTransactionCreated && it_t != d->m_transactions.end(); ++it_t) { storeTransactions = createTransaction(t, (*it_t).transaction(), (*it_t).split()); // if the transaction was created successfully, append it to the list if (storeTransactions) list.append(t); // if we created a new transaction keep that in mind if (t.id().isEmpty()) newTransactionCreated = true; } // if not interrupted by user, continue to store them in the engine if (storeTransactions) { auto i = 0; emit statusMsg(i18n("Storing transactions")); emit statusProgress(0, list.count()); MyMoneyFileTransaction ft; try { QList::iterator it_ts; QMap minBalanceEarly; QMap minBalanceAbsolute; QMap maxCreditEarly; QMap maxCreditAbsolute; QMap accountIds; for (it_ts = list.begin(); it_ts != list.end(); ++it_ts) { // if we have a categorization, make sure we remove // the 'imported' flag automagically if ((*it_ts).splitCount() > 1) (*it_ts).setImported(false); // create information about min and max balances QList::const_iterator it_s; for (it_s = (*it_ts).splits().constBegin(); it_s != (*it_ts).splits().constEnd(); ++it_s) { MyMoneyAccount acc = file->account((*it_s).accountId()); accountIds[acc.id()] = true; MyMoneyMoney balance = file->balance(acc.id()); if (!acc.value("minBalanceEarly").isEmpty()) { minBalanceEarly[acc.id()] = balance < MyMoneyMoney(acc.value("minBalanceEarly")); } if (!acc.value("minBalanceAbsolute").isEmpty()) { minBalanceAbsolute[acc.id()] = balance < MyMoneyMoney(acc.value("minBalanceAbsolute")); minBalanceEarly[acc.id()] = false; } if (!acc.value("maxCreditEarly").isEmpty()) { maxCreditEarly[acc.id()] = balance < MyMoneyMoney(acc.value("maxCreditEarly")); } if (!acc.value("maxCreditAbsolute").isEmpty()) { maxCreditAbsolute[acc.id()] = balance < MyMoneyMoney(acc.value("maxCreditAbsolute")); maxCreditEarly[acc.id()] = false; } } if ((*it_ts).id().isEmpty()) { bool enter = true; if (askForSchedule && (*it_ts).postDate() > QDate::currentDate()) { KGuiItem enterButton(i18n("&Enter"), QIcon::fromTheme(g_Icons[Icon::DialogOK]), i18n("Accepts the entered data and stores it"), i18n("Use this to enter the transaction into the ledger.")); KGuiItem scheduleButton(i18n("&Schedule"), QIcon::fromTheme(g_Icons[Icon::AppointmentNew]), i18n("Accepts the entered data and stores it as schedule"), i18n("Use this to schedule the transaction for later entry into the ledger.")); enter = KMessageBox::questionYesNo(d->m_regForm, QString("%1").arg(i18n("The transaction you are about to enter has a post date in the future.

Do you want to enter it in the ledger or add it to the schedules?")), i18nc("Dialog caption for 'Enter or schedule' dialog", "Enter or schedule?"), enterButton, scheduleButton, "EnterOrScheduleTransactionInFuture") == KMessageBox::Yes; } if (enter) { // add new transaction file->addTransaction(*it_ts); // pass the newly assigned id on to the caller newId = (*it_ts).id(); // refresh account object for transactional changes // refresh account and transaction object because they might have changed d->m_account = file->account(d->m_account.id()); t = (*it_ts); // if a new transaction has a valid number, keep it with the account d->keepNewNumber((*it_ts)); } else { // turn object creation on, so that moving the focus does // not screw up the dialog that might be popping up emit objectCreation(true); emit scheduleTransaction(*it_ts, eMyMoney::Schedule::Occurrence::Once); emit objectCreation(false); newTransactionCreated = false; } // send out the post date of this transaction emit lastPostDateUsed((*it_ts).postDate()); } else { // modify existing transaction // its number might have been edited // bearing in mind it could contain alpha characters d->keepNewNumber((*it_ts)); file->modifyTransaction(*it_ts); } } emit statusProgress(i++, 0); // update m_transactions to contain the newly created transaction so that // it is selected as the current one // we need to do that before we commit the transaction to the engine // as we need it during the update of the views that is caused by committing already. if (newTransactionCreated) { d->m_transactions.clear(); MyMoneySplit s; // a transaction w/o a single split should not exist and adding it // should throw an exception in MyMoneyFile::addTransaction, but we // remain on the save side of things to check for it if (t.splitCount() > 0) s = t.splits().front(); - KMyMoneyRegister::SelectedTransaction st(t, s, QString()); + KMyMoneyRegister::SelectedTransaction st(t, s); d->m_transactions.append(st); } // Save pricing information QList::const_iterator it_t; for (it_t = t.splits().constBegin(); it_t != t.splits().constEnd(); ++it_t) { if (((*it_t).action() != "Buy") && ((*it_t).action() != "Reinvest")) { continue; } QString id = (*it_t).accountId(); MyMoneyAccount acc = file->account(id); MyMoneySecurity sec = file->security(acc.currencyId()); MyMoneyPrice price(acc.currencyId(), sec.tradingCurrency(), t.postDate(), (*it_t).price(), "Transaction"); file->addPrice(price); break; } ft.commit(); // now analyze the balances and spit out warnings to the user QMap::const_iterator it_a; if (!suppressBalanceWarnings) { for (it_a = accountIds.constBegin(); it_a != accountIds.constEnd(); ++it_a) { QString msg; MyMoneyAccount acc = file->account(it_a.key()); MyMoneyMoney balance = file->balance(acc.id()); const MyMoneySecurity& sec = file->security(acc.currencyId()); QString key; key = "minBalanceEarly"; if (!acc.value(key).isEmpty()) { if (minBalanceEarly[acc.id()] == false && balance < MyMoneyMoney(acc.value(key))) { msg = QString("%1").arg(i18n("The balance of account %1 dropped below the warning balance of %2.", acc.name(), MyMoneyUtils::formatMoney(MyMoneyMoney(acc.value(key)), acc, sec))); } } key = "minBalanceAbsolute"; if (!acc.value(key).isEmpty()) { if (minBalanceAbsolute[acc.id()] == false && balance < MyMoneyMoney(acc.value(key))) { msg = QString("%1").arg(i18n("The balance of account %1 dropped below the minimum balance of %2.", acc.name(), MyMoneyUtils::formatMoney(MyMoneyMoney(acc.value(key)), acc, sec))); } } key = "maxCreditEarly"; if (!acc.value(key).isEmpty()) { if (maxCreditEarly[acc.id()] == false && balance < MyMoneyMoney(acc.value(key))) { msg = QString("%1").arg(i18n("The balance of account %1 dropped below the maximum credit warning limit of %2.", acc.name(), MyMoneyUtils::formatMoney(MyMoneyMoney(acc.value(key)), acc, sec))); } } key = "maxCreditAbsolute"; if (!acc.value(key).isEmpty()) { if (maxCreditAbsolute[acc.id()] == false && balance < MyMoneyMoney(acc.value(key))) { msg = QString("%1").arg(i18n("The balance of account %1 dropped below the maximum credit limit of %2.", acc.name(), MyMoneyUtils::formatMoney(MyMoneyMoney(acc.value(key)), acc, sec))); } } if (!msg.isEmpty()) { emit balanceWarning(d->m_regForm, acc, msg); } } } } catch (const MyMoneyException &e) { qDebug("Unable to store transaction within engine: %s", qPrintable(e.what())); newTransactionCreated = false; } emit statusProgress(-1, -1); emit statusMsg(QString()); } return storeTransactions; } void TransactionEditor::resizeForm() { Q_D(TransactionEditor); // force resizeing of the columns in the form auto form = dynamic_cast(d->m_regForm); if (form) { QMetaObject::invokeMethod(form, "resize", Qt::QueuedConnection, QGenericReturnArgument(), Q_ARG(int, ValueColumn1)); } } diff --git a/kmymoney/kmymoney.cpp b/kmymoney/kmymoney.cpp index b90a6a5ee..e497c5c4f 100644 --- a/kmymoney/kmymoney.cpp +++ b/kmymoney/kmymoney.cpp @@ -1,7592 +1,7592 @@ /*************************************************************************** kmymoney.cpp ------------------- copyright : (C) 2000 by Michael Edwardes (C) 2007 by Thomas Baumgart ****************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "config-kmymoney.h" #include "kmymoney.h" // ---------------------------------------------------------------------------- // Std C++ / STL Includes #include #include #include // ---------------------------------------------------------------------------- // QT Includes #include #include // only for performance tests #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KF5Holidays_FOUND #include #include #endif // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyglobalsettings.h" #include "kmymoneyadaptor.h" #include "dialogs/settings/ksettingskmymoney.h" #include "dialogs/kbackupdlg.h" #include "dialogs/kenterscheduledlg.h" #include "dialogs/kconfirmmanualenterdlg.h" #include "dialogs/kmymoneypricedlg.h" #include "dialogs/kcurrencyeditdlg.h" #include "dialogs/kequitypriceupdatedlg.h" #include "dialogs/kmymoneyfileinfodlg.h" #include "dialogs/kfindtransactiondlg.h" #include "dialogs/knewbankdlg.h" #include "wizards/newinvestmentwizard/knewinvestmentwizard.h" #include "dialogs/knewaccountdlg.h" #include "dialogs/editpersonaldatadlg.h" #include "dialogs/kselectdatabasedlg.h" #include "dialogs/kcurrencycalculator.h" #include "dialogs/keditscheduledlg.h" #include "wizards/newloanwizard/keditloanwizard.h" #include "dialogs/kpayeereassigndlg.h" #include "dialogs/ktagreassigndlg.h" #include "dialogs/kcategoryreassigndlg.h" #include "wizards/endingbalancedlg/kendingbalancedlg.h" #include "dialogs/kbalancechartdlg.h" #include "dialogs/kgeneratesqldlg.h" #include "dialogs/kloadtemplatedlg.h" #include "dialogs/kgpgkeyselectiondlg.h" #include "dialogs/ktemplateexportdlg.h" #include "dialogs/transactionmatcher.h" #include "wizards/newuserwizard/knewuserwizard.h" #include "wizards/newaccountwizard/knewaccountwizard.h" #include "dialogs/kbalancewarning.h" #include "widgets/onlinejobmessagesview.h" #include "widgets/kmymoneymvccombo.h" #include "views/kmymoneyview.h" #include "views/konlinejoboutbox.h" #include "models/onlinejobmessagesmodel.h" #include "mymoney/mymoneyobject.h" #include "mymoney/mymoneyfile.h" #include "mymoney/mymoneyinstitution.h" #include "mymoney/mymoneyaccount.h" #include "mymoney/mymoneyaccountloan.h" #include "mymoney/mymoneysecurity.h" #include "mymoney/mymoneypayee.h" #include "mymoney/mymoneytag.h" #include "mymoney/mymoneybudget.h" #include "mymoney/mymoneysplit.h" #include "mymoney/mymoneyutils.h" #include "mymoney/mymoneystatement.h" #include "mymoney/storage/mymoneystoragedump.h" #include "mymoney/storage/imymoneystorage.h" #include "mymoney/mymoneyforecast.h" #include "mymoney/onlinejobmessage.h" #include "converter/mymoneystatementreader.h" #include "converter/mymoneytemplate.h" #include "plugins/interfaces/kmmviewinterface.h" #include "plugins/interfaces/kmmstatementinterface.h" #include "plugins/interfaces/kmmimportinterface.h" #include "plugins/interfaceloader.h" #include "plugins/onlinepluginextended.h" #include "pluginloader.h" #include "tasks/credittransfer.h" #include "icons/icons.h" #include "misc/webconnect.h" #include "storage/imymoneyserialize.h" #include "storage/mymoneystoragesql.h" #include #include "transactioneditor.h" #include "konlinetransferform.h" #include #include #include "kmymoneyutils.h" #include "kcreditswindow.h" #include "ledgerdelegate.h" #include "storageenums.h" #include "mymoneyenums.h" #include "dialogenums.h" #include "misc/platformtools.h" using namespace Icons; static constexpr char recoveryKeyId[] = "59B0F826D2B08440"; // define the default period to warn about an expiring recoverkey to 30 days // but allows to override this setting during build time #ifndef RECOVER_KEY_EXPIRATION_WARNING #define RECOVER_KEY_EXPIRATION_WARNING 30 #endif const QHash KMyMoneyApp::s_Actions { {Action::FileOpenDatabase, QStringLiteral("open_database")}, {Action::FileSaveAsDatabase, QStringLiteral("saveas_database")}, {Action::FileBackup, QStringLiteral("file_backup")}, {Action::FileImportGNC, QStringLiteral("file_import_gnc")}, {Action::FileImportStatement, QStringLiteral("file_import_statement")}, {Action::FileImportTemplate, QStringLiteral("file_import_template")}, {Action::FileExportTemplate, QStringLiteral("file_export_template")}, {Action::FilePersonalData, QStringLiteral("view_personal_data")}, #ifdef KMM_DEBUG {Action::FileDump, QStringLiteral("file_dump")}, #endif {Action::FileInformation, QStringLiteral("view_file_info")}, {Action::EditFindTransaction, QStringLiteral("edit_find_transaction")}, {Action::ViewTransactionDetail, QStringLiteral("view_show_transaction_detail")}, {Action::ViewHideReconciled, QStringLiteral("view_hide_reconciled_transactions")}, {Action::ViewHideCategories, QStringLiteral("view_hide_unused_categories")}, {Action::ViewShowAll, QStringLiteral("view_show_all_accounts")}, {Action::InstitutionNew, QStringLiteral("institution_new")}, {Action::InstitutionEdit, QStringLiteral("institution_edit")}, {Action::InstitutionDelete, QStringLiteral("institution_delete")}, {Action::AccountNew, QStringLiteral("account_new")}, {Action::AccountOpen, QStringLiteral("account_open")}, {Action::AccountStartReconciliation, QStringLiteral("account_reconcile")}, {Action::AccountFinishReconciliation, QStringLiteral("account_reconcile_finish")}, {Action::AccountPostponeReconciliation, QStringLiteral("account_reconcile_postpone")}, {Action::AccountEdit, QStringLiteral("account_edit")}, {Action::AccountDelete, QStringLiteral("account_delete")}, {Action::AccountClose, QStringLiteral("account_close")}, {Action::AccountReopen, QStringLiteral("account_reopen")}, {Action::AccountTransactionReport, QStringLiteral("account_transaction_report")}, {Action::AccountBalanceChart, QStringLiteral("account_chart")}, {Action::AccountOnlineMap, QStringLiteral("account_online_map")}, {Action::AccountOnlineUnmap, QStringLiteral("account_online_unmap")}, {Action::AccountUpdateMenu, QStringLiteral("account_online_update_menu")}, {Action::AccountUpdate, QStringLiteral("account_online_update")}, {Action::AccountUpdateAll, QStringLiteral("account_online_update_all")}, {Action::AccountCreditTransfer, QStringLiteral("account_online_new_credit_transfer")}, {Action::CategoryNew, QStringLiteral("category_new")}, {Action::CategoryEdit, QStringLiteral("category_edit")}, {Action::CategoryDelete, QStringLiteral("category_delete")}, {Action::ToolCurrencies, QStringLiteral("tools_currency_editor")}, {Action::ToolPrices, QStringLiteral("tools_price_editor")}, {Action::ToolUpdatePrices, QStringLiteral("tools_update_prices")}, {Action::ToolConsistency, QStringLiteral("tools_consistency_check")}, {Action::ToolPerformance, QStringLiteral("tools_performancetest")}, {Action::ToolSQL, QStringLiteral("tools_generate_sql")}, {Action::ToolCalculator, QStringLiteral("tools_kcalc")}, {Action::SettingsAllMessages, QStringLiteral("settings_enable_messages")}, {Action::HelpShow, QStringLiteral("help_show_tip")}, {Action::TransactionNew, QStringLiteral("transaction_new")}, {Action::TransactionEdit, QStringLiteral("transaction_edit")}, {Action::TransactionEnter, QStringLiteral("transaction_enter")}, {Action::TransactionEditSplits, QStringLiteral("transaction_editsplits")}, {Action::TransactionCancel, QStringLiteral("transaction_cancel")}, {Action::TransactionDelete, QStringLiteral("transaction_delete")}, {Action::TransactionDuplicate, QStringLiteral("transaction_duplicate")}, {Action::TransactionMatch, QStringLiteral("transaction_match")}, {Action::TransactionAccept, QStringLiteral("transaction_accept")}, {Action::TransactionToggleReconciled, QStringLiteral("transaction_mark_toggle")}, {Action::TransactionToggleCleared, QStringLiteral("transaction_mark_cleared")}, {Action::TransactionReconciled, QStringLiteral("transaction_mark_reconciled")}, {Action::TransactionNotReconciled, QStringLiteral("transaction_mark_notreconciled")}, {Action::TransactionSelectAll, QStringLiteral("transaction_select_all")}, {Action::TransactionGoToAccount, QStringLiteral("transaction_goto_account")}, {Action::TransactionGoToPayee, QStringLiteral("transaction_goto_payee")}, {Action::TransactionCreateSchedule, QStringLiteral("transaction_create_schedule")}, {Action::TransactionAssignNumber, QStringLiteral("transaction_assign_number")}, {Action::TransactionCombine, QStringLiteral("transaction_combine")}, {Action::TransactionCopySplits, QStringLiteral("transaction_copy_splits")}, {Action::TransactionMoveMenu, QStringLiteral("transaction_move_menu")}, {Action::TransactionMarkMenu, QStringLiteral("transaction_mark_menu")}, {Action::TransactionContextMarkMenu, QStringLiteral("transaction_context_mark_menu")}, {Action::InvestmentNew, QStringLiteral("investment_new")}, {Action::InvestmentEdit, QStringLiteral("investment_edit")}, {Action::InvestmentDelete, QStringLiteral("investment_delete")}, {Action::InvestmentOnlinePrice, QStringLiteral("investment_online_price_update")}, {Action::InvestmentManualPrice, QStringLiteral("investment_manual_price_update")}, {Action::ScheduleNew, QStringLiteral("schedule_new")}, {Action::ScheduleEdit, QStringLiteral("schedule_edit")}, {Action::ScheduleDelete, QStringLiteral("schedule_delete")}, {Action::ScheduleDuplicate, QStringLiteral("schedule_duplicate")}, {Action::ScheduleEnter, QStringLiteral("schedule_enter")}, {Action::ScheduleSkip, QStringLiteral("schedule_skip")}, {Action::PayeeNew, QStringLiteral("payee_new")}, {Action::PayeeRename, QStringLiteral("payee_rename")}, {Action::PayeeDelete, QStringLiteral("payee_delete")}, {Action::PayeeMerge, QStringLiteral("payee_merge")}, {Action::TagNew, QStringLiteral("tag_new")}, {Action::TagRename, QStringLiteral("tag_rename")}, {Action::TagDelete, QStringLiteral("tag_delete")}, {Action::BudgetNew, QStringLiteral("budget_new")}, {Action::BudgetRename, QStringLiteral("budget_rename")}, {Action::BudgetDelete, QStringLiteral("budget_delete")}, {Action::BudgetCopy, QStringLiteral("budget_copy")}, {Action::BudgetChangeYear, QStringLiteral("budget_change_year")}, {Action::BudgetForecast, QStringLiteral("budget_forecast")}, {Action::CurrencyNew, QStringLiteral("currency_new")}, {Action::CurrencyRename, QStringLiteral("currency_rename")}, {Action::CurrencyDelete, QStringLiteral("currency_delete")}, {Action::CurrencySetBase, QStringLiteral("currency_setbase")}, {Action::PriceNew, QStringLiteral("price_new")}, {Action::PriceEdit, QStringLiteral("price_edit")}, {Action::PriceUpdate, QStringLiteral("price_update")}, {Action::PriceDelete, QStringLiteral("price_delete")}, #ifdef KMM_DEBUG {Action::WizardNewUser, QStringLiteral("new_user_wizard")}, {Action::DebugTraces, QStringLiteral("debug_traces")}, #endif {Action::DebugTimers, QStringLiteral("debug_timers")}, {Action::OnlineJobDelete, QStringLiteral("onlinejob_delete")}, {Action::OnlineJobEdit, QStringLiteral("onlinejob_edit")}, {Action::OnlineJobLog, QStringLiteral("onlinejob_log")}, }; enum backupStateE { BACKUP_IDLE = 0, BACKUP_MOUNTING, BACKUP_COPYING, BACKUP_UNMOUNTING }; class KMyMoneyApp::Private { public: Private(KMyMoneyApp *app) : q(app), m_ft(0), m_moveToAccountSelector(0), m_statementXMLindex(0), m_balanceWarning(0), m_collectingStatements(false), m_pluginLoader(0), m_backupResult(0), m_backupMount(0), m_ignoreBackupExitCode(false), m_myMoneyView(0), m_progressBar(0), m_smtReader(0), m_searchDlg(0), m_autoSaveTimer(0), m_progressTimer(0), m_inAutoSaving(false), m_transactionEditor(0), m_endingBalanceDlg(0), m_saveEncrypted(0), m_additionalKeyLabel(0), m_additionalKeyButton(0), m_recentFiles(0), #ifdef KF5Holidays_FOUND m_holidayRegion(0), #endif m_applicationIsReady(true), m_webConnect(new WebConnect(app)) { // since the days of the week are from 1 to 7, // and a day of the week is used to index this bit array, // resize the array to 8 elements (element 0 is left unused) m_processingDays.resize(8); } void closeFile(); void unlinkStatementXML(); void moveInvestmentTransaction(const QString& fromId, const QString& toId, const MyMoneyTransaction& t); QList > automaticReconciliation(const MyMoneyAccount &account, const QList > &transactions, const MyMoneyMoney &amount); /** * The public interface. */ KMyMoneyApp * const q; MyMoneyFileTransaction* m_ft; kMyMoneyAccountSelector* m_moveToAccountSelector; int m_statementXMLindex; KBalanceWarning* m_balanceWarning; bool m_collectingStatements; QStringList m_statementResults; KMyMoneyPlugin::PluginLoader* m_pluginLoader; QString m_lastPayeeEnteredId; /** the configuration object of the application */ KSharedConfigPtr m_config; /** * @brief List of all plugged plugins * * The key is the file name of the plugin. */ QMap m_plugins; /** * @brief List of plugged importer plugins * * The key is the objectName of the plugin. */ QMap m_importerPlugins; /** * @brief List of plugged online plugins * * The key is the objectName of the plugin. */ QMap m_onlinePlugins; /** * The following variable represents the state while crafting a backup. * It can have the following values * * - IDLE: the default value if not performing a backup * - MOUNTING: when a mount command has been issued * - COPYING: when a copy command has been issued * - UNMOUNTING: when an unmount command has been issued */ backupStateE m_backupState; /** * This variable keeps the result of the backup operation. */ int m_backupResult; /** * This variable is set, when the user selected to mount/unmount * the backup volume. */ bool m_backupMount; /** * Flag for internal run control */ bool m_ignoreBackupExitCode; KProcess m_proc; /// A pointer to the view holding the tabs. KMyMoneyView *m_myMoneyView; /// The URL of the file currently being edited when open. QUrl m_fileName; bool m_startDialog; QString m_mountpoint; QProgressBar* m_progressBar; QTime m_lastUpdate; QLabel* m_statusLabel; MyMoneyStatementReader* m_smtReader; // allows multiple imports to be launched trough web connect and to be executed sequentially QQueue m_importUrlsQueue; KFindTransactionDlg* m_searchDlg; QObject* m_pluginInterface; MyMoneyAccount m_selectedAccount; MyMoneyAccount m_reconciliationAccount; MyMoneyAccount m_selectedInvestment; MyMoneyInstitution m_selectedInstitution; MyMoneySchedule m_selectedSchedule; MyMoneySecurity m_selectedCurrency; MyMoneyPrice m_selectedPrice; QList m_selectedPayees; QList m_selectedTags; QList m_selectedBudgets; KMyMoneyRegister::SelectedTransactions m_selectedTransactions; // This is Auto Saving related bool m_autoSaveEnabled; QTimer* m_autoSaveTimer; QTimer* m_progressTimer; int m_autoSavePeriod; bool m_inAutoSaving; // pointer to the current transaction editor TransactionEditor* m_transactionEditor; // Reconciliation dialog KEndingBalanceDlg* m_endingBalanceDlg; // Pointer to the combo box used for key selection during // File/Save as KComboBox* m_saveEncrypted; // id's that need to be remembered QString m_accountGoto, m_payeeGoto; QStringList m_additionalGpgKeys; QLabel* m_additionalKeyLabel; QPushButton* m_additionalKeyButton; KRecentFilesAction* m_recentFiles; #ifdef KF5Holidays_FOUND // used by the calendar interface for schedules KHolidays::HolidayRegion* m_holidayRegion; #endif QBitArray m_processingDays; QMap m_holidayMap; QStringList m_consistencyCheckResult; bool m_applicationIsReady; WebConnect* m_webConnect; // methods void consistencyCheck(bool alwaysDisplayResults); static void setThemedCSS(); void copyConsistencyCheckResults(); void saveConsistencyCheckResults(); }; KMyMoneyApp::KMyMoneyApp(QWidget* parent) : KXmlGuiWindow(parent), d(new Private(this)) { #ifdef KMM_DBUS new KmymoneyAdaptor(this); QDBusConnection::sessionBus().registerObject("/KMymoney", this); QDBusConnection::sessionBus().interface()->registerService( "org.kde.kmymoney", QDBusConnectionInterface::DontQueueService); #endif // Register the main engine types used as meta-objects qRegisterMetaType("MyMoneyMoney"); qRegisterMetaType("MyMoneySecurity"); // preset the pointer because we need it during the course of this constructor kmymoney = this; d->m_config = KSharedConfig::openConfig(); d->setThemedCSS(); MyMoneyTransactionFilter::setFiscalYearStart(KMyMoneyGlobalSettings::firstFiscalMonth(), KMyMoneyGlobalSettings::firstFiscalDay()); updateCaption(true); QFrame* frame = new QFrame; frame->setFrameStyle(QFrame::NoFrame); // values for margin (11) and spacing(6) taken from KDialog implementation QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, frame); layout->setContentsMargins(2, 2, 2, 2); layout->setSpacing(6); { QString themeName = KMyMoneySettings::iconsTheme(); // get theme user wants if (!themeName.isEmpty() && themeName != QLatin1Literal("system")) // if it isn't default theme then set it QIcon::setThemeName(themeName); Icons::setIconThemeNames(QIcon::themeName()); // get whatever theme user ends up with and hope our icon names will fit that theme } initStatusBar(); initActions(); initDynamicMenus(); d->m_myMoneyView = new KMyMoneyView(this/*the global variable kmymoney is not yet assigned. So we pass it here*/); layout->addWidget(d->m_myMoneyView, 10); connect(d->m_myMoneyView, &KMyMoneyView::aboutToChangeView, this, &KMyMoneyApp::slotResetSelections); connect(d->m_myMoneyView, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), this, SLOT(slotUpdateActions())); connectActionsAndViews(); /////////////////////////////////////////////////////////////////// // call inits to invoke all other construction parts readOptions(); // now initialize the plugin structure createInterfaces(); loadPlugins(); setCentralWidget(frame); connect(&d->m_proc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotBackupHandleEvents())); // force to show the home page if the file is closed connect(actionCollection()->action(s_Actions[Action::ViewTransactionDetail]), &QAction::toggled, d->m_myMoneyView, &KMyMoneyView::slotShowTransactionDetail); d->m_backupState = BACKUP_IDLE; QLocale locale; int weekStart = locale.firstDayOfWeek(); int weekEnd = weekStart-1; if (weekEnd < Qt::Monday) { weekEnd = Qt::Sunday; } bool startFirst = (weekStart < weekEnd); for (int i = 0; i < 8; i++) { if (startFirst) d->m_processingDays.setBit(i, (i >= weekStart && i <= weekEnd)); else d->m_processingDays.setBit(i, (i >= weekStart || i <= weekEnd)); } d->m_autoSaveTimer = new QTimer(this); d->m_progressTimer = new QTimer(this); connect(d->m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); connect(d->m_progressTimer, SIGNAL(timeout()), this, SLOT(slotStatusProgressDone())); // make sure, we get a note when the engine changes state connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotDataChanged())); // connect the WebConnect server connect(d->m_webConnect, SIGNAL(gotUrl(QUrl)), this, SLOT(webConnect(QUrl))); // make sure we have a balance warning object d->m_balanceWarning = new KBalanceWarning(this); // setup the initial configuration slotUpdateConfiguration(); // kickstart date change timer slotDateChanged(); connect(this, SIGNAL(fileLoaded(QUrl)), onlineJobAdministration::instance(), SLOT(updateOnlineTaskProperties())); } KMyMoneyApp::~KMyMoneyApp() { // delete cached objects since the are in the way // when unloading the plugins onlineJobAdministration::instance()->clearCaches(); // we need to unload all plugins before we destroy anything else unloadPlugins(); delete d->m_searchDlg; delete d->m_transactionEditor; delete d->m_endingBalanceDlg; delete d->m_moveToAccountSelector; #ifdef KF5Holidays_FOUND delete d->m_holidayRegion; #endif delete d; } QUrl KMyMoneyApp::lastOpenedURL() { QUrl url = d->m_startDialog ? QUrl() : d->m_fileName; if (!url.isValid()) { url = QUrl::fromUserInput(readLastUsedFile()); } ready(); return url; } void KMyMoneyApp::slotObjectDestroyed(QObject* o) { if (o == d->m_moveToAccountSelector) { d->m_moveToAccountSelector = 0; } } void KMyMoneyApp::slotInstallConsistencyCheckContextMenu() { // this code relies on the implementation of KMessageBox::informationList to add a context menu to that list, // please adjust it if it's necessary or rewrite the way the consistency check results are displayed if (QWidget* dialog = QApplication::activeModalWidget()) { if (QListWidget* widget = dialog->findChild()) { // give the user a hint that the data can be saved widget->setToolTip(i18n("This is the consistency check log, use the context menu to copy or save it.")); widget->setWhatsThis(widget->toolTip()); widget->setContextMenuPolicy(Qt::CustomContextMenu); connect(widget, SIGNAL(customContextMenuRequested(QPoint)), SLOT(slotShowContextMenuForConsistencyCheck(QPoint))); } } } void KMyMoneyApp::slotShowContextMenuForConsistencyCheck(const QPoint &pos) { // allow the user to save the consistency check results if (QWidget* widget = qobject_cast< QWidget* >(sender())) { QMenu contextMenu(widget); QAction* copy = new QAction(i18n("Copy to clipboard"), widget); QAction* save = new QAction(i18n("Save to file"), widget); contextMenu.addAction(copy); contextMenu.addAction(save); QAction *result = contextMenu.exec(widget->mapToGlobal(pos)); if (result == copy) { // copy the consistency check results to the clipboard d->copyConsistencyCheckResults(); } else if (result == save) { // save the consistency check results to a file d->saveConsistencyCheckResults(); } } } void KMyMoneyApp::createTransactionMoveMenu() { if (!d->m_moveToAccountSelector) { QWidget* w = factory()->container("transaction_move_menu", this); QMenu *menu = dynamic_cast(w); if (menu) { QWidgetAction *accountSelectorAction = new QWidgetAction(menu); d->m_moveToAccountSelector = new kMyMoneyAccountSelector(menu, 0, false); d->m_moveToAccountSelector->setObjectName("transaction_move_menu_selector"); accountSelectorAction->setDefaultWidget(d->m_moveToAccountSelector); menu->addAction(accountSelectorAction); connect(d->m_moveToAccountSelector, SIGNAL(destroyed(QObject*)), this, SLOT(slotObjectDestroyed(QObject*))); connect(d->m_moveToAccountSelector, SIGNAL(itemSelected(QString)), this, SLOT(slotMoveToAccount(QString))); } } } void KMyMoneyApp::initDynamicMenus() { connect(this, SIGNAL(accountSelected(MyMoneyAccount)), this, SLOT(slotUpdateMoveToAccountMenu())); connect(this, SIGNAL(transactionsSelected(KMyMoneyRegister::SelectedTransactions)), this, SLOT(slotUpdateMoveToAccountMenu())); connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotUpdateMoveToAccountMenu())); } void KMyMoneyApp::initActions() { KActionCollection *aC = actionCollection(); // ************* // Adding standard actions // ************* KStandardAction::openNew(this, &KMyMoneyApp::slotFileNew, aC); KStandardAction::open(this, &KMyMoneyApp::slotFileOpen, aC); d->m_recentFiles = KStandardAction::openRecent(this, &KMyMoneyApp::slotFileOpenRecent, aC); KStandardAction::save(this, &KMyMoneyApp::slotFileSave, aC); KStandardAction::saveAs(this, &KMyMoneyApp::slotFileSaveAs, aC); KStandardAction::close(this, &KMyMoneyApp::slotFileClose, aC); KStandardAction::quit(this, &KMyMoneyApp::slotFileQuit, aC); KStandardAction::print(this, &KMyMoneyApp::slotPrintView, aC); KStandardAction::preferences(this, &KMyMoneyApp::slotSettings, aC); /* Look-up table for all custom actions. It's required for: 1) building QList with QActions to be added to ActionCollection 2) adding custom features to QActions like e.g. overlaid icon or keyboard shortcut */ QHash lutActions; // ************* // Adding all actions // ************* { // struct for QAction's vital (except icon) informations struct actionInfo { Action action; KMyMoneyAppFunc callback; QString text; Icon icon; }; const QVector actionInfos { // ************* // The File menu // ************* {Action::FileOpenDatabase, &KMyMoneyApp::slotOpenDatabase, i18n("Open database..."), Icon::SVNUpdate}, {Action::FileSaveAsDatabase, &KMyMoneyApp::slotSaveAsDatabase, i18n("Save as database..."), Icon::FileArchiver}, {Action::FileBackup, &KMyMoneyApp::slotBackupFile, i18n("Backup..."), Icon::Empty}, {Action::FileImportGNC, &KMyMoneyApp::slotGncImport, i18n("GnuCash..."), Icon::Empty}, {Action::FileImportStatement, &KMyMoneyApp::slotStatementImport, i18n("Statement file..."), Icon::Empty}, {Action::FileImportTemplate, &KMyMoneyApp::slotLoadAccountTemplates, i18n("Account Template..."), Icon::Empty}, {Action::FileExportTemplate, &KMyMoneyApp::slotSaveAccountTemplates, i18n("Account Template..."), Icon::Empty}, {Action::FilePersonalData, &KMyMoneyApp::slotFileViewPersonal, i18n("Personal Data..."), Icon::UserProperties}, #ifdef KMM_DEBUG {Action::FileDump, &KMyMoneyApp::slotFileFileInfo, i18n("Dump Memory"), Icon::Empty}, #endif {Action::FileInformation, &KMyMoneyApp::slotFileInfoDialog, i18n("File-Information..."), Icon::DocumentProperties}, // ************* // The Edit menu // ************* {Action::EditFindTransaction, &KMyMoneyApp::slotFindTransaction, i18n("Find transaction..."), Icon::Empty}, // ************* // The View menu // ************* {Action::ViewTransactionDetail, &KMyMoneyApp::slotShowTransactionDetail, i18n("Show Transaction Detail"), Icon::ViewTransactionDetail}, {Action::ViewHideReconciled, &KMyMoneyApp::slotHideReconciledTransactions, i18n("Hide reconciled transactions"), Icon::HideReconciled}, {Action::ViewHideCategories, &KMyMoneyApp::slotHideUnusedCategories, i18n("Hide unused categories"), Icon::HideCategories}, {Action::ViewShowAll, &KMyMoneyApp::slotShowAllAccounts, i18n("Show all accounts"), Icon::Empty}, // ********************* // The institutions menu // ********************* {Action::InstitutionNew, &KMyMoneyApp::slotInstitutionNew, i18n("New institution..."), Icon::Empty}, {Action::InstitutionEdit, &KMyMoneyApp::slotInstitutionEditEmpty, i18n("Edit institution..."), Icon::Empty}, {Action::InstitutionDelete, &KMyMoneyApp::slotInstitutionDelete, i18n("Delete institution..."), Icon::Empty}, // ***************** // The accounts menu // ***************** {Action::AccountNew, &KMyMoneyApp::slotAccountNew, i18n("New account..."), Icon::Empty}, {Action::AccountOpen, &KMyMoneyApp::slotAccountOpenEmpty, i18n("Open ledger"), Icon::ViewFinancialList}, {Action::AccountStartReconciliation, &KMyMoneyApp::slotAccountReconcileStart, i18n("Reconcile..."), Icon::Reconcile}, {Action::AccountFinishReconciliation, &KMyMoneyApp::slotAccountReconcileFinish, i18nc("Finish reconciliation", "Finish"), Icon::Empty}, {Action::AccountPostponeReconciliation, &KMyMoneyApp::slotAccountReconcilePostpone, i18n("Postpone reconciliation"), Icon::MediaPlaybackPause}, {Action::AccountEdit, &KMyMoneyApp::slotAccountEdit, i18n("Edit account..."), Icon::Empty}, {Action::AccountDelete, &KMyMoneyApp::slotAccountDelete, i18n("Delete account..."), Icon::Empty}, {Action::AccountClose, &KMyMoneyApp::slotAccountClose, i18n("Close account"), Icon::Empty}, {Action::AccountReopen, &KMyMoneyApp::slotAccountReopen, i18n("Reopen account"), Icon::Empty}, {Action::AccountTransactionReport, &KMyMoneyApp::slotAccountTransactionReport, i18n("Transaction report"), Icon::ViewFinancialList}, {Action::AccountBalanceChart, &KMyMoneyApp::slotAccountChart, i18n("Show balance chart..."), Icon::OfficeChartLine}, {Action::AccountOnlineMap, &KMyMoneyApp::slotAccountMapOnline, i18n("Map account..."), Icon::NewsSubscribe}, {Action::AccountOnlineUnmap, &KMyMoneyApp::slotAccountUnmapOnline, i18n("Unmap account..."), Icon::NewsUnsubscribe}, {Action::AccountUpdateMenu, &KMyMoneyApp::slotAccountUpdateOnline, i18nc("Update online accounts menu", "Update"), Icon::Empty}, {Action::AccountUpdate, &KMyMoneyApp::slotAccountUpdateOnline, i18n("Update account..."), Icon::Empty}, {Action::AccountUpdateAll, &KMyMoneyApp::slotAccountUpdateOnlineAll, i18n("Update all accounts..."), Icon::Empty}, {Action::AccountCreditTransfer, &KMyMoneyApp::slotNewOnlineTransfer, i18n("New credit transfer"), Icon::Empty}, // ******************* // The categories menu // ******************* {Action::CategoryNew, &KMyMoneyApp::slotCategoryNew, i18n("New category..."), Icon::Empty}, {Action::CategoryEdit, &KMyMoneyApp::slotAccountEdit, i18n("Edit category..."), Icon::Empty}, {Action::CategoryDelete, &KMyMoneyApp::slotAccountDelete, i18n("Delete category..."), Icon::Empty}, // ************** // The tools menu // ************** {Action::ToolCurrencies, &KMyMoneyApp::slotCurrencyDialog, i18n("Currencies..."), Icon::ViewCurrencyList}, {Action::ToolPrices, &KMyMoneyApp::slotPriceDialog, i18n("Prices..."), Icon::Empty}, {Action::ToolUpdatePrices, &KMyMoneyApp::slotEquityPriceUpdate, i18n("Update Stock and Currency Prices..."), Icon::Empty}, {Action::ToolConsistency, &KMyMoneyApp::slotFileConsistencyCheck, i18n("Consistency Check"), Icon::Empty}, {Action::ToolPerformance, &KMyMoneyApp::slotPerformanceTest, i18n("Performance-Test"), Icon::Fork}, {Action::ToolSQL, &KMyMoneyApp::slotGenerateSql, i18n("Generate Database SQL"), Icon::Empty}, {Action::ToolCalculator, &KMyMoneyApp::slotToolsStartKCalc, i18n("Calculator..."), Icon::AccessoriesCalculator}, // ***************** // The settings menu // ***************** {Action::SettingsAllMessages, &KMyMoneyApp::slotEnableMessages, i18n("Enable all messages"), Icon::Empty}, // ************* // The help menu // ************* {Action::HelpShow, &KMyMoneyApp::slotShowTipOfTheDay, i18n("&Show tip of the day"), Icon::Tip}, // *************************** // Actions w/o main menu entry // *************************** {Action::TransactionNew, &KMyMoneyApp::slotTransactionsNew, i18nc("New transaction button", "New"), Icon::Empty}, {Action::TransactionEdit, &KMyMoneyApp::slotTransactionsEdit, i18nc("Edit transaction button", "Edit"), Icon::Empty}, {Action::TransactionEnter, &KMyMoneyApp::slotTransactionsEnter, i18nc("Enter transaction", "Enter"), Icon::DialogOK}, {Action::TransactionEditSplits, &KMyMoneyApp::slotTransactionsEditSplits, i18nc("Edit split button", "Edit splits"), Icon::Split}, {Action::TransactionCancel, &KMyMoneyApp::slotTransactionsCancel, i18nc("Cancel transaction edit", "Cancel"), Icon::DialogCancel}, {Action::TransactionDelete, &KMyMoneyApp::slotTransactionsDelete, i18nc("Delete transaction", "Delete"), Icon::EditDelete}, {Action::TransactionDuplicate, &KMyMoneyApp::slotTransactionDuplicate, i18nc("Duplicate transaction", "Duplicate"), Icon::EditCopy}, {Action::TransactionMatch, &KMyMoneyApp::slotTransactionMatch, i18nc("Button text for match transaction", "Match"),Icon::Empty}, {Action::TransactionAccept, &KMyMoneyApp::slotTransactionsAccept, i18nc("Accept 'imported' and 'matched' transaction", "Accept"), Icon::Empty}, {Action::TransactionToggleReconciled, &KMyMoneyApp::slotToggleReconciliationFlag, i18nc("Toggle reconciliation flag", "Toggle"), Icon::Empty}, {Action::TransactionToggleCleared, &KMyMoneyApp::slotMarkTransactionCleared, i18nc("Mark transaction cleared", "Cleared"), Icon::Empty}, {Action::TransactionReconciled, &KMyMoneyApp::slotMarkTransactionReconciled, i18nc("Mark transaction reconciled", "Reconciled"), Icon::Empty}, {Action::TransactionNotReconciled, &KMyMoneyApp::slotMarkTransactionNotReconciled, i18nc("Mark transaction not reconciled", "Not reconciled"), Icon::Empty}, {Action::TransactionSelectAll, &KMyMoneyApp::selectAllTransactions, i18nc("Select all transactions", "Select all"), Icon::Empty}, {Action::TransactionGoToAccount, &KMyMoneyApp::slotTransactionGotoAccount, i18n("Go to account"), Icon::GoJump}, {Action::TransactionGoToPayee, &KMyMoneyApp::slotTransactionGotoPayee, i18n("Go to payee"), Icon::GoJump}, {Action::TransactionCreateSchedule, &KMyMoneyApp::slotTransactionCreateSchedule, i18n("Create scheduled transaction..."), Icon::AppointmentNew}, {Action::TransactionAssignNumber, &KMyMoneyApp::slotTransactionAssignNumber, i18n("Assign next number"), Icon::Empty}, {Action::TransactionCombine, &KMyMoneyApp::slotTransactionCombine, i18nc("Combine transactions", "Combine"), Icon::Empty}, {Action::TransactionCopySplits, &KMyMoneyApp::slotTransactionCopySplits, i18n("Copy splits"), Icon::Empty}, //Investment {Action::InvestmentNew, &KMyMoneyApp::slotInvestmentNew, i18n("New investment..."), Icon::Empty}, {Action::InvestmentEdit, &KMyMoneyApp::slotInvestmentEdit, i18n("Edit investment..."), Icon::Empty}, {Action::InvestmentDelete, &KMyMoneyApp::slotInvestmentDelete, i18n("Delete investment..."), Icon::Empty}, {Action::InvestmentOnlinePrice, &KMyMoneyApp::slotOnlinePriceUpdate, i18n("Online price update..."), Icon::Empty}, {Action::InvestmentManualPrice, &KMyMoneyApp::slotManualPriceUpdate, i18n("Manual price update..."), Icon::Empty}, //Schedule {Action::ScheduleNew, &KMyMoneyApp::slotScheduleNew, i18n("New scheduled transaction"), Icon::AppointmentNew}, {Action::ScheduleEdit, &KMyMoneyApp::slotScheduleEdit, i18n("Edit scheduled transaction"), Icon::DocumentEdit}, {Action::ScheduleDelete, &KMyMoneyApp::slotScheduleDelete, i18n("Delete scheduled transaction"), Icon::EditDelete}, {Action::ScheduleDuplicate, &KMyMoneyApp::slotScheduleDuplicate, i18n("Duplicate scheduled transaction"), Icon::EditCopy}, {Action::ScheduleEnter, &KMyMoneyApp::slotScheduleEnter, i18n("Enter next transaction..."), Icon::KeyEnter}, {Action::ScheduleSkip, &KMyMoneyApp::slotScheduleSkip, i18n("Skip next transaction..."), Icon::MediaSeekForward}, //Payees {Action::PayeeNew, &KMyMoneyApp::slotPayeeNew, i18n("New payee"), Icon::ListAddUser}, {Action::PayeeRename, &KMyMoneyApp::payeeRename, i18n("Rename payee"), Icon::PayeeRename}, {Action::PayeeDelete, &KMyMoneyApp::slotPayeeDelete, i18n("Delete payee"), Icon::ListRemoveUser}, {Action::PayeeMerge, &KMyMoneyApp::slotPayeeMerge, i18n("Merge payees"), Icon::PayeeMerge}, //Tags {Action::TagNew, &KMyMoneyApp::slotTagNew, i18n("New tag"), Icon::ListAddTag}, {Action::TagRename, &KMyMoneyApp::slotTagRename, i18n("Rename tag"), Icon::TagRename}, {Action::TagDelete, &KMyMoneyApp::slotTagDelete, i18n("Delete tag"), Icon::ListRemoveTag}, //Budget {Action::BudgetNew, &KMyMoneyApp::slotBudgetNew, i18n("New budget"), Icon::Empty}, {Action::BudgetRename, &KMyMoneyApp::budgetRename, i18n("Rename budget"), Icon::Empty}, {Action::BudgetDelete, &KMyMoneyApp::slotBudgetDelete, i18n("Delete budget"), Icon::Empty}, {Action::BudgetCopy, &KMyMoneyApp::slotBudgetCopy, i18n("Copy budget"), Icon::Empty}, {Action::BudgetChangeYear, &KMyMoneyApp::slotBudgetChangeYear, i18n("Change budget year"), Icon::ViewCalendar}, {Action::BudgetForecast, &KMyMoneyApp::slotBudgetForecast, i18n("Budget based on forecast"), Icon::ViewForecast}, //Currency actions {Action::CurrencyNew, &KMyMoneyApp::slotCurrencyNew, i18n("New currency"), Icon::Empty}, {Action::CurrencyRename, &KMyMoneyApp::currencyRename, i18n("Rename currency"), Icon::EditRename}, {Action::CurrencyDelete, &KMyMoneyApp::slotCurrencyDelete, i18n("Delete currency"), Icon::EditDelete}, {Action::CurrencySetBase, &KMyMoneyApp::slotCurrencySetBase, i18n("Select as base currency"), Icon::KMyMoney}, //Price actions {Action::PriceNew, &KMyMoneyApp::priceNew, i18n("New price..."), Icon::DocumentNew}, {Action::PriceEdit, &KMyMoneyApp::priceEdit, i18n("Edit price..."), Icon::DocumentEdit}, {Action::PriceUpdate, &KMyMoneyApp::priceOnlineUpdate, i18n("Online Price Update..."), Icon::Empty}, {Action::PriceDelete, &KMyMoneyApp::priceDelete, i18n("Delete price..."), Icon::EditDelete}, //debug actions #ifdef KMM_DEBUG {Action::WizardNewUser, &KMyMoneyApp::slotNewFeature, i18n("Test new feature"), Icon::Empty}, {Action::DebugTraces, &KMyMoneyApp::slotToggleTraces, i18n("Debug Traces"), Icon::Empty}, #endif {Action::DebugTimers, &KMyMoneyApp::slotToggleTimers, i18n("Debug Timers"), Icon::Empty}, // onlineJob actions {Action::OnlineJobDelete, &KMyMoneyApp::slotRemoveJob, i18n("Remove credit transfer"), Icon::EditDelete}, {Action::OnlineJobEdit, &KMyMoneyApp::slotEditJob, i18n("Edit credit transfer"), Icon::DocumentEdit}, {Action::OnlineJobLog, &KMyMoneyApp::slotOnlineJobLog, i18n("Show log"), Icon::Empty}, }; foreach (const auto info, actionInfos) { QAction *a = new QAction(0); // KActionCollection::addAction by name sets object name anyways, // so, as better alternative, set it here right from the start a->setObjectName(s_Actions.value(info.action)); a->setText(info.text); if (info.icon != Icon::Empty) // no need to set empty icon a->setIcon(QIcon::fromTheme(g_Icons.value(info.icon))); connect(a, &QAction::triggered, this, info.callback); lutActions.insert(info.action, a); // store QAction's pointer for later processing } } // ************* // Setting some of added actions checkable // ************* { // Some of acitions schould be checkable, // so set them here const QVector checkableActions { Action::ViewTransactionDetail, Action::ViewHideReconciled, Action::ViewHideCategories, #ifdef KMM_DEBUG Action::DebugTraces, #endif Action::ViewShowAll }; foreach (const auto it, checkableActions) lutActions[it]->setCheckable(true); } // ************* // Setting custom icons for some of added actions // ************* { // struct for QAction's icon overlays struct iconOverlayInfo { Action action; Icon icon; Icon overlay; Qt::Corner corner; }; const QVector actionOverlaidIcons { {Action::EditFindTransaction, Icon::ViewFinancialTransfer, Icon::EditFind, Qt::BottomRightCorner}, {Action::InstitutionNew, Icon::ViewBank, Icon::ListAdd, Qt::BottomRightCorner}, {Action::InstitutionEdit, Icon::ViewBank, Icon::DocumentEdit, Qt::BottomRightCorner}, {Action::InstitutionDelete, Icon::ViewBank, Icon::EditDelete, Qt::BottomRightCorner}, {Action::AccountNew, Icon::ViewBankAccount, Icon::ListAdd, Qt::TopRightCorner}, {Action::AccountFinishReconciliation, Icon::Merge, Icon::DialogOK, Qt::BottomRightCorner}, {Action::AccountEdit, Icon::ViewBankAccount, Icon::DocumentEdit, Qt::BottomRightCorner}, {Action::AccountDelete, Icon::ViewBankAccount, Icon::EditDelete, Qt::BottomRightCorner}, {Action::AccountClose, Icon::ViewBankAccount, Icon::DialogClose, Qt::BottomRightCorner}, {Action::AccountReopen, Icon::ViewBankAccount, Icon::DialogOK, Qt::BottomRightCorner}, {Action::AccountUpdateMenu, Icon::ViewBankAccount, Icon::Download, Qt::BottomRightCorner}, {Action::AccountUpdate, Icon::ViewBankAccount, Icon::Download, Qt::BottomRightCorner}, {Action::AccountUpdateAll, Icon::ViewBankAccount, Icon::Download, Qt::BottomRightCorner}, {Action::AccountCreditTransfer, Icon::ViewBankAccount, Icon::MailMessageNew, Qt::BottomRightCorner}, {Action::CategoryNew, Icon::ViewFinancialCategories, Icon::ListAdd, Qt::TopRightCorner}, {Action::CategoryEdit, Icon::ViewFinancialCategories, Icon::DocumentEdit, Qt::BottomRightCorner}, {Action::CategoryDelete, Icon::ViewFinancialCategories, Icon::EditDelete, Qt::BottomRightCorner}, {Action::ToolUpdatePrices, Icon::ViewInvestment, Icon::Download, Qt::BottomRightCorner}, {Action::TransactionNew, Icon::ViewFinancialTransfer, Icon::ListAdd, Qt::TopRightCorner}, {Action::TransactionEdit, Icon::ViewFinancialTransfer, Icon::DocumentEdit, Qt::BottomRightCorner}, {Action::TransactionMatch, Icon::ViewFinancialTransfer, Icon::DocumentImport, Qt::BottomRightCorner}, {Action::TransactionAccept, Icon::ViewFinancialTransfer, Icon::DialogOKApply, Qt::BottomRightCorner}, {Action::InvestmentNew, Icon::ViewInvestment, Icon::ListAdd, Qt::TopRightCorner}, {Action::InvestmentEdit, Icon::ViewInvestment, Icon::DocumentEdit, Qt::BottomRightCorner}, {Action::InvestmentDelete, Icon::ViewInvestment, Icon::EditDelete, Qt::BottomRightCorner}, {Action::InvestmentOnlinePrice, Icon::ViewInvestment, Icon::Download, Qt::BottomRightCorner}, {Action::BudgetNew, Icon::ViewTimeScheduleCalculus, Icon::ListAdd, Qt::TopRightCorner}, {Action::BudgetRename, Icon::ViewTimeScheduleCalculus, Icon::DocumentEdit, Qt::BottomRightCorner}, {Action::BudgetDelete, Icon::ViewTimeScheduleCalculus, Icon::EditDelete, Qt::BottomRightCorner}, {Action::BudgetCopy, Icon::ViewTimeScheduleCalculus, Icon::EditCopy, Qt::BottomRightCorner}, {Action::PriceUpdate, Icon::ViewCurrencyList, Icon::Download, Qt::BottomRightCorner} }; for(const auto& it : actionOverlaidIcons) lutActions[it.action]->setIcon(KMyMoneyUtils::overlayIcon(g_Icons[it.icon], g_Icons[it.overlay], it.corner)); } // ************* // Setting keyboard shortcuts for some of added actions // ************* { const QVector> actionShortcuts { {qMakePair(Action::EditFindTransaction, Qt::CTRL + Qt::Key_F)}, {qMakePair(Action::ViewTransactionDetail, Qt::CTRL + Qt::Key_T)}, {qMakePair(Action::ViewHideReconciled, Qt::CTRL + Qt::Key_R)}, {qMakePair(Action::ViewHideCategories, Qt::CTRL + Qt::Key_U)}, {qMakePair(Action::ViewShowAll, Qt::CTRL + Qt::SHIFT + Qt::Key_A)}, {qMakePair(Action::AccountStartReconciliation, Qt::CTRL + Qt::SHIFT + Qt::Key_R)}, {qMakePair(Action::TransactionNew, Qt::CTRL + Qt::Key_Insert)}, {qMakePair(Action::TransactionToggleReconciled, Qt::CTRL + Qt::Key_Space)}, {qMakePair(Action::TransactionToggleCleared, Qt::CTRL + Qt::Key_Alt + Qt::Key_Space)}, {qMakePair(Action::TransactionReconciled, Qt::CTRL + Qt::Key_Shift + Qt::Key_Space)}, {qMakePair(Action::TransactionSelectAll, Qt::CTRL + Qt::Key_A)}, #ifdef KMM_DEBUG {qMakePair(Action::WizardNewUser, Qt::CTRL + Qt::Key_G)}, #endif {qMakePair(Action::TransactionAssignNumber, Qt::CTRL + Qt::Key_Shift + Qt::Key_N)} }; for(const auto& it : actionShortcuts) aC->setDefaultShortcut(lutActions[it.first], it.second); } // ************* // Misc settings // ************* connect(onlineJobAdministration::instance(), &onlineJobAdministration::canSendCreditTransferChanged, lutActions.value(Action::AccountCreditTransfer), &QAction::setEnabled); // Setup transaction detail switch lutActions[Action::ViewTransactionDetail]->setChecked(KMyMoneyGlobalSettings::showRegisterDetailed()); lutActions[Action::ViewHideReconciled]->setChecked(KMyMoneyGlobalSettings::hideReconciledTransactions()); lutActions[Action::ViewHideCategories]->setChecked(KMyMoneyGlobalSettings::hideUnusedCategory()); lutActions[Action::ViewShowAll]->setChecked(false); // ************* // Adding actions to ActionCollection // ************* actionCollection()->addActions(lutActions.values()); // ************************ // Currently unused actions // ************************ #if 0 new KToolBarPopupAction(i18n("View back"), "go-previous", 0, this, SLOT(slotShowPreviousView()), actionCollection(), "go_back"); new KToolBarPopupAction(i18n("View forward"), "go-next", 0, this, SLOT(slotShowNextView()), actionCollection(), "go_forward"); action("go_back")->setEnabled(false); action("go_forward")->setEnabled(false); #endif // use the absolute path to your kmymoneyui.rc file for testing purpose in createGUI(); setupGUI(); // reconnect about app entry to dialog with full credits information QAction *aboutApp = aC->action(QString::fromLatin1(KStandardAction::name(KStandardAction::AboutApp))); aboutApp->disconnect(); connect(aboutApp, &QAction::triggered, this, &KMyMoneyApp::slotShowCredits); QMenu *menuContainer; menuContainer = static_cast(factory()->container(QStringLiteral("import"), this)); menuContainer->setIcon(QIcon::fromTheme(g_Icons[Icon::DocumentImport])); menuContainer = static_cast(factory()->container(QStringLiteral("export"), this)); menuContainer->setIcon(QIcon::fromTheme(g_Icons[Icon::DocumentExport])); } void KMyMoneyApp::connectActionsAndViews() { KOnlineJobOutbox *const outbox = d->m_myMoneyView->getOnlineJobOutbox(); Q_CHECK_PTR(outbox); QAction *const onlineJob_delete = actionCollection()->action(s_Actions[Action::OnlineJobDelete]); Q_CHECK_PTR(onlineJob_delete); connect(onlineJob_delete, SIGNAL(triggered()), outbox, SLOT(slotRemoveJob())); QAction *const onlineJob_edit = actionCollection()->action(s_Actions[Action::OnlineJobEdit]); Q_CHECK_PTR(onlineJob_edit); connect(onlineJob_edit, SIGNAL(triggered()), outbox, SLOT(slotEditJob())); } #ifdef KMM_DEBUG void KMyMoneyApp::dumpActions() const { const QList list = actionCollection()->actions(); foreach (const auto it, list) std::cout << qPrintable(it->objectName()) << ": " << qPrintable(it->text()) << std::endl; } #endif bool KMyMoneyApp::isActionToggled(const Action _a) { return actionCollection()->action(s_Actions[_a])->isChecked(); } void KMyMoneyApp::initStatusBar() { /////////////////////////////////////////////////////////////////// // STATUSBAR d->m_statusLabel = new QLabel; statusBar()->addWidget(d->m_statusLabel); ready(); // Initialization of progress bar taken from KDevelop ;-) d->m_progressBar = new QProgressBar; statusBar()->addWidget(d->m_progressBar); d->m_progressBar->setFixedHeight(d->m_progressBar->sizeHint().height() - 8); // hide the progress bar for now slotStatusProgressBar(-1, -1); } void KMyMoneyApp::saveOptions() { KConfigGroup grp = d->m_config->group("General Options"); grp.writeEntry("Geometry", size()); grp.writeEntry("Show Statusbar", actionCollection()->action(KStandardAction::name(KStandardAction::ShowStatusbar))->isChecked()); KConfigGroup toolbarGrp = d->m_config->group("mainToolBar"); toolBar("mainToolBar")->saveSettings(toolbarGrp); d->m_recentFiles->saveEntries(d->m_config->group("Recent Files")); } void KMyMoneyApp::readOptions() { KConfigGroup grp = d->m_config->group("General Options"); actionCollection()->action(s_Actions[Action::ViewHideReconciled])->setChecked(KMyMoneyGlobalSettings::hideReconciledTransactions()); actionCollection()->action(s_Actions[Action::ViewHideCategories])->setChecked(KMyMoneyGlobalSettings::hideUnusedCategory()); d->m_recentFiles->loadEntries(d->m_config->group("Recent Files")); // Startdialog is written in the settings dialog d->m_startDialog = grp.readEntry("StartDialog", true); } void KMyMoneyApp::resizeEvent(QResizeEvent* ev) { KMainWindow::resizeEvent(ev); updateCaption(true); } int KMyMoneyApp::askSaveOnClose() { int ans; if (KMyMoneyGlobalSettings::autoSaveOnClose()) { ans = KMessageBox::Yes; } else { ans = KMessageBox::warningYesNoCancel(this, i18n("The file has been changed, save it?")); } return ans; } bool KMyMoneyApp::queryClose() { if (!isReady()) return false; if (d->m_myMoneyView->dirty()) { int ans = askSaveOnClose(); if (ans == KMessageBox::Cancel) return false; else if (ans == KMessageBox::Yes) { bool saved = slotFileSave(); saveOptions(); return saved; } } if (d->m_myMoneyView->isDatabase()) slotFileClose(); // close off the database saveOptions(); return true; } ///////////////////////////////////////////////////////////////////// // SLOT IMPLEMENTATION ///////////////////////////////////////////////////////////////////// void KMyMoneyApp::slotFileInfoDialog() { QPointer dlg = new KMyMoneyFileInfoDlg(0); dlg->exec(); delete dlg; } void KMyMoneyApp::slotPerformanceTest() { // dump performance report to stderr int measurement[2]; QTime timer; MyMoneyAccount acc; qDebug("--- Starting performance tests ---"); // AccountList MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; timer.start(); for (int i = 0; i < 1000; ++i) { QList list; MyMoneyFile::instance()->accountList(list); measurement[i != 0] = timer.elapsed(); } std::cerr << "accountList()" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Total time: " << (measurement[0] + measurement[1]) << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 1000 << " msec" << std::endl; // Balance of asset account(s) MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; acc = MyMoneyFile::instance()->asset(); for (int i = 0; i < 1000; ++i) { timer.start(); MyMoneyMoney result = MyMoneyFile::instance()->balance(acc.id()); measurement[i != 0] += timer.elapsed(); } std::cerr << "balance(Asset)" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 1000 << " msec" << std::endl; // total balance of asset account MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; acc = MyMoneyFile::instance()->asset(); for (int i = 0; i < 1000; ++i) { timer.start(); MyMoneyMoney result = MyMoneyFile::instance()->totalBalance(acc.id()); measurement[i != 0] += timer.elapsed(); } std::cerr << "totalBalance(Asset)" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 1000 << " msec" << std::endl; // Balance of expense account(s) MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; acc = MyMoneyFile::instance()->expense(); for (int i = 0; i < 1000; ++i) { timer.start(); MyMoneyMoney result = MyMoneyFile::instance()->balance(acc.id()); measurement[i != 0] += timer.elapsed(); } std::cerr << "balance(Expense)" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 1000 << " msec" << std::endl; // total balance of expense account MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; acc = MyMoneyFile::instance()->expense(); timer.start(); for (int i = 0; i < 1000; ++i) { MyMoneyMoney result = MyMoneyFile::instance()->totalBalance(acc.id()); measurement[i != 0] = timer.elapsed(); } std::cerr << "totalBalance(Expense)" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Total time: " << (measurement[0] + measurement[1]) << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 1000 << " msec" << std::endl; // transaction list MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; if (MyMoneyFile::instance()->asset().accountCount()) { MyMoneyTransactionFilter filter(MyMoneyFile::instance()->asset().accountList()[0]); filter.setDateFilter(QDate(), QDate::currentDate()); QList list; timer.start(); for (int i = 0; i < 100; ++i) { list = MyMoneyFile::instance()->transactionList(filter); measurement[i != 0] = timer.elapsed(); } std::cerr << "transactionList()" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Total time: " << (measurement[0] + measurement[1]) << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 100 << " msec" << std::endl; } // transaction list MyMoneyFile::instance()->preloadCache(); measurement[0] = measurement[1] = 0; if (MyMoneyFile::instance()->asset().accountCount()) { MyMoneyTransactionFilter filter(MyMoneyFile::instance()->asset().accountList()[0]); filter.setDateFilter(QDate(), QDate::currentDate()); QList list; timer.start(); for (int i = 0; i < 100; ++i) { MyMoneyFile::instance()->transactionList(list, filter); measurement[i != 0] = timer.elapsed(); } std::cerr << "transactionList(list)" << std::endl; std::cerr << "First time: " << measurement[0] << " msec" << std::endl; std::cerr << "Total time: " << (measurement[0] + measurement[1]) << " msec" << std::endl; std::cerr << "Average : " << (measurement[0] + measurement[1]) / 100 << " msec" << std::endl; } MyMoneyFile::instance()->preloadCache(); } void KMyMoneyApp::slotFileNew() { KMSTATUS(i18n("Creating new document...")); slotFileClose(); if (!d->m_myMoneyView->fileOpen()) { // next line required until we move all file handling out of KMyMoneyView d->m_myMoneyView->newFile(); d->m_fileName = QUrl(); updateCaption(); NewUserWizard::Wizard *wizard = new NewUserWizard::Wizard(); if (wizard->exec() == QDialog::Accepted) { MyMoneyFileTransaction ft; MyMoneyFile* file = MyMoneyFile::instance(); try { // store the user info file->setUser(wizard->user()); // create and setup base currency file->addCurrency(wizard->baseCurrency()); file->setBaseCurrency(wizard->baseCurrency()); // create a possible institution MyMoneyInstitution inst = wizard->institution(); if (inst.name().length()) { file->addInstitution(inst); } // create a possible checking account MyMoneyAccount acc = wizard->account(); if (acc.name().length()) { acc.setInstitutionId(inst.id()); MyMoneyAccount asset = file->asset(); file->addAccount(acc, asset); // create possible opening balance transaction if (!wizard->openingBalance().isZero()) { file->createOpeningBalanceTransaction(acc, wizard->openingBalance()); } } // import the account templates QList templates = wizard->templates(); QList::iterator it_t; for (it_t = templates.begin(); it_t != templates.end(); ++it_t) { (*it_t).importTemplate(&progressCallback); } d->m_fileName = wizard->url(); ft.commit(); KMyMoneyGlobalSettings::setFirstTimeRun(false); // FIXME This is a bit clumsy. We re-read the freshly // created file to be able to run through all the // fixup logic and then save it to keep the modified // flag off. slotFileSave(); d->m_myMoneyView->readFile(d->m_fileName); slotFileSave(); // now keep the filename in the recent files used list //KRecentFilesAction *p = dynamic_cast(action(KStandardAction::name(KStandardAction::OpenRecent))); //if(p) d->m_recentFiles->addUrl(d->m_fileName); writeLastUsedFile(d->m_fileName.url()); } catch (const MyMoneyException &) { // next line required until we move all file handling out of KMyMoneyView d->m_myMoneyView->closeFile(); } if (wizard->startSettingsAfterFinished()) slotSettings(); } else { // next line required until we move all file handling out of KMyMoneyView d->m_myMoneyView->closeFile(); } delete wizard; updateCaption(); emit fileLoaded(d->m_fileName); } } QUrl KMyMoneyApp::selectFile(const QString& title, const QString& _path, const QString& mask, QFileDialog::FileMode mode, QWidget* widget) { QString path(_path); // if the path is not specified open the file dialog in the last used directory // 'kmymoney' is the keyword that identifies the last used directory in KFileDialog if (path.isEmpty()) { path = KRecentDirs::dir(":kmymoney-import"); } QPointer dialog = new QFileDialog(this, title, path, mask); dialog->setFileMode(mode); QUrl url; if (dialog->exec() == QDialog::Accepted && dialog != 0) { QList selectedUrls = dialog->selectedUrls(); if (!selectedUrls.isEmpty()) { url = selectedUrls.first(); if (_path.isEmpty()) { KRecentDirs::add(":kmymoney-import", url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path()); } } } // in case we have an additional widget, we remove it from the // dialog, so that the caller can still access it. Therefore, it is // the callers responsibility to delete the object if (widget) widget->setParent(0); delete dialog; return url; } // General open void KMyMoneyApp::slotFileOpen() { KMSTATUS(i18n("Open a file.")); QString prevDir = readLastUsedDir(); QPointer dialog = new QFileDialog(this, QString(), prevDir, i18n("KMyMoney files (*.kmy *.xml);;All files")); dialog->setFileMode(QFileDialog::ExistingFile); dialog->setAcceptMode(QFileDialog::AcceptOpen); if (dialog->exec() == QDialog::Accepted && dialog != nullptr) { slotFileOpenRecent(dialog->selectedUrls().first()); } delete dialog; } void KMyMoneyApp::slotOpenDatabase() { KMSTATUS(i18n("Open a file.")); QPointer dialog = new KSelectDatabaseDlg(QIODevice::ReadWrite); if (!dialog->checkDrivers()) { delete dialog; return; } if (dialog->exec() == QDialog::Accepted && dialog != 0) { slotFileOpenRecent(dialog->selectedURL()); } delete dialog; } bool KMyMoneyApp::isImportableFile(const QUrl &url) { bool result = false; // Iterate through the plugins and see if there's a loaded plugin who can handle it QMap::const_iterator it_plugin = d->m_importerPlugins.constBegin(); while (it_plugin != d->m_importerPlugins.constEnd()) { if ((*it_plugin)->isMyFormat(url.path())) { result = true; break; } ++it_plugin; } // If we did not find a match, try importing it as a KMM statement file, // which is really just for testing. the statement file is not exposed // to users. if (it_plugin == d->m_importerPlugins.constEnd()) if (MyMoneyStatement::isStatementFile(url.path())) result = true; // Place code here to test for QIF and other locally-supported formats // (i.e. not a plugin). If you add them here, be sure to add it to // the webConnect function. return result; } void KMyMoneyApp::slotFileOpenRecent(const QUrl &url) { KMSTATUS(i18n("Loading file...")); QUrl lastFile = d->m_fileName; // check if there are other instances which might have this file open QList list = instanceList(); QList::ConstIterator it; bool duplicate = false; #ifdef KMM_DBUS for (it = list.constBegin(); duplicate == false && it != list.constEnd(); ++it) { QDBusInterface remoteApp(*it, "/KMymoney", "org.kde.kmymoney"); QDBusReply reply = remoteApp.call("filename"); if (!reply.isValid()) { qDebug("D-Bus error while calling app->filename()"); } else { if (reply.value() == url.url()) { duplicate = true; } } } #endif if (!duplicate) { QUrl newurl = url; if ((newurl.scheme() == QLatin1String("sql"))) { const QString key = QLatin1String("driver"); // take care and convert some old url to their new counterpart QUrlQuery query(newurl); if (query.queryItemValue(key) == QLatin1String("QMYSQL3")) { // fix any old urls query.removeQueryItem(key); query.addQueryItem(key, QLatin1String("QMYSQL")); } if (query.queryItemValue(key) == QLatin1String("QSQLITE3")) { query.removeQueryItem(key); query.addQueryItem(key, QLatin1String("QSQLITE")); } newurl.setQuery(query); if (query.queryItemValue(key) == QLatin1String("QSQLITE")) { newurl.setUserInfo(QString()); newurl.setHost(QString()); } // check if a password is needed. it may be if the URL came from the last/recent file list QPointer dialog = new KSelectDatabaseDlg(QIODevice::ReadWrite, newurl); if (!dialog->checkDrivers()) { delete dialog; return; } // if we need to supply a password, then show the dialog // otherwise it isn't needed if ((query.queryItemValue("secure").toLower() == QLatin1String("yes")) && newurl.password().isEmpty()) { if (dialog->exec() == QDialog::Accepted && dialog != nullptr) { newurl = dialog->selectedURL(); } else { delete dialog; return; } } delete dialog; } // TODO: port KF5 (NetAccess) if ((newurl.scheme() == QLatin1String("sql")) || (newurl.isValid() /*&& KIO::NetAccess::exists(newurl, KIO::NetAccess::SourceSide, this)*/)) { slotFileClose(); if (!d->m_myMoneyView->fileOpen()) { try { if (d->m_myMoneyView->readFile(newurl)) { if ((d->m_myMoneyView->isNativeFile())) { d->m_fileName = newurl; updateCaption(); d->m_recentFiles->addUrl(newurl); writeLastUsedFile(newurl.toDisplayString(QUrl::PreferLocalFile)); } else { d->m_fileName = QUrl(); // imported files have no filename } // Check the schedules slotCheckSchedules(); } } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("Cannot open file as requested. Error was: %1", e.what())); } updateCaption(); emit fileLoaded(d->m_fileName); } else { /*fileOpen failed - should we do something or maybe fileOpen puts out the message... - it does for database*/ } } else { // newurl invalid slotFileClose(); KMessageBox::sorry(this, i18n("

%1 is either an invalid filename or the file does not exist. You can open another file or create a new one.

", url.toDisplayString(QUrl::PreferLocalFile)), i18n("File not found")); } } else { // isDuplicate KMessageBox::sorry(this, i18n("

File %1 is already opened in another instance of KMyMoney

", url.toDisplayString(QUrl::PreferLocalFile)), i18n("Duplicate open")); } } bool KMyMoneyApp::slotFileSave() { // if there's nothing changed, there's no need to save anything if (!d->m_myMoneyView->dirty()) return true; bool rc = false; KMSTATUS(i18n("Saving file...")); if (d->m_fileName.isEmpty()) return slotFileSaveAs(); d->consistencyCheck(false); /*if (myMoneyView->isDatabase()) { rc = myMoneyView->saveDatabase(m_fileName); // the 'save' function is no longer relevant for a database*/ setEnabled(false); rc = d->m_myMoneyView->saveFile(d->m_fileName, MyMoneyFile::instance()->value("kmm-encryption-key")); setEnabled(true); d->m_autoSaveTimer->stop(); updateCaption(); return rc; } void KMyMoneyApp::slotFileSaveAsFilterChanged(const QString& filter) { if (!d->m_saveEncrypted) return; if (filter.compare(QLatin1String("*.kmy"), Qt::CaseInsensitive) != 0) { d->m_saveEncrypted->setCurrentItem(0); d->m_saveEncrypted->setEnabled(false); } else { d->m_saveEncrypted->setEnabled(true); } } void KMyMoneyApp::slotManageGpgKeys() { QPointer dlg = new KGpgKeySelectionDlg(this); dlg->setKeys(d->m_additionalGpgKeys); if (dlg->exec() == QDialog::Accepted && dlg != 0) { d->m_additionalGpgKeys = dlg->keys(); d->m_additionalKeyLabel->setText(i18n("Additional encryption keys to be used: %1", d->m_additionalGpgKeys.count())); } delete dlg; } void KMyMoneyApp::slotKeySelected(int idx) { int cnt = 0; if (idx != 0) { cnt = d->m_additionalGpgKeys.count(); } d->m_additionalKeyLabel->setEnabled(idx != 0); d->m_additionalKeyButton->setEnabled(idx != 0); d->m_additionalKeyLabel->setText(i18n("Additional encryption keys to be used: %1", cnt)); } bool KMyMoneyApp::slotFileSaveAs() { bool rc = false; // in event of it being a database, ensure that all data is read into storage for saveas if (d->m_myMoneyView->isDatabase()) dynamic_cast(MyMoneyFile::instance()->storage())->fillStorage(); KMSTATUS(i18n("Saving file with a new filename...")); // fill the additional key list with the default d->m_additionalGpgKeys = KMyMoneyGlobalSettings::gpgRecipientList(); QWidget* vbox = new QWidget(); QVBoxLayout *vboxVBoxLayout = new QVBoxLayout(vbox); vboxVBoxLayout->setMargin(0); if (KGPGFile::GPGAvailable()) { QWidget* keyBox = new QWidget(vbox); QHBoxLayout *keyBoxHBoxLayout = new QHBoxLayout(keyBox); keyBoxHBoxLayout->setMargin(0); vboxVBoxLayout->addWidget(keyBox); QLabel *keyLabel = new QLabel(i18n("Encryption key to be used"), keyBox); keyBoxHBoxLayout->addWidget(keyLabel); d->m_saveEncrypted = new KComboBox(keyBox); keyBoxHBoxLayout->addWidget(d->m_saveEncrypted); QWidget* labelBox = new QWidget(vbox); QHBoxLayout *labelBoxHBoxLayout = new QHBoxLayout(labelBox); labelBoxHBoxLayout->setMargin(0); vboxVBoxLayout->addWidget(labelBox); d->m_additionalKeyLabel = new QLabel(i18n("Additional encryption keys to be used: %1", d->m_additionalGpgKeys.count()), labelBox); labelBoxHBoxLayout->addWidget(d->m_additionalKeyLabel); d->m_additionalKeyButton = new QPushButton(i18n("Manage additional keys"), labelBox); labelBoxHBoxLayout->addWidget(d->m_additionalKeyButton); connect(d->m_additionalKeyButton, SIGNAL(clicked()), this, SLOT(slotManageGpgKeys())); connect(d->m_saveEncrypted, SIGNAL(activated(int)), this, SLOT(slotKeySelected(int))); // fill the secret key list and combo box QStringList keyList; KGPGFile::secretKeyList(keyList); d->m_saveEncrypted->addItem(i18n("No encryption")); for (QStringList::iterator it = keyList.begin(); it != keyList.end(); ++it) { QStringList fields = (*it).split(':', QString::SkipEmptyParts); if (fields[0] != recoveryKeyId) { // replace parenthesis in name field with brackets QString name = fields[1]; name.replace('(', "["); name.replace(')', "]"); name = QString("%1 (0x%2)").arg(name).arg(fields[0]); d->m_saveEncrypted->addItem(name); if (name.contains(KMyMoneyGlobalSettings::gpgRecipient())) { d->m_saveEncrypted->setCurrentItem(name); } } } } QString prevDir; // don't prompt file name if not a native file if (d->m_myMoneyView->isNativeFile()) prevDir = readLastUsedDir(); QPointer dlg = new QFileDialog(this, i18n("Save As"), prevDir, QString(QLatin1String("%2 (%1);;")).arg(QStringLiteral("*.kmy")).arg(i18nc("KMyMoney (Filefilter)", "KMyMoney files")) + QString(QLatin1String("%2 (%1);;")).arg(QStringLiteral("*.xml")).arg(i18nc("XML (Filefilter)", "XML files")) + QString(QLatin1String("%2 (%1);;")).arg(QStringLiteral("*.anon.xml")).arg(i18nc("Anonymous (Filefilter)", "Anonymous files")) + QString(QLatin1String("%2 (%1);;")).arg(QStringLiteral("*")).arg(i18nc("All files (Filefilter)", "All files"))); dlg->setAcceptMode(QFileDialog::AcceptSave); connect(dlg, SIGNAL(filterChanged(QString)), this, SLOT(slotFileSaveAsFilterChanged(QString))); if (dlg->exec() == QDialog::Accepted && dlg != 0) { QUrl newURL = dlg->selectedUrls().first(); if (!newURL.fileName().isEmpty()) { d->consistencyCheck(false); // deleting the dialog will delete the combobox pointed to by d->m_saveEncrypted so get the key name here QString selectedKeyName; if (d->m_saveEncrypted && d->m_saveEncrypted->currentIndex() != 0) selectedKeyName = d->m_saveEncrypted->currentText(); d->m_saveEncrypted = 0; QString newName = newURL.toDisplayString(QUrl::PreferLocalFile); // append extension if not present if (!newName.endsWith(QLatin1String(".kmy"), Qt::CaseInsensitive) && !newName.endsWith(QLatin1String(".xml"), Qt::CaseInsensitive)) newName.append(QLatin1String(".kmy")); newURL = QUrl::fromUserInput(newName); d->m_recentFiles->addUrl(newURL); setEnabled(false); // If this is the anonymous file export, just save it, don't actually take the // name, or remember it! Don't even try to encrypt it if (newName.endsWith(QLatin1String(".anon.xml"), Qt::CaseInsensitive)) rc = d->m_myMoneyView->saveFile(newURL); else { d->m_fileName = newURL; QString encryptionKeys; QRegExp keyExp(".* \\((.*)\\)"); if (keyExp.indexIn(selectedKeyName) != -1) { encryptionKeys = keyExp.cap(1); } if (!d->m_additionalGpgKeys.isEmpty()) { if (!encryptionKeys.isEmpty()) encryptionKeys.append(QLatin1Char(',')); encryptionKeys.append(d->m_additionalGpgKeys.join(QLatin1Char(','))); } rc = d->m_myMoneyView->saveFile(d->m_fileName, encryptionKeys); //write the directory used for this file as the default one for next time. writeLastUsedDir(newURL.toDisplayString(QUrl::RemoveFilename | QUrl::PreferLocalFile | QUrl::StripTrailingSlash)); writeLastUsedFile(newName); } d->m_autoSaveTimer->stop(); setEnabled(true); } } delete dlg; updateCaption(); return rc; } void KMyMoneyApp::slotSaveAsDatabase() { saveAsDatabase(); } bool KMyMoneyApp::saveAsDatabase() { bool rc = false; QUrl oldUrl; // in event of it being a database, ensure that all data is read into storage for saveas if (d->m_myMoneyView->isDatabase()) { dynamic_cast(MyMoneyFile::instance()->storage())->fillStorage(); oldUrl = d->m_fileName.isEmpty() ? lastOpenedURL() : d->m_fileName; } KMSTATUS(i18n("Saving file to database...")); QPointer dialog = new KSelectDatabaseDlg(QIODevice::WriteOnly); QUrl url = oldUrl; if (!dialog->checkDrivers()) { delete dialog; return (false); } while (oldUrl == url && dialog->exec() == QDialog::Accepted && dialog != 0) { url = dialog->selectedURL(); // If the protocol is SQL for the old and new, and the hostname and database names match // Let the user know that the current database cannot be saved on top of itself. if (url.scheme() == "sql" && oldUrl.scheme() == "sql" && oldUrl.host() == url.host() && QUrlQuery(oldUrl).queryItemValue("driver") == QUrlQuery(url).queryItemValue("driver") && oldUrl.path().right(oldUrl.path().length() - 1) == url.path().right(url.path().length() - 1)) { KMessageBox::sorry(this, i18n("Cannot save to current database.")); } else { try { rc = d->m_myMoneyView->saveAsDatabase(url); } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("Cannot save to current database: %1", e.what())); } } } delete dialog; if (rc) { //KRecentFilesAction *p = dynamic_cast(action("file_open_recent")); //if(p) d->m_recentFiles->addUrl(url); writeLastUsedFile(url.toDisplayString(QUrl::PreferLocalFile)); } d->m_autoSaveTimer->stop(); updateCaption(); return rc; } void KMyMoneyApp::slotFileCloseWindow() { KMSTATUS(i18n("Closing window...")); if (d->m_myMoneyView->dirty()) { int answer = askSaveOnClose(); if (answer == KMessageBox::Cancel) return; else if (answer == KMessageBox::Yes) slotFileSave(); } close(); } void KMyMoneyApp::slotFileClose() { bool okToSelect = true; // check if transaction editor is open and ask user what he wants to do slotTransactionsCancelOrEnter(okToSelect); if (!okToSelect) return; // no update status here, as we might delete the status too early. if (d->m_myMoneyView->dirty()) { int answer = askSaveOnClose(); if (answer == KMessageBox::Cancel) return; else if (answer == KMessageBox::Yes) slotFileSave(); } d->closeFile(); } void KMyMoneyApp::slotFileQuit() { // don't modify the status message here as this will prevent quit from working!! // See the beginning of queryClose() and isReady() why. Thomas Baumgart 2005-10-17 bool quitApplication = true; QList memberList = KMainWindow::memberList(); if (!memberList.isEmpty()) { QList::const_iterator w_it = memberList.constBegin(); for (; w_it != memberList.constEnd(); ++w_it) { // only close the window if the closeEvent is accepted. If the user presses Cancel on the saveModified() dialog, // the window and the application stay open. if (!(*w_it)->close()) { quitApplication = false; break; } } } // We will only quit if all windows were processed and not cancelled if (quitApplication) { QCoreApplication::quit(); } } void KMyMoneyApp::slotShowTransactionDetail() { } void KMyMoneyApp::slotHideReconciledTransactions() { KMyMoneyGlobalSettings::setHideReconciledTransactions(actionCollection()->action(s_Actions[Action::ViewHideReconciled])->isChecked()); d->m_myMoneyView->slotRefreshViews(); } void KMyMoneyApp::slotHideUnusedCategories() { KMyMoneyGlobalSettings::setHideUnusedCategory(actionCollection()->action(s_Actions[Action::ViewHideCategories])->isChecked()); d->m_myMoneyView->slotRefreshViews(); } void KMyMoneyApp::slotShowAllAccounts() { d->m_myMoneyView->slotRefreshViews(); } #ifdef KMM_DEBUG void KMyMoneyApp::slotToggleTraces() { MyMoneyTracer::onOff(actionCollection()->action(s_Actions[Action::DebugTraces])->isChecked() ? 1 : 0); } #endif void KMyMoneyApp::slotToggleTimers() { extern bool timersOn; // main.cpp timersOn = actionCollection()->action(s_Actions[Action::DebugTimers])->isChecked(); } QString KMyMoneyApp::slotStatusMsg(const QString &text) { /////////////////////////////////////////////////////////////////// // change status message permanently QString previousMessage = d->m_statusLabel->text(); d->m_applicationIsReady = false; QString currentMessage = text; if (currentMessage.isEmpty() || currentMessage == i18nc("Application is ready to use", "Ready.")) { d->m_applicationIsReady = true; currentMessage = i18nc("Application is ready to use", "Ready."); } statusBar()->clearMessage(); d->m_statusLabel->setText(currentMessage); return previousMessage; } void KMyMoneyApp::ready() { slotStatusMsg(QString()); } bool KMyMoneyApp::isReady() { return d->m_applicationIsReady; } void KMyMoneyApp::slotStatusProgressBar(int current, int total) { if (total == -1 && current == -1) { // reset if (d->m_progressTimer) { d->m_progressTimer->start(500); // remove from screen in 500 msec d->m_progressBar->setValue(d->m_progressBar->maximum()); } } else if (total != 0) { // init d->m_progressTimer->stop(); d->m_progressBar->setMaximum(total); d->m_progressBar->setValue(0); d->m_progressBar->show(); } else { // update QTime currentTime = QTime::currentTime(); // only process painting if last update is at least 250 ms ago if (abs(d->m_lastUpdate.msecsTo(currentTime)) > 250) { d->m_progressBar->setValue(current); d->m_lastUpdate = currentTime; } } } void KMyMoneyApp::slotStatusProgressDone() { d->m_progressTimer->stop(); d->m_progressBar->reset(); d->m_progressBar->hide(); d->m_progressBar->setValue(0); } void KMyMoneyApp::progressCallback(int current, int total, const QString& msg) { if (!msg.isEmpty()) kmymoney->slotStatusMsg(msg); kmymoney->slotStatusProgressBar(current, total); } void KMyMoneyApp::slotFileViewPersonal() { if (!d->m_myMoneyView->fileOpen()) { KMessageBox::information(this, i18n("No KMyMoneyFile open")); return; } KMSTATUS(i18n("Viewing personal data...")); MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyPayee user = file->user(); QPointer editPersonalDataDlg = new EditPersonalDataDlg(user.name(), user.address(), user.city(), user.state(), user.postcode(), user.telephone(), user.email(), this, i18n("Edit Personal Data")); if (editPersonalDataDlg->exec() == QDialog::Accepted && editPersonalDataDlg != 0) { user.setName(editPersonalDataDlg->userName()); user.setAddress(editPersonalDataDlg->userStreet()); user.setCity(editPersonalDataDlg->userTown()); user.setState(editPersonalDataDlg->userCountry()); user.setPostcode(editPersonalDataDlg->userPostcode()); user.setTelephone(editPersonalDataDlg->userTelephone()); user.setEmail(editPersonalDataDlg->userEmail()); MyMoneyFileTransaction ft; try { file->setUser(user); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to store user information: %1", e.what())); } } delete editPersonalDataDlg; } void KMyMoneyApp::slotFileFileInfo() { if (!d->m_myMoneyView->fileOpen()) { KMessageBox::information(this, i18n("No KMyMoneyFile open")); return; } QFile g("kmymoney.dump"); g.open(QIODevice::WriteOnly); QDataStream st(&g); MyMoneyStorageDump dumper; dumper.writeStream(st, dynamic_cast(MyMoneyFile::instance()->storage())); g.close(); } void KMyMoneyApp::slotLoadAccountTemplates() { KMSTATUS(i18n("Importing account templates.")); int rc; QPointer dlg = new KLoadTemplateDlg(); if ((rc = dlg->exec()) == QDialog::Accepted && dlg != 0) { MyMoneyFileTransaction ft; try { // import the account templates QList templates = dlg->templates(); QList::iterator it_t; for (it_t = templates.begin(); it_t != templates.end(); ++it_t) { (*it_t).importTemplate(&progressCallback); } ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Error"), i18n("Unable to import template(s): %1, thrown in %2:%3", e.what(), e.file(), e.line())); } } delete dlg; } void KMyMoneyApp::slotSaveAccountTemplates() { KMSTATUS(i18n("Exporting account templates.")); QString savePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/templates/" + QLocale().name(); QDir d(savePath); if (!d.exists()) d.mkpath(savePath); QString newName = QFileDialog::getSaveFileName(this, i18n("Save as..."), savePath, i18n("KMyMoney template files (*.kmt);;All files (*)")); // // If there is no file extension, then append a .kmt at the end of the file name. // If there is a file extension, make sure it is .kmt, delete any others. // if (!newName.isEmpty()) { // find last . delimiter int nLoc = newName.lastIndexOf('.'); if (nLoc != -1) { QString strExt, strTemp; strTemp = newName.left(nLoc + 1); strExt = newName.right(newName.length() - (nLoc + 1)); if ((strExt.indexOf("kmt", 0, Qt::CaseInsensitive) == -1)) { strTemp.append("kmt"); //append to make complete file name newName = strTemp; } } else { newName.append(".kmt"); } if (okToWriteFile(QUrl::fromLocalFile(newName))) { QPointer dlg = new KTemplateExportDlg(this); if (dlg->exec() == QDialog::Accepted && dlg) { MyMoneyTemplate templ; templ.setTitle(dlg->title()); templ.setShortDescription(dlg->shortDescription()); templ.setLongDescription(dlg->longDescription()); templ.exportTemplate(&progressCallback); templ.saveTemplate(QUrl::fromLocalFile(newName)); } delete dlg; } } } void KMyMoneyApp::slotGncImport() { if (d->m_myMoneyView->fileOpen()) { switch (KMessageBox::questionYesNoCancel(0, i18n("You cannot import GnuCash data into an existing file. Do you wish to save this file?"), PACKAGE)) { case KMessageBox::Yes: slotFileSave(); break; case KMessageBox::No: d->closeFile(); break; default: return; } } KMSTATUS(i18n("Importing a GnuCash file.")); QUrl fileToRead = QFileDialog::getOpenFileUrl(this, QString(), QUrl(), i18n("GnuCash files (*.gnucash *.xac *.gnc);;All files (*)")); if (!fileToRead.isEmpty()) { // call the importer if (d->m_myMoneyView->readFile(fileToRead)) { // imported files don't have a name d->m_fileName = QUrl(); updateCaption(); emit fileLoaded(d->m_fileName); } } } void KMyMoneyApp::slotAccountChart() { if (!d->m_selectedAccount.id().isEmpty()) { QPointer dlg = new KBalanceChartDlg(d->m_selectedAccount, this); dlg->exec(); delete dlg; } } // // KMyMoneyApp::slotStatementImport() is for testing only. The MyMoneyStatement // is not intended to be exposed to users in XML form. // void KMyMoneyApp::slotStatementImport() { bool result = false; KMSTATUS(i18n("Importing an XML Statement.")); QList files{QFileDialog::getOpenFileUrls(this, QString(), QUrl(), i18n("XML files (*.xml);;All files (*)"))}; if (!files.isEmpty()) { d->m_collectingStatements = (files.count() > 1); foreach (const QUrl &url, files) { qDebug("Processing '%s'", qPrintable(url.path())); result |= slotStatementImport(url.path()); } /* QFile f( dialog->selectedURL().path() ); f.open( QIODevice::ReadOnly ); QString error = "Unable to parse file"; QDomDocument* doc = new QDomDocument; if(doc->setContent(&f, FALSE)) { if ( doc->doctype().name() == "KMYMONEY-STATEMENT" ) { QDomElement rootElement = doc->documentElement(); if(!rootElement.isNull()) { QDomNode child = rootElement.firstChild(); if(!child.isNull() && child.isElement()) { MyMoneyStatement s; if ( s.read(child.toElement()) ) result = slotStatementImport(s); else error = "File does not contain any statements"; } } } else error = "File is not a KMyMoney Statement"; } delete doc; if ( !result ) { QMessageBox::critical( this, i18n("Critical Error"), i18n("Unable to read file %1: %2").arg( dialog->selectedURL().path()).arg(error), QMessageBox::Ok, 0 ); }*/ } if (!result) { // re-enable all standard widgets setEnabled(true); } } bool KMyMoneyApp::slotStatementImport(const QString& url) { bool result = false; MyMoneyStatement s; if (MyMoneyStatement::readXMLFile(s, url)) result = slotStatementImport(s); else KMessageBox::error(this, i18n("Error importing %1: This file is not a valid KMM statement file.", url), i18n("Invalid Statement")); return result; } bool KMyMoneyApp::slotStatementImport(const MyMoneyStatement& s, bool silent) { bool result = false; // keep a copy of the statement if (KMyMoneySettings::logImportedStatements()) { QString logFile = QString("%1/kmm-statement-%2.txt").arg(KMyMoneySettings::logPath()).arg(d->m_statementXMLindex++); MyMoneyStatement::writeXMLFile(s, logFile); } // we use an object on the heap here, so that we can check the presence // of it during slotUpdateActions() by looking at the pointer. d->m_smtReader = new MyMoneyStatementReader; d->m_smtReader->setAutoCreatePayee(true); d->m_smtReader->setProgressCallback(&progressCallback); // disable all standard widgets during the import setEnabled(false); QStringList messages; result = d->m_smtReader->import(s, messages); bool transactionAdded = d->m_smtReader->anyTransactionAdded(); // get rid of the statement reader and tell everyone else // about the destruction by setting the pointer to zero delete d->m_smtReader; d->m_smtReader = 0; slotStatusProgressBar(-1, -1); ready(); // re-enable all standard widgets setEnabled(true); if (!d->m_collectingStatements && !silent) KMessageBox::informationList(this, i18n("The statement has been processed with the following results:"), messages, i18n("Statement stats")); else if (transactionAdded) d->m_statementResults += messages; slotUpdateActions();// Re-enable menu items after import via plugin. return result; } bool KMyMoneyApp::okToWriteFile(const QUrl &url) { Q_UNUSED(url) // check if the file exists and warn the user bool reallySaveFile = true; // TODO: port KF5 (NetAccess) //if (KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, this)) { // if (KMessageBox::warningYesNo(this, QLatin1String("") + i18n("The file %1 already exists. Do you really want to overwrite it?", url.toDisplayString(QUrl::PreferLocalFile)) + QLatin1String(""), i18n("File already exists")) != KMessageBox::Yes) // reallySaveFile = false; //} return reallySaveFile; } void KMyMoneyApp::slotSettings() { // if we already have an instance of the settings dialog, then use it if (KConfigDialog::showDialog("KMyMoney-Settings")) return; // otherwise, we have to create it KConfigDialog* dlg = new KSettingsKMyMoney(this, "KMyMoney-Settings", KMyMoneyGlobalSettings::self()); connect(dlg, &KConfigDialog::settingsChanged, this, &KMyMoneyApp::slotUpdateConfiguration); dlg->show(); } void KMyMoneyApp::slotShowCredits() { KAboutData aboutData = initializeCreditsData(); KAboutApplicationDialog dlg(aboutData, this); dlg.exec(); } void KMyMoneyApp::slotUpdateConfiguration() { MyMoneyTransactionFilter::setFiscalYearStart(KMyMoneyGlobalSettings::firstFiscalMonth(), KMyMoneyGlobalSettings::firstFiscalDay()); LedgerSeperator::setFirstFiscalDate(KMyMoneyGlobalSettings::firstFiscalMonth(), KMyMoneyGlobalSettings::firstFiscalDay()); d->m_myMoneyView->updateViewType(); // update the sql storage module settings MyMoneyStorageSql::setStartDate(KMyMoneyGlobalSettings::startDate().date()); // update the report module settings MyMoneyReport::setLineWidth(KMyMoneyGlobalSettings::lineWidth()); // update the holiday region configuration setHolidayRegion(KMyMoneyGlobalSettings::holidayRegion()); d->m_myMoneyView->slotRefreshViews(); // re-read autosave configuration d->m_autoSaveEnabled = KMyMoneyGlobalSettings::autoSaveFile(); d->m_autoSavePeriod = KMyMoneyGlobalSettings::autoSavePeriod(); // stop timer if turned off but running if (d->m_autoSaveTimer->isActive() && !d->m_autoSaveEnabled) { d->m_autoSaveTimer->stop(); } // start timer if turned on and needed but not running if (!d->m_autoSaveTimer->isActive() && d->m_autoSaveEnabled && d->m_myMoneyView->dirty()) { d->m_autoSaveTimer->setSingleShot(true); d->m_autoSaveTimer->start(d->m_autoSavePeriod * 60 * 1000); } d->setThemedCSS(); // check if the recovery key is still valid or expires soon if (KMyMoneySettings::writeDataEncrypted() && KMyMoneySettings::encryptRecover()) { if (KGPGFile::GPGAvailable()) { KGPGFile file; QDateTime expirationDate = file.keyExpires(QLatin1String(recoveryKeyId)); if (expirationDate.isValid() && QDateTime::currentDateTime().daysTo(expirationDate) <= RECOVER_KEY_EXPIRATION_WARNING) { bool skipMessage = false; //get global config object for our app. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); KConfigGroup grp; QDate lastWarned; if (kconfig) { grp = d->m_config->group("General Options"); lastWarned = grp.readEntry("LastRecoverKeyExpirationWarning", QDate()); if (QDate::currentDate() == lastWarned) { skipMessage = true; } } if (!skipMessage) { if (kconfig) { grp.writeEntry("LastRecoverKeyExpirationWarning", QDate::currentDate()); } KMessageBox::information(this, i18np("You have configured KMyMoney to use GPG to protect your data and to encrypt your data also with the KMyMoney recover key. This key is about to expire in %1 day. Please update the key from a keyserver using your GPG frontend (e.g. KGPG).", "You have configured KMyMoney to use GPG to protect your data and to encrypt your data also with the KMyMoney recover key. This key is about to expire in %1 days. Please update the key from a keyserver using your GPG frontend (e.g. KGPG).", QDateTime::currentDateTime().daysTo(expirationDate)), i18n("Recover key expires soon")); } } } } } void KMyMoneyApp::slotBackupFile() { // Save the file first so isLocalFile() works if (d->m_myMoneyView && d->m_myMoneyView->dirty()) { if (KMessageBox::questionYesNo(this, i18n("The file must be saved first " "before it can be backed up. Do you want to continue?")) == KMessageBox::No) { return; } slotFileSave(); } if (d->m_fileName.isEmpty()) return; if (!d->m_fileName.isLocalFile()) { KMessageBox::sorry(this, i18n("The current implementation of the backup functionality only supports local files as source files. Your current source file is '%1'.", d->m_fileName.url()), i18n("Local files only")); return; } QPointer backupDlg = new KBackupDlg(this); #ifdef Q_OS_WIN backupDlg->mountCheckBox->setEnabled(false); #endif int returncode = backupDlg->exec(); if (returncode == QDialog::Accepted && backupDlg != 0) { d->m_backupMount = backupDlg->mountCheckBox(); d->m_proc.clearProgram(); d->m_backupState = BACKUP_MOUNTING; d->m_mountpoint = backupDlg->mountPoint(); if (d->m_backupMount) { slotBackupMount(); } else { progressCallback(0, 300, ""); #ifdef Q_OS_WIN d->m_ignoreBackupExitCode = true; QTimer::singleShot(0, this, SLOT(slotBackupHandleEvents())); #else // If we don't have to mount a device, we just issue // a dummy command to start the copy operation d->m_proc.setProgram("true"); d->m_proc.start(); #endif } } delete backupDlg; } void KMyMoneyApp::slotBackupMount() { progressCallback(0, 300, i18n("Mounting %1", d->m_mountpoint)); d->m_proc.setProgram("mount"); d->m_proc << d->m_mountpoint; d->m_proc.start(); } bool KMyMoneyApp::slotBackupWriteFile() { QFileInfo fi(d->m_fileName.fileName()); QString today = QDate::currentDate().toString("-yyyy-MM-dd.") + fi.suffix(); QString backupfile = d->m_mountpoint + '/' + d->m_fileName.fileName(); KMyMoneyUtils::appendCorrectFileExt(backupfile, today); // check if file already exists and ask what to do QFile f(backupfile); if (f.exists()) { int answer = KMessageBox::warningContinueCancel(this, i18n("Backup file for today exists on that device. Replace?"), i18n("Backup"), KGuiItem(i18n("&Replace"))); if (answer == KMessageBox::Cancel) { return false; } } progressCallback(50, 0, i18n("Writing %1", backupfile)); d->m_proc.clearProgram(); #ifdef Q_OS_WIN d->m_proc << "cmd.exe" << "/c" << "copy" << "/b" << "/y"; d->m_proc << (QDir::toNativeSeparators(d->m_fileName.toLocalFile()) + "+ nul") << QDir::toNativeSeparators(backupfile); #else d->m_proc << "cp" << "-f"; d->m_proc << d->m_fileName.toLocalFile() << backupfile; #endif d->m_backupState = BACKUP_COPYING; d->m_proc.start(); return true; } void KMyMoneyApp::slotBackupUnmount() { progressCallback(250, 0, i18n("Unmounting %1", d->m_mountpoint)); d->m_proc.clearProgram(); d->m_proc.setProgram("umount"); d->m_proc << d->m_mountpoint; d->m_backupState = BACKUP_UNMOUNTING; d->m_proc.start(); } void KMyMoneyApp::slotBackupFinish() { d->m_backupState = BACKUP_IDLE; progressCallback(-1, -1, QString()); ready(); } void KMyMoneyApp::slotBackupHandleEvents() { switch (d->m_backupState) { case BACKUP_MOUNTING: if (d->m_ignoreBackupExitCode || (d->m_proc.exitStatus() == QProcess::NormalExit && d->m_proc.exitCode() == 0)) { d->m_ignoreBackupExitCode = false; d->m_backupResult = 0; if (!slotBackupWriteFile()) { d->m_backupResult = 1; if (d->m_backupMount) slotBackupUnmount(); else slotBackupFinish(); } } else { KMessageBox::information(this, i18n("Error mounting device"), i18n("Backup")); d->m_backupResult = 1; if (d->m_backupMount) slotBackupUnmount(); else slotBackupFinish(); } break; case BACKUP_COPYING: if (d->m_proc.exitStatus() == QProcess::NormalExit && d->m_proc.exitCode() == 0) { if (d->m_backupMount) { slotBackupUnmount(); } else { progressCallback(300, 0, i18nc("Backup done", "Done")); KMessageBox::information(this, i18n("File successfully backed up"), i18n("Backup")); slotBackupFinish(); } } else { qDebug("copy exit code is %d", d->m_proc.exitCode()); d->m_backupResult = 1; KMessageBox::information(this, i18n("Error copying file to device"), i18n("Backup")); if (d->m_backupMount) slotBackupUnmount(); else slotBackupFinish(); } break; case BACKUP_UNMOUNTING: if (d->m_proc.exitStatus() == QProcess::NormalExit && d->m_proc.exitCode() == 0) { progressCallback(300, 0, i18nc("Backup done", "Done")); if (d->m_backupResult == 0) KMessageBox::information(this, i18n("File successfully backed up"), i18n("Backup")); } else { KMessageBox::information(this, i18n("Error unmounting device"), i18n("Backup")); } slotBackupFinish(); break; default: qWarning("Unknown state for backup operation!"); progressCallback(-1, -1, QString()); ready(); break; } } void KMyMoneyApp::slotShowTipOfTheDay() { KTipDialog::showTip(d->m_myMoneyView, "", true); } void KMyMoneyApp::slotShowPreviousView() { } void KMyMoneyApp::slotShowNextView() { } void KMyMoneyApp::slotGenerateSql() { QPointer editor = new KGenerateSqlDlg(this); editor->setObjectName("Generate Database SQL"); editor->exec(); delete editor; } void KMyMoneyApp::slotToolsStartKCalc() { QString cmd = KMyMoneyGlobalSettings::externalCalculator(); // if none is present, we fall back to the default if (cmd.isEmpty()) { #if defined(Q_OS_WIN32) cmd = QLatin1String("calc"); #elif defined(Q_OS_MAC) cmd = QLatin1String("open -a Calculator"); #else cmd = QLatin1String("kcalc"); #endif } KRun::runCommand(cmd, this); } void KMyMoneyApp::slotFindTransaction() { if (d->m_searchDlg == 0) { d->m_searchDlg = new KFindTransactionDlg(this); connect(d->m_searchDlg, SIGNAL(destroyed()), this, SLOT(slotCloseSearchDialog())); connect(d->m_searchDlg, SIGNAL(transactionSelected(QString,QString)), d->m_myMoneyView, SLOT(slotLedgerSelected(QString,QString))); } d->m_searchDlg->show(); d->m_searchDlg->raise(); d->m_searchDlg->activateWindow(); } void KMyMoneyApp::slotCloseSearchDialog() { if (d->m_searchDlg) d->m_searchDlg->deleteLater(); d->m_searchDlg = 0; } void KMyMoneyApp::createInstitution(MyMoneyInstitution& institution) { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyFileTransaction ft; try { file->addInstitution(institution); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Cannot add institution: %1", e.what())); } } void KMyMoneyApp::slotInstitutionNew() { MyMoneyInstitution institution; slotInstitutionNew(institution); } void KMyMoneyApp::slotInstitutionNew(MyMoneyInstitution& institution) { institution.clearId(); QPointer dlg = new KNewBankDlg(institution); if (dlg->exec() == QDialog::Accepted && dlg != 0) { institution = dlg->institution(); createInstitution(institution); } delete dlg; } void KMyMoneyApp::slotInstitutionEditEmpty() { slotInstitutionEdit(MyMoneyInstitution()); } void KMyMoneyApp::slotInstitutionEdit(const MyMoneyObject& obj) { if (typeid(obj) != typeid(MyMoneyInstitution)) return; // make sure the selected object has an id if (d->m_selectedInstitution.id().isEmpty()) return; try { MyMoneyFile* file = MyMoneyFile::instance(); //grab a pointer to the view, regardless of it being a account or institution view. MyMoneyInstitution institution = file->institution(d->m_selectedInstitution.id()); // bankSuccess is not checked anymore because d->m_file->institution will throw anyway QPointer dlg = new KNewBankDlg(institution); if (dlg->exec() == QDialog::Accepted && dlg != 0) { MyMoneyFileTransaction ft; try { file->modifyInstitution(dlg->institution()); ft.commit(); slotSelectInstitution(file->institution(dlg->institution().id())); } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to store institution: %1", e.what())); } } delete dlg; } catch (const MyMoneyException &e) { if (!obj.id().isEmpty()) KMessageBox::information(this, i18n("Unable to edit institution: %1", e.what())); } } void KMyMoneyApp::slotInstitutionDelete() { MyMoneyFile *file = MyMoneyFile::instance(); try { MyMoneyInstitution institution = file->institution(d->m_selectedInstitution.id()); if ((KMessageBox::questionYesNo(this, i18n("

Do you really want to delete the institution %1?

", institution.name()))) == KMessageBox::No) return; MyMoneyFileTransaction ft; try { file->removeInstitution(institution); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to delete institution: %1", e.what())); } } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to delete institution: %1", e.what())); } } void KMyMoneyApp::createAccount(MyMoneyAccount& newAccount, MyMoneyAccount& parentAccount, MyMoneyAccount& brokerageAccount, MyMoneyMoney openingBal) { MyMoneyFile *file = MyMoneyFile::instance(); try { const MyMoneySecurity& sec = file->security(newAccount.currencyId()); // Check the opening balance if (openingBal.isPositive() && newAccount.accountGroup() == eMyMoney::Account::Liability) { QString message = i18n("This account is a liability and if the " "opening balance represents money owed, then it should be negative. " "Negate the amount?\n\n" "Please click Yes to change the opening balance to %1,\n" "Please click No to leave the amount as %2,\n" "Please click Cancel to abort the account creation." , MyMoneyUtils::formatMoney(-openingBal, newAccount, sec) , MyMoneyUtils::formatMoney(openingBal, newAccount, sec)); int ans = KMessageBox::questionYesNoCancel(this, message); if (ans == KMessageBox::Yes) { openingBal = -openingBal; } else if (ans == KMessageBox::Cancel) return; } file->createAccount(newAccount, parentAccount, brokerageAccount, openingBal); } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to add account: %1", e.what())); } } void KMyMoneyApp::slotCategoryNew(const QString& name, QString& id) { MyMoneyAccount account; account.setName(name); slotCategoryNew(account, MyMoneyFile::instance()->expense()); id = account.id(); } void KMyMoneyApp::slotCategoryNew(MyMoneyAccount& account, const MyMoneyAccount& parent) { if (KMessageBox::questionYesNo(this, QString("%1").arg(i18n("

The category %1 currently does not exist. Do you want to create it?

The parent account will default to %2 but can be changed in the following dialog.

", account.name(), parent.name())), i18n("Create category"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "CreateNewCategories") == KMessageBox::Yes) { createCategory(account, parent); } else { // we should not keep the 'no' setting because that can confuse people like // I have seen in some usability tests. So we just delete it right away. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); if (kconfig) { kconfig->group(QLatin1String("Notification Messages")).deleteEntry(QLatin1String("CreateNewCategories")); } } } void KMyMoneyApp::slotCategoryNew(MyMoneyAccount& account) { slotCategoryNew(account, MyMoneyAccount()); } void KMyMoneyApp::slotCategoryNew() { MyMoneyAccount parent; MyMoneyAccount account; // Preselect the parent account by looking at the current selected account/category if (!d->m_selectedAccount.id().isEmpty() && d->m_selectedAccount.isIncomeExpense()) { MyMoneyFile* file = MyMoneyFile::instance(); try { parent = file->account(d->m_selectedAccount.id()); } catch (const MyMoneyException &) { } } createCategory(account, parent); } void KMyMoneyApp::createCategory(MyMoneyAccount& account, const MyMoneyAccount& parent) { if (!parent.id().isEmpty()) { try { // make sure parent account exists MyMoneyFile::instance()->account(parent.id()); account.setParentAccountId(parent.id()); account.setAccountType(parent.accountType()); } catch (const MyMoneyException &) { } } QPointer dialog = new KNewAccountDlg(account, false, true, 0, i18n("Create a new Category")); dialog->setOpeningBalanceShown(false); dialog->setOpeningDateShown(false); if (dialog->exec() == QDialog::Accepted && dialog != 0) { MyMoneyAccount parentAccount, brokerageAccount; account = dialog->account(); parentAccount = dialog->parentAccount(); MyMoneyFile::instance()->createAccount(account, parentAccount, brokerageAccount, MyMoneyMoney()); } delete dialog; } void KMyMoneyApp::slotAccountNew() { MyMoneyAccount acc; acc.setInstitutionId(d->m_selectedInstitution.id()); acc.setOpeningDate(KMyMoneyGlobalSettings::firstFiscalDate()); slotAccountNew(acc); } void KMyMoneyApp::slotAccountNew(MyMoneyAccount& account) { NewAccountWizard::Wizard* wizard = new NewAccountWizard::Wizard(); connect(wizard, SIGNAL(createInstitution(MyMoneyInstitution&)), this, SLOT(slotInstitutionNew(MyMoneyInstitution&))); connect(wizard, SIGNAL(createAccount(MyMoneyAccount&)), this, SLOT(slotAccountNew(MyMoneyAccount&))); connect(wizard, SIGNAL(createPayee(QString,QString&)), this, SLOT(slotPayeeNew(QString,QString&))); connect(wizard, SIGNAL(createCategory(MyMoneyAccount&,MyMoneyAccount)), this, SLOT(slotCategoryNew(MyMoneyAccount&,MyMoneyAccount))); wizard->setAccount(account); if (wizard->exec() == QDialog::Accepted) { MyMoneyAccount acc = wizard->account(); if (!(acc == MyMoneyAccount())) { MyMoneyFileTransaction ft; MyMoneyFile* file = MyMoneyFile::instance(); try { // create the account MyMoneyAccount parent = wizard->parentAccount(); file->addAccount(acc, parent); // tell the wizard about the account id which it // needs to create a possible schedule and transactions wizard->setAccount(acc); // store a possible conversion rate for the currency if (acc.currencyId() != file->baseCurrency().id()) { file->addPrice(wizard->conversionRate()); } // create the opening balance transaction if any file->createOpeningBalanceTransaction(acc, wizard->openingBalance()); // create the payout transaction for loans if any MyMoneyTransaction payoutTransaction = wizard->payoutTransaction(); if (payoutTransaction.splits().count() > 0) { file->addTransaction(payoutTransaction); } // create a brokerage account if selected MyMoneyAccount brokerageAccount = wizard->brokerageAccount(); if (!(brokerageAccount == MyMoneyAccount())) { file->addAccount(brokerageAccount, parent); } // create a possible schedule MyMoneySchedule sch = wizard->schedule(); if (!(sch == MyMoneySchedule())) { MyMoneyFile::instance()->addSchedule(sch); if (acc.isLoan()) { MyMoneyAccountLoan accLoan = MyMoneyFile::instance()->account(acc.id()); accLoan.setSchedule(sch.id()); acc = accLoan; MyMoneyFile::instance()->modifyAccount(acc); } } ft.commit(); account = acc; } catch (const MyMoneyException &e) { KMessageBox::error(this, i18n("Unable to create account: %1", e.what())); } } } delete wizard; } void KMyMoneyApp::slotInvestmentNew(MyMoneyAccount& account, const MyMoneyAccount& parent) { QString dontShowAgain = "CreateNewInvestments"; if (KMessageBox::questionYesNo(this, QString("") + i18n("The security %1 currently does not exist as sub-account of %2. " "Do you want to create it?", account.name(), parent.name()) + QString(""), i18n("Create security"), KStandardGuiItem::yes(), KStandardGuiItem::no(), dontShowAgain) == KMessageBox::Yes) { KNewInvestmentWizard dlg; dlg.setName(account.name()); if (dlg.exec() == QDialog::Accepted) { dlg.createObjects(parent.id()); account = dlg.account(); } } else { // in case the user said no but turned on the don't show again selection, we will enable // the message no matter what. Otherwise, the user is not able to use this feature // in the future anymore. KMessageBox::enableMessage(dontShowAgain); } } void KMyMoneyApp::slotInvestmentNew() { QPointer dlg = new KNewInvestmentWizard(this); if (dlg->exec() == QDialog::Accepted) dlg->createObjects(d->m_selectedAccount.id()); delete dlg; } void KMyMoneyApp::slotInvestmentEdit() { QPointer dlg = new KNewInvestmentWizard(d->m_selectedInvestment); if (dlg->exec() == QDialog::Accepted) dlg->createObjects(d->m_selectedAccount.id()); delete dlg; } void KMyMoneyApp::slotInvestmentDelete() { if (KMessageBox::questionYesNo(this, i18n("

Do you really want to delete the investment %1?

", d->m_selectedInvestment.name()), i18n("Delete investment"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "DeleteInvestment") == KMessageBox::Yes) { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyFileTransaction ft; try { d->m_selectedAccount = MyMoneyAccount(); // CAUTION: deleting equity from investments view needs this, if ID of the equity to be deleted is the smallest from all file->removeAccount(d->m_selectedInvestment); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to delete investment: %1", e.what())); } } else { // we should not keep the 'no' setting because that can confuse people like // I have seen in some usability tests. So we just delete it right away. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); if (kconfig) { kconfig->group(QLatin1String("Notification Messages")).deleteEntry(QLatin1String("DeleteInvestment")); } } } void KMyMoneyApp::slotOnlinePriceUpdate() { if (!d->m_selectedInvestment.id().isEmpty()) { QPointer dlg = new KEquityPriceUpdateDlg(0, d->m_selectedInvestment.currencyId()); if (dlg->exec() == QDialog::Accepted && dlg != 0) { dlg->storePrices(); } delete dlg; } } void KMyMoneyApp::slotManualPriceUpdate() { if (!d->m_selectedInvestment.id().isEmpty()) { try { MyMoneySecurity security = MyMoneyFile::instance()->security(d->m_selectedInvestment.currencyId()); MyMoneySecurity currency = MyMoneyFile::instance()->security(security.tradingCurrency()); const MyMoneyPrice &price = MyMoneyFile::instance()->price(security.id(), currency.id()); QPointer calc = new KCurrencyCalculator(security, currency, MyMoneyMoney::ONE, price.rate(currency.id()), price.date(), MyMoneyMoney::precToDenom(security.pricePrecision())); calc->setupPriceEditor(); // The dialog takes care of adding the price if necessary calc->exec(); delete calc; } catch (const MyMoneyException &e) { qDebug("Error in price update: %s", qPrintable(e.what())); } } } void KMyMoneyApp::createSchedule(MyMoneySchedule newSchedule, MyMoneyAccount& newAccount) { MyMoneyFile* file = MyMoneyFile::instance(); // Add the schedule only if one exists // // Remember to modify the first split to reference the newly created account if (!newSchedule.name().isEmpty()) { try { // We assume at least 2 splits in the transaction MyMoneyTransaction t = newSchedule.transaction(); if (t.splitCount() < 2) { throw MYMONEYEXCEPTION("Transaction for schedule has less than 2 splits!"); } MyMoneyFileTransaction ft; try { file->addSchedule(newSchedule); // in case of a loan account, we keep a reference to this // schedule in the account if (newAccount.accountType() == eMyMoney::Account::Loan || newAccount.accountType() == eMyMoney::Account::AssetLoan) { newAccount.setValue("schedule", newSchedule.id()); file->modifyAccount(newAccount); } ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to add scheduled transaction: %1", e.what())); } } catch (const MyMoneyException &e) { KMessageBox::information(this, i18n("Unable to add scheduled transaction: %1", e.what())); } } } void KMyMoneyApp::slotAccountDelete() { if (d->m_selectedAccount.id().isEmpty()) return; // need an account ID MyMoneyFile* file = MyMoneyFile::instance(); // can't delete standard accounts or account which still have transactions assigned if (file->isStandardAccount(d->m_selectedAccount.id())) return; // check if the account is referenced by a transaction or schedule QBitArray skip((int)eStorage::Reference::Count); skip.fill(false); skip.setBit((int)eStorage::Reference::Account); skip.setBit((int)eStorage::Reference::Institution); skip.setBit((int)eStorage::Reference::Payee); skip.setBit((int)eStorage::Reference::Tag); skip.setBit((int)eStorage::Reference::Security); skip.setBit((int)eStorage::Reference::Currency); skip.setBit((int)eStorage::Reference::Price); bool hasReference = file->isReferenced(d->m_selectedAccount, skip); // make sure we only allow transactions in a 'category' (income/expense account) switch (d->m_selectedAccount.accountType()) { case eMyMoney::Account::Income: case eMyMoney::Account::Expense: break; default: // if the account is still referenced if (hasReference) { return; } break; } // if we get here and still have transactions referencing the account, we // need to check with the user to possibly re-assign them to a different account bool needAskUser = true; bool exit = false; MyMoneyFileTransaction ft; if (hasReference) { // show transaction reassignment dialog needAskUser = false; KCategoryReassignDlg* dlg = new KCategoryReassignDlg(this); QString categoryId = dlg->show(d->m_selectedAccount); delete dlg; // and kill the dialog if (categoryId.isEmpty()) return; // the user aborted the dialog, so let's abort as well MyMoneyAccount newCategory = file->account(categoryId); try { { KMSTATUS(i18n("Adjusting transactions...")); /* d->m_selectedAccount.id() is the old id, categoryId the new one Now search all transactions and schedules that reference d->m_selectedAccount.id() and replace that with categoryId. */ // get the list of all transactions that reference the old account MyMoneyTransactionFilter filter(d->m_selectedAccount.id()); filter.setReportAllSplits(false); QList tlist; QList::iterator it_t; file->transactionList(tlist, filter); slotStatusProgressBar(0, tlist.count()); int cnt = 0; for (it_t = tlist.begin(); it_t != tlist.end(); ++it_t) { slotStatusProgressBar(++cnt, 0); MyMoneyTransaction t = (*it_t); if (t.replaceId(categoryId, d->m_selectedAccount.id())) file->modifyTransaction(t); } slotStatusProgressBar(tlist.count(), 0); } // now fix all schedules { KMSTATUS(i18n("Adjusting scheduled transactions...")); QList slist = file->scheduleList(d->m_selectedAccount.id()); QList::iterator it_s; int cnt = 0; slotStatusProgressBar(0, slist.count()); for (it_s = slist.begin(); it_s != slist.end(); ++it_s) { slotStatusProgressBar(++cnt, 0); MyMoneySchedule sch = (*it_s); if (sch.replaceId(categoryId, d->m_selectedAccount.id())) { file->modifySchedule(sch); } } slotStatusProgressBar(slist.count(), 0); } // now fix all budgets { KMSTATUS(i18n("Adjusting budgets...")); QList blist = file->budgetList(); QList::const_iterator it_b; for (it_b = blist.constBegin(); it_b != blist.constEnd(); ++it_b) { if ((*it_b).hasReferenceTo(d->m_selectedAccount.id())) { MyMoneyBudget b = (*it_b); MyMoneyBudget::AccountGroup fromBudget = b.account(d->m_selectedAccount.id()); MyMoneyBudget::AccountGroup toBudget = b.account(categoryId); toBudget += fromBudget; b.setAccount(toBudget, categoryId); b.removeReference(d->m_selectedAccount.id()); file->modifyBudget(b); } } slotStatusProgressBar(blist.count(), 0); } } catch (MyMoneyException &e) { KMessageBox::error(this, i18n("Unable to exchange category %1 with category %2. Reason: %3", d->m_selectedAccount.name(), newCategory.name(), e.what())); exit = true; } slotStatusProgressBar(-1, -1); } if (exit) return; // retain the account name for a possible later usage in the error message box // since the account removal notifies the views the selected account can be changed // so we make sure by doing this that we display the correct name in the error message QString selectedAccountName = d->m_selectedAccount.name(); // at this point, we must not have a reference to the account // to be deleted anymore switch (d->m_selectedAccount.accountGroup()) { // special handling for categories to allow deleting of empty subcategories case eMyMoney::Account::Income: case eMyMoney::Account::Expense: { // open a compound statement here to be able to declare variables // which would otherwise not work within a case label. // case A - only a single, unused category without subcats selected if (d->m_selectedAccount.accountList().isEmpty()) { if (!needAskUser || (KMessageBox::questionYesNo(this, QString("") + i18n("Do you really want to delete category %1?", selectedAccountName) + QString("")) == KMessageBox::Yes)) { try { file->removeAccount(d->m_selectedAccount); d->m_selectedAccount.clearId(); slotUpdateActions(); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::error(this, QString("") + i18n("Unable to delete category %1. Cause: %2", selectedAccountName, e.what()) + QString("")); } } return; } // case B - we have some subcategories, maybe the user does not want to // delete them all, but just the category itself? MyMoneyAccount parentAccount = file->account(d->m_selectedAccount.parentAccountId()); QStringList accountsToReparent; int result = KMessageBox::questionYesNoCancel(this, QString("") + i18n("Do you want to delete category %1 with all its sub-categories or only " "the category itself? If you only delete the category itself, all its sub-categories " "will be made sub-categories of %2.", selectedAccountName, parentAccount.name()) + QString(""), QString(), KGuiItem(i18n("Delete all")), KGuiItem(i18n("Just the category"))); if (result == KMessageBox::Cancel) return; // cancel pressed? ok, no delete then... // "No" means "Just the category" and that means we need to reparent all subaccounts bool need_confirmation = false; // case C - User only wants to delete the category itself if (result == KMessageBox::No) accountsToReparent = d->m_selectedAccount.accountList(); else { // case D - User wants to delete all subcategories, now check all subcats of // d->m_selectedAccount and remember all that cannot be deleted and // must be "reparented" foreach (const auto accountID, d->m_selectedAccount.accountList()) { // reparent account if a transaction is assigned if (file->transactionCount(accountID) != 0) accountsToReparent.push_back(accountID); else if (!file->account(accountID).accountList().isEmpty()) { // or if we have at least one sub-account that is used for transactions if (!file->hasOnlyUnusedAccounts(file->account(accountID).accountList())) { accountsToReparent.push_back(accountID); //qDebug() << "subaccount not empty"; } } } if (!accountsToReparent.isEmpty()) need_confirmation = true; } if (!accountsToReparent.isEmpty() && need_confirmation) { if (KMessageBox::questionYesNo(this, i18n("

Some sub-categories of category %1 cannot " "be deleted, because they are still used. They will be made sub-categories of %2. Proceed?

", selectedAccountName, parentAccount.name())) != KMessageBox::Yes) { return; // user gets wet feet... } } // all good, now first reparent selected sub-categories try { MyMoneyAccount parent = file->account(d->m_selectedAccount.parentAccountId()); for (QStringList::const_iterator it = accountsToReparent.constBegin(); it != accountsToReparent.constEnd(); ++it) { MyMoneyAccount child = file->account(*it); file->reparentAccount(child, parent); } // reload the account because the sub-account list might have changed d->m_selectedAccount = file->account(d->m_selectedAccount.id()); // now recursively delete remaining sub-categories file->removeAccountList(d->m_selectedAccount.accountList()); // don't forget to update d->m_selectedAccount, because we still have a copy of // the old account list, which is no longer valid d->m_selectedAccount = file->account(d->m_selectedAccount.id()); } catch (const MyMoneyException &e) { KMessageBox::error(this, QString("") + i18n("Unable to delete a sub-category of category %1. Reason: %2", selectedAccountName, e.what()) + QString("")); return; } } break; // the category/account is deleted after the switch default: if (!d->m_selectedAccount.accountList().isEmpty()) return; // can't delete accounts which still have subaccounts if (KMessageBox::questionYesNo(this, i18n("

Do you really want to " "delete account %1?

", selectedAccountName)) != KMessageBox::Yes) { return; // ok, you don't want to? why did you click then, hmm? } } // switch; try { file->removeAccount(d->m_selectedAccount); d->m_selectedAccount.clearId(); slotUpdateActions(); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::error(this, i18n("Unable to delete account '%1'. Cause: %2", selectedAccountName, e.what())); } } void KMyMoneyApp::slotAccountEdit() { MyMoneyFile* file = MyMoneyFile::instance(); if (!d->m_selectedAccount.id().isEmpty()) { if (!file->isStandardAccount(d->m_selectedAccount.id())) { if (d->m_selectedAccount.accountType() != eMyMoney::Account::Loan && d->m_selectedAccount.accountType() != eMyMoney::Account::AssetLoan) { QString caption; bool category = false; switch (d->m_selectedAccount.accountGroup()) { default: caption = i18n("Edit account '%1'", d->m_selectedAccount.name()); break; case eMyMoney::Account::Expense: case eMyMoney::Account::Income: caption = i18n("Edit category '%1'", d->m_selectedAccount.name()); category = true; break; } // set a status message so that the application can't be closed until the editing is done slotStatusMsg(caption); QString tid = file->openingBalanceTransaction(d->m_selectedAccount); MyMoneyTransaction t; MyMoneySplit s0, s1; QPointer dlg = new KNewAccountDlg(d->m_selectedAccount, true, category, 0, caption); if (category) { dlg->setOpeningBalanceShown(false); dlg->setOpeningDateShown(false); tid.clear(); } else { if (!tid.isEmpty()) { try { t = file->transaction(tid); s0 = t.splitByAccount(d->m_selectedAccount.id()); s1 = t.splitByAccount(d->m_selectedAccount.id(), false); dlg->setOpeningBalance(s0.shares()); if (d->m_selectedAccount.accountGroup() == eMyMoney::Account::Liability) { dlg->setOpeningBalance(-s0.shares()); } } catch (const MyMoneyException &e) { qDebug() << "Error retrieving opening balance transaction " << tid << ": " << e.what() << "\n"; tid.clear(); } } } // check for online modules QMap::const_iterator it_plugin = d->m_onlinePlugins.constEnd(); const MyMoneyKeyValueContainer& kvp = d->m_selectedAccount.onlineBankingSettings(); if (!kvp["provider"].isEmpty()) { // if we have an online provider for this account, we need to check // that we have the corresponding plugin. If that exists, we ask it // to provide an additional tab for the account editor. it_plugin = d->m_onlinePlugins.constFind(kvp["provider"]); if (it_plugin != d->m_onlinePlugins.constEnd()) { QString name; QWidget *w = (*it_plugin)->accountConfigTab(d->m_selectedAccount, name); dlg->addTab(w, name); } } if (dlg != 0 && dlg->exec() == QDialog::Accepted) { try { MyMoneyFileTransaction ft; MyMoneyAccount account = dlg->account(); MyMoneyAccount parent = dlg->parentAccount(); if (it_plugin != d->m_onlinePlugins.constEnd()) { account.setOnlineBankingSettings((*it_plugin)->onlineBankingSettings(account.onlineBankingSettings())); } MyMoneyMoney bal = dlg->openingBalance(); if (d->m_selectedAccount.accountGroup() == eMyMoney::Account::Liability) { bal = -bal; } // we need to modify first, as reparent would override all other changes file->modifyAccount(account); if (account.parentAccountId() != parent.id()) { file->reparentAccount(account, parent); } if (!tid.isEmpty() && dlg->openingBalance().isZero()) { file->removeTransaction(t); } else if (!tid.isEmpty() && !dlg->openingBalance().isZero()) { s0.setShares(bal); s0.setValue(bal); t.modifySplit(s0); s1.setShares(-bal); s1.setValue(-bal); t.modifySplit(s1); t.setPostDate(account.openingDate()); file->modifyTransaction(t); } else if (tid.isEmpty() && !dlg->openingBalance().isZero()) { file->createOpeningBalanceTransaction(d->m_selectedAccount, bal); } ft.commit(); // reload the account object as it might have changed in the meantime slotSelectAccount(file->account(account.id())); } catch (const MyMoneyException &e) { KMessageBox::error(this, i18n("Unable to modify account '%1'. Cause: %2", d->m_selectedAccount.name(), e.what())); } } delete dlg; ready(); } else { QPointer wizard = new KEditLoanWizard(d->m_selectedAccount); connect(wizard, SIGNAL(newCategory(MyMoneyAccount&)), this, SLOT(slotCategoryNew(MyMoneyAccount&))); connect(wizard, SIGNAL(createPayee(QString,QString&)), this, SLOT(slotPayeeNew(QString,QString&))); if (wizard->exec() == QDialog::Accepted && wizard != 0) { MyMoneySchedule sch; try { MyMoneySchedule sch = file->schedule(d->m_selectedAccount.value("schedule").toLatin1()); } catch (const MyMoneyException &e) { qDebug() << "schedule" << d->m_selectedAccount.value("schedule").toLatin1() << "not found"; } if (!(d->m_selectedAccount == wizard->account()) || !(sch == wizard->schedule())) { MyMoneyFileTransaction ft; try { file->modifyAccount(wizard->account()); if (!sch.id().isEmpty()) { sch = wizard->schedule(); } try { file->schedule(sch.id()); file->modifySchedule(sch); ft.commit(); } catch (const MyMoneyException &) { try { if(sch.transaction().splitCount() >= 2) { file->addSchedule(sch); } ft.commit(); } catch (const MyMoneyException &e) { qDebug("Cannot add schedule: '%s'", qPrintable(e.what())); } } } catch (const MyMoneyException &e) { qDebug("Unable to modify account %s: '%s'", qPrintable(d->m_selectedAccount.name()), qPrintable(e.what())); } } } delete wizard; } } } } QList > KMyMoneyApp::Private::automaticReconciliation(const MyMoneyAccount &account, const QList > &transactions, const MyMoneyMoney &amount) { static const int NR_OF_STEPS_LIMIT = 300000; static const int PROGRESSBAR_STEPS = 1000; QList > result = transactions; KMSTATUS(i18n("Running automatic reconciliation")); int progressBarIndex = 0; kmymoney->slotStatusProgressBar(progressBarIndex, NR_OF_STEPS_LIMIT / PROGRESSBAR_STEPS); // optimize the most common case - all transactions should be cleared QListIterator > itTransactionSplitResult(result); MyMoneyMoney transactionsBalance; while (itTransactionSplitResult.hasNext()) { const QPair &transactionSplit = itTransactionSplitResult.next(); transactionsBalance += transactionSplit.second.shares(); } if (amount == transactionsBalance) { result = transactions; return result; } kmymoney->slotStatusProgressBar(progressBarIndex++, 0); // only one transaction is uncleared itTransactionSplitResult.toFront(); int index = 0; while (itTransactionSplitResult.hasNext()) { const QPair &transactionSplit = itTransactionSplitResult.next(); if (transactionsBalance - transactionSplit.second.shares() == amount) { result.removeAt(index); return result; } index++; } kmymoney->slotStatusProgressBar(progressBarIndex++, 0); // more than one transaction is uncleared - apply the algorithm result.clear(); const MyMoneySecurity &security = MyMoneyFile::instance()->security(account.currencyId()); double precision = 0.1 / account.fraction(security); QList sumList; sumList << MyMoneyMoney(); QMap > > sumToComponentsMap; // compute the possible matches QListIterator > itTransactionSplit(transactions); while (itTransactionSplit.hasNext()) { const QPair &transactionSplit = itTransactionSplit.next(); QListIterator itSum(sumList); QList tempList; while (itSum.hasNext()) { const MyMoneyMoney &sum = itSum.next(); QList > splitIds; splitIds << qMakePair(transactionSplit.first.id(), transactionSplit.second.id()); if (sumToComponentsMap.contains(sum)) { if (sumToComponentsMap.value(sum).contains(qMakePair(transactionSplit.first.id(), transactionSplit.second.id()))) { continue; } splitIds.append(sumToComponentsMap.value(sum)); } tempList << transactionSplit.second.shares() + sum; sumToComponentsMap[transactionSplit.second.shares() + sum] = splitIds; int size = sumToComponentsMap.size(); if (size % PROGRESSBAR_STEPS == 0) { kmymoney->slotStatusProgressBar(progressBarIndex++, 0); } if (size > NR_OF_STEPS_LIMIT) { return result; // it's taking too much resources abort the algorithm } } QList unionList; unionList.append(tempList); unionList.append(sumList); qSort(unionList); sumList.clear(); MyMoneyMoney smallestSumFromUnion = unionList.first(); sumList.append(smallestSumFromUnion); QListIterator itUnion(unionList); while (itUnion.hasNext()) { MyMoneyMoney sumFromUnion = itUnion.next(); if (smallestSumFromUnion < MyMoneyMoney(1 - precision / transactions.size())*sumFromUnion) { smallestSumFromUnion = sumFromUnion; sumList.append(sumFromUnion); } } } kmymoney->slotStatusProgressBar(NR_OF_STEPS_LIMIT / PROGRESSBAR_STEPS, 0); if (sumToComponentsMap.contains(amount)) { QListIterator > itTransactionSplit(transactions); while (itTransactionSplit.hasNext()) { const QPair &transactionSplit = itTransactionSplit.next(); const QList > &splitIds = sumToComponentsMap.value(amount); if (splitIds.contains(qMakePair(transactionSplit.first.id(), transactionSplit.second.id()))) { result.append(transactionSplit); } } } #ifdef KMM_DEBUG qDebug("For the amount %s a number of %d possible sums where computed from the set of %d transactions: ", qPrintable(MyMoneyUtils::formatMoney(amount, security)), sumToComponentsMap.size(), transactions.size()); #endif kmymoney->slotStatusProgressBar(-1, -1); return result; } void KMyMoneyApp::slotAccountReconcileStart() { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyAccount account; // we cannot reconcile standard accounts if (!file->isStandardAccount(d->m_selectedAccount.id())) { // check if we can reconcile this account // it make's sense for asset and liability accounts try { // check if we have overdue schedules for this account QList schedules = file->scheduleList(d->m_selectedAccount.id(), eMyMoney::Schedule::Type::Any, eMyMoney::Schedule::Occurrence::Any, eMyMoney::Schedule::PaymentType::Any, QDate(), QDate(), true); if (schedules.count() > 0) { if (KMessageBox::questionYesNo(this, i18n("KMyMoney has detected some overdue scheduled transactions for this account. Do you want to enter those scheduled transactions now?"), i18n("Scheduled transactions found")) == KMessageBox::Yes) { QMap skipMap; bool processedOne; eDialogs::ScheduleResultCode rc = eDialogs::ScheduleResultCode::Enter; do { processedOne = false; QList::const_iterator it_sch; for (it_sch = schedules.constBegin(); (rc != eDialogs::ScheduleResultCode::Cancel) && (it_sch != schedules.constEnd()); ++it_sch) { MyMoneySchedule sch(*(it_sch)); // and enter it if it is not on the skip list if (skipMap.find((*it_sch).id()) == skipMap.end()) { rc = enterSchedule(sch, false, true); if (rc == eDialogs::ScheduleResultCode::Ignore) { skipMap[(*it_sch).id()] = true; } } } // reload list (maybe this schedule needs to be added again) schedules = file->scheduleList(d->m_selectedAccount.id(), eMyMoney::Schedule::Type::Any, eMyMoney::Schedule::Occurrence::Any, eMyMoney::Schedule::PaymentType::Any, QDate(), QDate(), true); } while (processedOne); } } account = file->account(d->m_selectedAccount.id()); // get rid of previous run. delete d->m_endingBalanceDlg; d->m_endingBalanceDlg = new KEndingBalanceDlg(account, this); if (account.isAssetLiability()) { connect(d->m_endingBalanceDlg, SIGNAL(createPayee(QString,QString&)), this, SLOT(slotPayeeNew(QString,QString&))); connect(d->m_endingBalanceDlg, SIGNAL(createCategory(MyMoneyAccount&,MyMoneyAccount)), this, SLOT(slotCategoryNew(MyMoneyAccount&,MyMoneyAccount))); if (d->m_endingBalanceDlg->exec() == QDialog::Accepted) { if (KMyMoneyGlobalSettings::autoReconciliation()) { MyMoneyMoney startBalance = d->m_endingBalanceDlg->previousBalance(); MyMoneyMoney endBalance = d->m_endingBalanceDlg->endingBalance(); QDate endDate = d->m_endingBalanceDlg->statementDate(); QList > transactionList; MyMoneyTransactionFilter filter(account.id()); filter.addState((int)eMyMoney::TransactionFilter::State::Cleared); filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled); filter.setDateFilter(QDate(), endDate); filter.setConsiderCategory(false); filter.setReportAllSplits(true); file->transactionList(transactionList, filter); QList > result = d->automaticReconciliation(account, transactionList, endBalance - startBalance); if (!result.empty()) { QString message = i18n("KMyMoney has detected transactions matching your reconciliation data.\nWould you like KMyMoney to clear these transactions for you?"); if (KMessageBox::questionYesNo(this, message, i18n("Automatic reconciliation"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "AcceptAutomaticReconciliation") == KMessageBox::Yes) { // mark the transactions cleared KMyMoneyRegister::SelectedTransactions oldSelection = d->m_selectedTransactions; d->m_selectedTransactions.clear(); QListIterator > itTransactionSplitResult(result); while (itTransactionSplitResult.hasNext()) { const QPair &transactionSplit = itTransactionSplitResult.next(); - d->m_selectedTransactions.append(KMyMoneyRegister::SelectedTransaction(transactionSplit.first, transactionSplit.second, QString())); + d->m_selectedTransactions.append(KMyMoneyRegister::SelectedTransaction(transactionSplit.first, transactionSplit.second)); } // mark all transactions in d->m_selectedTransactions as 'Cleared' markTransaction(eMyMoney::Split::State::Cleared); d->m_selectedTransactions = oldSelection; } } } if (d->m_myMoneyView->startReconciliation(account, d->m_endingBalanceDlg->statementDate(), d->m_endingBalanceDlg->endingBalance())) { // check if the user requests us to create interest // or charge transactions. MyMoneyTransaction ti = d->m_endingBalanceDlg->interestTransaction(); MyMoneyTransaction tc = d->m_endingBalanceDlg->chargeTransaction(); MyMoneyFileTransaction ft; try { if (ti != MyMoneyTransaction()) { MyMoneyFile::instance()->addTransaction(ti); } if (tc != MyMoneyTransaction()) { MyMoneyFile::instance()->addTransaction(tc); } ft.commit(); } catch (const MyMoneyException &e) { qWarning("interest transaction not stored: '%s'", qPrintable(e.what())); } // reload the account object as it might have changed in the meantime d->m_reconciliationAccount = file->account(account.id()); slotUpdateActions(); } } } } catch (const MyMoneyException &) { } } } void KMyMoneyApp::slotAccountReconcileFinish() { MyMoneyFile* file = MyMoneyFile::instance(); if (!d->m_reconciliationAccount.id().isEmpty()) { // retrieve list of all transactions that are not reconciled or cleared QList > transactionList; MyMoneyTransactionFilter filter(d->m_reconciliationAccount.id()); filter.addState((int)eMyMoney::TransactionFilter::State::Cleared); filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled); filter.setDateFilter(QDate(), d->m_endingBalanceDlg->statementDate()); filter.setConsiderCategory(false); filter.setReportAllSplits(true); file->transactionList(transactionList, filter); MyMoneyMoney balance = MyMoneyFile::instance()->balance(d->m_reconciliationAccount.id(), d->m_endingBalanceDlg->statementDate()); MyMoneyMoney actBalance, clearedBalance; actBalance = clearedBalance = balance; // walk the list of transactions to figure out the balance(s) QList >::const_iterator it; for (it = transactionList.constBegin(); it != transactionList.constEnd(); ++it) { if ((*it).second.reconcileFlag() == eMyMoney::Split::State::NotReconciled) { clearedBalance -= (*it).second.shares(); } } if (d->m_endingBalanceDlg->endingBalance() != clearedBalance) { QString message = i18n("You are about to finish the reconciliation of this account with a difference between your bank statement and the transactions marked as cleared.\n" "Are you sure you want to finish the reconciliation?"); if (KMessageBox::questionYesNo(this, message, i18n("Confirm end of reconciliation"), KStandardGuiItem::yes(), KStandardGuiItem::no()) == KMessageBox::No) return; } MyMoneyFileTransaction ft; // refresh object d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id()); // Turn off reconciliation mode d->m_myMoneyView->finishReconciliation(d->m_reconciliationAccount); // only update the last statement balance here, if we haven't a newer one due // to download of online statements. if (d->m_reconciliationAccount.value("lastImportedTransactionDate").isEmpty() || QDate::fromString(d->m_reconciliationAccount.value("lastImportedTransactionDate"), Qt::ISODate) < d->m_endingBalanceDlg->statementDate()) { d->m_reconciliationAccount.setValue("lastStatementBalance", d->m_endingBalanceDlg->endingBalance().toString()); // in case we override the last statement balance here, we have to make sure // that we don't show the online balance anymore, as it might be different d->m_reconciliationAccount.deletePair("lastImportedTransactionDate"); } d->m_reconciliationAccount.setLastReconciliationDate(d->m_endingBalanceDlg->statementDate()); // keep a record of this reconciliation d->m_reconciliationAccount.addReconciliation(d->m_endingBalanceDlg->statementDate(), d->m_endingBalanceDlg->endingBalance()); d->m_reconciliationAccount.deletePair("lastReconciledBalance"); d->m_reconciliationAccount.deletePair("statementBalance"); d->m_reconciliationAccount.deletePair("statementDate"); try { // update the account data file->modifyAccount(d->m_reconciliationAccount); /* // collect the list of cleared splits for this account filter.clear(); filter.addAccount(d->m_reconciliationAccount.id()); filter.addState(eMyMoney::TransactionFilter::Cleared); filter.setConsiderCategory(false); filter.setReportAllSplits(true); file->transactionList(transactionList, filter); */ // walk the list of transactions/splits and mark the cleared ones as reconciled QList >::iterator it; for (it = transactionList.begin(); it != transactionList.end(); ++it) { MyMoneySplit sp = (*it).second; // skip the ones that are not marked cleared if (sp.reconcileFlag() != eMyMoney::Split::State::Cleared) continue; // always retrieve a fresh copy of the transaction because we // might have changed it already with another split MyMoneyTransaction t = file->transaction((*it).first.id()); sp.setReconcileFlag(eMyMoney::Split::State::Reconciled); sp.setReconcileDate(d->m_endingBalanceDlg->statementDate()); t.modifySplit(sp); // update the engine ... file->modifyTransaction(t); // ... and the list (*it) = qMakePair(t, sp); } ft.commit(); // reload account data from engine as the data might have changed in the meantime d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id()); emit accountReconciled(d->m_reconciliationAccount, d->m_endingBalanceDlg->statementDate(), d->m_endingBalanceDlg->previousBalance(), d->m_endingBalanceDlg->endingBalance(), transactionList); } catch (const MyMoneyException &) { qDebug("Unexpected exception when setting cleared to reconcile"); } } // Turn off reconciliation mode d->m_reconciliationAccount = MyMoneyAccount(); slotUpdateActions(); } void KMyMoneyApp::slotAccountReconcilePostpone() { MyMoneyFileTransaction ft; MyMoneyFile* file = MyMoneyFile::instance(); if (!d->m_reconciliationAccount.id().isEmpty()) { // refresh object d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id()); // Turn off reconciliation mode d->m_myMoneyView->finishReconciliation(d->m_reconciliationAccount); d->m_reconciliationAccount.setValue("lastReconciledBalance", d->m_endingBalanceDlg->previousBalance().toString()); d->m_reconciliationAccount.setValue("statementBalance", d->m_endingBalanceDlg->endingBalance().toString()); d->m_reconciliationAccount.setValue("statementDate", d->m_endingBalanceDlg->statementDate().toString(Qt::ISODate)); try { file->modifyAccount(d->m_reconciliationAccount); ft.commit(); d->m_reconciliationAccount = MyMoneyAccount(); slotUpdateActions(); } catch (const MyMoneyException &) { qDebug("Unexpected exception when setting last reconcile info into account"); ft.rollback(); d->m_reconciliationAccount = file->account(d->m_reconciliationAccount.id()); } } } void KMyMoneyApp::slotAccountOpenEmpty() { slotAccountOpen(MyMoneyAccount()); } void KMyMoneyApp::slotAccountOpen(const MyMoneyObject& obj) { if (typeid(obj) != typeid(MyMoneyAccount)) return; MyMoneyFile* file = MyMoneyFile::instance(); QString id = d->m_selectedAccount.id(); // if the caller passed a non-empty object, we need to select that if (!obj.id().isEmpty()) { id = obj.id(); } // we cannot reconcile standard accounts if (!file->isStandardAccount(id)) { // check if we can open this account // currently it make's sense for asset and liability accounts try { MyMoneyAccount account = file->account(id); d->m_myMoneyView->slotLedgerSelected(account.id()); } catch (const MyMoneyException &) { } } } void KMyMoneyApp::enableCloseAccountAction(const MyMoneyAccount& acc) { QAction *a = actionCollection()->action(s_Actions[Action::AccountClose]); switch (canCloseAccount(acc)) { case KMyMoneyUtils::AccountCanClose: { a->setEnabled(true); break; } case KMyMoneyUtils::AccountBalanceNonZero: { a->setEnabled(false); a->setToolTip(i18n("The balance of the account must be zero before the account can be closed")); break; } case KMyMoneyUtils::AccountChildrenOpen: { a->setEnabled(false); a->setToolTip(i18n("All subaccounts must be closed before the account can be closed")); break; } case KMyMoneyUtils::AccountScheduleReference: { a->setEnabled(false); a->setToolTip(i18n("This account is still included in an active schedule")); break; } } } KMyMoneyUtils::CanCloseAccountCodeE KMyMoneyApp::canCloseAccount(const MyMoneyAccount& acc) const { // balance must be zero if (!acc.balance().isZero()) return KMyMoneyUtils::AccountBalanceNonZero; // all children must be already closed QStringList::const_iterator it_a; for (it_a = acc.accountList().constBegin(); it_a != acc.accountList().constEnd(); ++it_a) { MyMoneyAccount a = MyMoneyFile::instance()->account(*it_a); if (!a.isClosed()) { return KMyMoneyUtils::AccountChildrenOpen; } } // there must be no unfinished schedule referencing the account QList list = MyMoneyFile::instance()->scheduleList(); QList::const_iterator it_l; for (it_l = list.constBegin(); it_l != list.constEnd(); ++it_l) { if ((*it_l).isFinished()) continue; if ((*it_l).hasReferenceTo(acc.id())) return KMyMoneyUtils::AccountScheduleReference; } return KMyMoneyUtils::AccountCanClose; } void KMyMoneyApp::slotAccountClose() { MyMoneyAccount a; if (!d->m_selectedInvestment.id().isEmpty()) a = d->m_selectedInvestment; else if (!d->m_selectedAccount.id().isEmpty()) a = d->m_selectedAccount; if (a.id().isEmpty()) return; // need an account ID MyMoneyFileTransaction ft; try { a.setClosed(true); MyMoneyFile::instance()->modifyAccount(a); ft.commit(); if (KMyMoneyGlobalSettings::hideClosedAccounts()) { KMessageBox::information(this, QString("") + i18n("You have closed this account. It remains in the system because you have transactions which still refer to it, but it is not shown in the views. You can make it visible again by going to the View menu and selecting Show all accounts or by deselecting the Do not show closed accounts setting.") + QString(""), i18n("Information"), "CloseAccountInfo"); } } catch (const MyMoneyException &) { } } void KMyMoneyApp::slotAccountReopen() { MyMoneyAccount a; if (!d->m_selectedInvestment.id().isEmpty()) a = d->m_selectedInvestment; else if (!d->m_selectedAccount.id().isEmpty()) a = d->m_selectedAccount; if (a.id().isEmpty()) return; // need an account ID MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyFileTransaction ft; try { while (a.isClosed()) { a.setClosed(false); file->modifyAccount(a); a = file->account(a.parentAccountId()); } ft.commit(); } catch (const MyMoneyException &) { } } void KMyMoneyApp::slotReparentAccount(const MyMoneyAccount& _src, const MyMoneyInstitution& _dst) { MyMoneyAccount src(_src); src.setInstitutionId(_dst.id()); MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->modifyAccount(src); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("

%1 cannot be moved to institution %2. Reason: %3

", src.name(), _dst.name(), e.what())); } } void KMyMoneyApp::slotReparentAccount(const MyMoneyAccount& _src, const MyMoneyAccount& _dst) { MyMoneyAccount src(_src); MyMoneyAccount dst(_dst); MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->reparentAccount(src, dst); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("

%1 cannot be moved to %2. Reason: %3

", src.name(), dst.name(), e.what())); } } void KMyMoneyApp::slotAccountTransactionReport() { // Generate a transaction report that contains transactions for only the // currently selected account. if (!d->m_selectedAccount.id().isEmpty()) { MyMoneyReport report( MyMoneyReport::eAccount, MyMoneyReport::eQCnumber | MyMoneyReport::eQCpayee | MyMoneyReport::eQCcategory, eMyMoney::TransactionFilter::Date::YearToDate, MyMoneyReport::eDetailAll, i18n("%1 YTD Account Transactions", d->m_selectedAccount.name()), i18n("Generated Report") ); report.setGroup(i18n("Transactions")); report.addAccount(d->m_selectedAccount.id()); d->m_myMoneyView->slotShowReport(report); } } void KMyMoneyApp::slotScheduleNew() { slotScheduleNew(MyMoneyTransaction()); } void KMyMoneyApp::slotScheduleNew(const MyMoneyTransaction& _t, eMyMoney::Schedule::Occurrence occurrence) { MyMoneySchedule schedule; schedule.setOccurrence(occurrence); // if the schedule is based on an existing transaction, // we take the post date and project it to the next // schedule in a month. if (_t != MyMoneyTransaction()) { MyMoneyTransaction t(_t); schedule.setTransaction(t); if (occurrence != eMyMoney::Schedule::Occurrence::Once) schedule.setNextDueDate(schedule.nextPayment(t.postDate())); } QPointer dlg = new KEditScheduleDlg(schedule, this); TransactionEditor* transactionEditor = dlg->startEdit(); if (transactionEditor) { KMyMoneyMVCCombo::setSubstringSearchForChildren(dlg, !KMyMoneySettings::stringMatchFromStart()); if (dlg->exec() == QDialog::Accepted && dlg != 0) { MyMoneyFileTransaction ft; try { schedule = dlg->schedule(); MyMoneyFile::instance()->addSchedule(schedule); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::error(this, i18n("Unable to add scheduled transaction: %1", e.what()), i18n("Add scheduled transaction")); } } } delete transactionEditor; delete dlg; } void KMyMoneyApp::slotScheduleEdit() { if (!d->m_selectedSchedule.id().isEmpty()) { try { MyMoneySchedule schedule = MyMoneyFile::instance()->schedule(d->m_selectedSchedule.id()); KEditScheduleDlg* sched_dlg = 0; KEditLoanWizard* loan_wiz = 0; switch (schedule.type()) { case eMyMoney::Schedule::Type::Bill: case eMyMoney::Schedule::Type::Deposit: case eMyMoney::Schedule::Type::Transfer: sched_dlg = new KEditScheduleDlg(schedule, this); d->m_transactionEditor = sched_dlg->startEdit(); if (d->m_transactionEditor) { KMyMoneyMVCCombo::setSubstringSearchForChildren(sched_dlg, !KMyMoneySettings::stringMatchFromStart()); if (sched_dlg->exec() == QDialog::Accepted) { MyMoneyFileTransaction ft; try { MyMoneySchedule sched = sched_dlg->schedule(); // Check whether the new Schedule Date // is at or before the lastPaymentDate // If it is, ask the user whether to clear the // lastPaymentDate const QDate& next = sched.nextDueDate(); const QDate& last = sched.lastPayment(); if (next.isValid() && last.isValid() && next <= last) { // Entered a date effectively no later // than previous payment. Date would be // updated automatically so we probably // want to clear it. Let's ask the user. if (KMessageBox::questionYesNo(this, QString("") + i18n("You have entered a scheduled transaction date of %1. Because the scheduled transaction was last paid on %2, KMyMoney will automatically adjust the scheduled transaction date to the next date unless the last payment date is reset. Do you want to reset the last payment date?", QLocale().toString(next, QLocale::ShortFormat), QLocale().toString(last, QLocale::ShortFormat)) + QString(""), i18n("Reset Last Payment Date"), KStandardGuiItem::yes(), KStandardGuiItem::no()) == KMessageBox::Yes) { sched.setLastPayment(QDate()); } } MyMoneyFile::instance()->modifySchedule(sched); // delete the editor before we emit the dataChanged() signal from the // engine. Calling this twice in a row does not hurt. deleteTransactionEditor(); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unable to modify scheduled transaction '%1'", d->m_selectedSchedule.name()), e.what()); } } deleteTransactionEditor(); } delete sched_dlg; break; case eMyMoney::Schedule::Type::LoanPayment: loan_wiz = new KEditLoanWizard(schedule.account(2)); connect(loan_wiz, SIGNAL(newCategory(MyMoneyAccount&)), this, SLOT(slotCategoryNew(MyMoneyAccount&))); connect(loan_wiz, SIGNAL(createPayee(QString,QString&)), this, SLOT(slotPayeeNew(QString,QString&))); if (loan_wiz->exec() == QDialog::Accepted) { MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->modifySchedule(loan_wiz->schedule()); MyMoneyFile::instance()->modifyAccount(loan_wiz->account()); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unable to modify scheduled transaction '%1'", d->m_selectedSchedule.name()), e.what()); } } delete loan_wiz; break; case eMyMoney::Schedule::Type::Any: break; } } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unable to modify scheduled transaction '%1'", d->m_selectedSchedule.name()), e.what()); } } } void KMyMoneyApp::slotScheduleDelete() { if (!d->m_selectedSchedule.id().isEmpty()) { MyMoneyFileTransaction ft; try { MyMoneySchedule sched = MyMoneyFile::instance()->schedule(d->m_selectedSchedule.id()); QString msg = i18n("

Are you sure you want to delete the scheduled transaction %1?

", d->m_selectedSchedule.name()); if (sched.type() == eMyMoney::Schedule::Type::LoanPayment) { msg += QString(" "); msg += i18n("In case of loan payments it is currently not possible to recreate the scheduled transaction."); } if (KMessageBox::questionYesNo(this, msg) == KMessageBox::No) return; MyMoneyFile::instance()->removeSchedule(sched); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unable to remove scheduled transaction '%1'", d->m_selectedSchedule.name()), e.what()); } } } void KMyMoneyApp::slotScheduleDuplicate() { // since we may jump here via code, we have to make sure to react only // if the action is enabled if (kmymoney->actionCollection()->action(s_Actions[Action::ScheduleDuplicate])->isEnabled()) { MyMoneySchedule sch = d->m_selectedSchedule; sch.clearId(); sch.setLastPayment(QDate()); sch.setName(i18nc("Copy of scheduled transaction name", "Copy of %1", sch.name())); // make sure that we set a valid next due date if the original next due date is invalid if (!sch.nextDueDate().isValid()) sch.setNextDueDate(QDate::currentDate()); MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->addSchedule(sch); ft.commit(); // select the new schedule in the view if (!d->m_selectedSchedule.id().isEmpty()) d->m_myMoneyView->slotScheduleSelected(sch.id()); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to duplicate scheduled transaction: '%1'", d->m_selectedSchedule.name()), e.what()); } } } void KMyMoneyApp::slotScheduleSkip() { if (!d->m_selectedSchedule.id().isEmpty()) { try { MyMoneySchedule schedule = MyMoneyFile::instance()->schedule(d->m_selectedSchedule.id()); skipSchedule(schedule); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unknown scheduled transaction '%1'", d->m_selectedSchedule.name()), e.what()); } } } void KMyMoneyApp::skipSchedule(MyMoneySchedule& schedule) { if (!schedule.id().isEmpty()) { try { schedule = MyMoneyFile::instance()->schedule(schedule.id()); if (!schedule.isFinished()) { if (schedule.occurrence() != eMyMoney::Schedule::Occurrence::Once) { QDate next = schedule.nextDueDate(); if (!schedule.isFinished() && (KMessageBox::questionYesNo(this, QString("") + i18n("Do you really want to skip the %1 transaction scheduled for %2?", schedule.name(), QLocale().toString(next, QLocale::ShortFormat)) + QString(""))) == KMessageBox::Yes) { MyMoneyFileTransaction ft; schedule.setLastPayment(next); schedule.setNextDueDate(schedule.nextPayment(next)); MyMoneyFile::instance()->modifySchedule(schedule); ft.commit(); } } } } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, QString("") + i18n("Unable to skip scheduled transaction %1.", schedule.name()) + QString(""), e.what()); } } } void KMyMoneyApp::slotScheduleEnter() { if (!d->m_selectedSchedule.id().isEmpty()) { try { MyMoneySchedule schedule = MyMoneyFile::instance()->schedule(d->m_selectedSchedule.id()); enterSchedule(schedule); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unknown scheduled transaction '%1'", d->m_selectedSchedule.name()), e.what()); } } } eDialogs::ScheduleResultCode KMyMoneyApp::enterSchedule(MyMoneySchedule& schedule, bool autoEnter, bool extendedKeys) { eDialogs::ScheduleResultCode rc = eDialogs::ScheduleResultCode::Cancel; if (!schedule.id().isEmpty()) { try { schedule = MyMoneyFile::instance()->schedule(schedule.id()); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unable to enter scheduled transaction '%1'", schedule.name()), e.what()); return rc; } QPointer dlg = new KEnterScheduleDlg(this, schedule); try { QDate origDueDate = schedule.nextDueDate(); dlg->showExtendedKeys(extendedKeys); d->m_transactionEditor = dlg->startEdit(); if (d->m_transactionEditor) { KMyMoneyMVCCombo::setSubstringSearchForChildren(dlg, !KMyMoneySettings::stringMatchFromStart()); MyMoneyTransaction torig, taccepted; d->m_transactionEditor->createTransaction(torig, dlg->transaction(), schedule.transaction().splits().isEmpty() ? MyMoneySplit() : schedule.transaction().splits().front(), true); // force actions to be available no matter what (will be updated according to the state during // slotTransactionsEnter or slotTransactionsCancel) kmymoney->actionCollection()->action(s_Actions[Action::TransactionCancel])->setEnabled(true); kmymoney->actionCollection()->action(s_Actions[Action::TransactionEnter])->setEnabled(true); KConfirmManualEnterDlg::Action action = KConfirmManualEnterDlg::ModifyOnce; if (!autoEnter || !schedule.isFixed()) { for (; dlg != 0;) { rc = eDialogs::ScheduleResultCode::Cancel; if (dlg->exec() == QDialog::Accepted && dlg != 0) { rc = dlg->resultCode(); if (rc == eDialogs::ScheduleResultCode::Enter) { d->m_transactionEditor->createTransaction(taccepted, torig, torig.splits().isEmpty() ? MyMoneySplit() : torig.splits().front(), true); // make sure to suppress comparison of some data: postDate torig.setPostDate(taccepted.postDate()); if (torig != taccepted) { QPointer cdlg = new KConfirmManualEnterDlg(schedule, this); cdlg->loadTransactions(torig, taccepted); if (cdlg->exec() == QDialog::Accepted) { action = cdlg->action(); delete cdlg; break; } delete cdlg; // the user has chosen 'cancel' during confirmation, // we go back to the editor continue; } } else if (rc == eDialogs::ScheduleResultCode::Skip) { slotTransactionsCancel(); skipSchedule(schedule); } else { slotTransactionsCancel(); } } else { if (autoEnter) { if (KMessageBox::warningYesNo(this, i18n("Are you sure you wish to stop this scheduled transaction from being entered into the register?\n\nKMyMoney will prompt you again next time it starts unless you manually enter it later.")) == KMessageBox::No) { // the user has chosen 'No' for the above question, // we go back to the editor continue; } } slotTransactionsCancel(); } break; } } // if we still have the editor around here, the user did not cancel if ((d->m_transactionEditor != 0) && (dlg != 0)) { MyMoneyFileTransaction ft; try { MyMoneyTransaction t; // add the new transaction switch (action) { case KConfirmManualEnterDlg::UseOriginal: // setup widgets with original transaction data d->m_transactionEditor->setTransaction(dlg->transaction(), dlg->transaction().splits().isEmpty() ? MyMoneySplit() : dlg->transaction().splits().front()); // and create a transaction based on that data taccepted = MyMoneyTransaction(); d->m_transactionEditor->createTransaction(taccepted, dlg->transaction(), dlg->transaction().splits().isEmpty() ? MyMoneySplit() : dlg->transaction().splits().front(), true); break; case KConfirmManualEnterDlg::ModifyAlways: torig = taccepted; torig.setPostDate(origDueDate); schedule.setTransaction(torig); break; case KConfirmManualEnterDlg::ModifyOnce: break; } QString newId; connect(d->m_transactionEditor, SIGNAL(balanceWarning(QWidget*,MyMoneyAccount,QString)), d->m_balanceWarning, SLOT(slotShowMessage(QWidget*,MyMoneyAccount,QString))); if (d->m_transactionEditor->enterTransactions(newId, false)) { if (!newId.isEmpty()) { MyMoneyTransaction t = MyMoneyFile::instance()->transaction(newId); schedule.setLastPayment(t.postDate()); } // in case the next due date is invalid, the schedule is finished // we mark it as such by setting the next due date to one day past the end QDate nextDueDate = schedule.nextPayment(origDueDate); if (!nextDueDate.isValid()) { schedule.setNextDueDate(schedule.endDate().addDays(1)); } else { schedule.setNextDueDate(nextDueDate); } MyMoneyFile::instance()->modifySchedule(schedule); rc = eDialogs::ScheduleResultCode::Enter; // delete the editor before we emit the dataChanged() signal from the // engine. Calling this twice in a row does not hurt. deleteTransactionEditor(); ft.commit(); } } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unable to enter scheduled transaction '%1'", schedule.name()), e.what()); } deleteTransactionEditor(); } } } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unable to enter scheduled transaction '%1'", schedule.name()), e.what()); } delete dlg; } return rc; } void KMyMoneyApp::slotPayeeNew(const QString& newnameBase, QString& id) { createPayeeNew(newnameBase, id); } bool KMyMoneyApp::createPayeeNew(const QString& newnameBase, QString& id) { bool doit = true; if (newnameBase != i18n("New Payee")) { // Ask the user if that is what he intended to do? QString msg = QLatin1String("") + i18n("Do you want to add %1 as payer/receiver?", newnameBase) + QLatin1String(""); if (KMessageBox::questionYesNo(this, msg, i18n("New payee/receiver"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "NewPayee") == KMessageBox::No) { doit = false; // we should not keep the 'no' setting because that can confuse people like // I have seen in some usability tests. So we just delete it right away. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); if (kconfig) { kconfig->group(QLatin1String("Notification Messages")).deleteEntry(QLatin1String("NewPayee")); } } } if (doit) { MyMoneyFileTransaction ft; try { QString newname(newnameBase); // adjust name until a unique name has been created int count = 0; for (;;) { try { MyMoneyFile::instance()->payeeByName(newname); newname = QString("%1 [%2]").arg(newnameBase).arg(++count); } catch (const MyMoneyException &) { break; } } MyMoneyPayee p; p.setName(newname); MyMoneyFile::instance()->addPayee(p); id = p.id(); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unable to add payee"), i18n("%1 thrown in %2:%3", e.what(), e.file(), e.line())); doit = false; } } return doit; } void KMyMoneyApp::slotPayeeNew() { QString id; slotPayeeNew(i18n("New Payee"), id); // the callbacks should have made sure, that the payees view has been // updated already. So we search for the id in the list of items // and select it. emit payeeCreated(id); } bool KMyMoneyApp::payeeInList(const QList& list, const QString& id) const { bool rc = false; QList::const_iterator it_p = list.begin(); while (it_p != list.end()) { if ((*it_p).id() == id) { rc = true; break; } ++it_p; } return rc; } void KMyMoneyApp::slotPayeeDelete() { if (d->m_selectedPayees.isEmpty()) return; // shouldn't happen // get confirmation from user QString prompt; if (d->m_selectedPayees.size() == 1) prompt = i18n("

Do you really want to remove the payee %1?

", d->m_selectedPayees.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; payeeReassign(KPayeeReassignDlg::TypeDelete); } void KMyMoneyApp::slotPayeeMerge() { if (d->m_selectedPayees.size() < 1) return; // shouldn't happen if (KMessageBox::questionYesNo(this, i18n("

Do you really want to merge the selected payees?"), i18n("Merge Payees")) == KMessageBox::No) return; if (payeeReassign(KPayeeReassignDlg::TypeMerge)) // clean selection since we just deleted the selected payees slotSelectPayees(QList()); } bool KMyMoneyApp::payeeReassign(int type) { if (!(type >= 0 && type < KPayeeReassignDlg::TypeCount)) return false; MyMoneyFile * file = MyMoneyFile::instance(); MyMoneyFileTransaction ft; try { // create a transaction filter that contains all payees selected for removal MyMoneyTransactionFilter f = MyMoneyTransactionFilter(); for (QList::const_iterator it = d->m_selectedPayees.constBegin(); it != d->m_selectedPayees.constEnd(); ++it) { f.addPayee((*it).id()); } // request a list of all transactions that still use the payees in question QList translist = file->transactionList(f); // qDebug() << "[KPayeesView::slotDeletePayee] " << translist.count() << " transaction still assigned to payees"; // now get a list of all schedules that make use of one of the payees QList all_schedules = file->scheduleList(); QList used_schedules; for (QList::ConstIterator it = all_schedules.constBegin(); it != all_schedules.constEnd(); ++it) { // loop over all splits in the transaction of the schedule for (QList::ConstIterator s_it = (*it).transaction().splits().constBegin(); s_it != (*it).transaction().splits().constEnd(); ++s_it) { // is the payee in the split to be deleted? if (payeeInList(d->m_selectedPayees, (*s_it).payeeId())) { used_schedules.push_back(*it); // remember this schedule break; } } } // qDebug() << "[KPayeesView::slotDeletePayee] " << used_schedules.count() << " schedules use one of the selected payees"; // and a list of all loan accounts that references one of the payees QList allAccounts; QList usedAccounts; file->accountList(allAccounts); foreach (const MyMoneyAccount &account, allAccounts) { if (account.isLoan()) { MyMoneyAccountLoan loanAccount(account); foreach (const MyMoneyPayee &payee, d->m_selectedPayees) { if (loanAccount.hasReferenceTo(payee.id())) { usedAccounts.append(account); } } } } MyMoneyPayee newPayee; bool addToMatchList = false; // if at least one payee is still referenced, we need to reassign its transactions first if (!translist.isEmpty() || !used_schedules.isEmpty() || !usedAccounts.isEmpty()) { // first create list with all non-selected payees QList remainingPayees; if (type == KPayeeReassignDlg::TypeMerge) { remainingPayees = d->m_selectedPayees; } else { remainingPayees = file->payeeList(); QList::iterator it_p; for (it_p = remainingPayees.begin(); it_p != remainingPayees.end();) { if (d->m_selectedPayees.contains(*it_p)) { it_p = remainingPayees.erase(it_p); } else { ++it_p; } } } // show error message if no payees remain if (remainingPayees.isEmpty()) { KMessageBox::sorry(this, i18n("At least one transaction/scheduled transaction or loan account is still referenced by a payee. " "Currently you have all payees selected. However, at least one payee must remain so " "that the transaction/scheduled transaction or loan account can be reassigned.")); return false; } // show transaction reassignment dialog KPayeeReassignDlg * dlg = new KPayeeReassignDlg(static_cast(type), this); KMyMoneyMVCCombo::setSubstringSearchForChildren(dlg, !KMyMoneySettings::stringMatchFromStart()); QString payee_id = dlg->show(remainingPayees); addToMatchList = dlg->addToMatchList(); delete dlg; // and kill the dialog if (payee_id.isEmpty()) return false; // the user aborted the dialog, so let's abort as well // try to get selected payee. If not possible and we are merging payees, // then we create a new one try { newPayee = file->payee(payee_id); } catch (const MyMoneyException &e) { if (type == KPayeeReassignDlg::TypeMerge) { // it's ok to use payee_id for both arguments since the first is const, // so it's garantee not to change its content if (!createPayeeNew(payee_id, payee_id)) return false; // the user aborted the dialog, so let's abort as well newPayee = file->payee(payee_id); } else { return false; } } // TODO : check if we have a report that explicitively uses one of our payees // and issue an appropriate warning try { QList::iterator s_it; // now loop over all transactions and reassign payee for (QList::iterator it = translist.begin(); it != translist.end(); ++it) { // create a copy of the splits list in the transaction QList splits = (*it).splits(); // loop over all splits for (s_it = splits.begin(); s_it != splits.end(); ++s_it) { // if the split is assigned to one of the selected payees, we need to modify it if (payeeInList(d->m_selectedPayees, (*s_it).payeeId())) { (*s_it).setPayeeId(payee_id); // first modify payee in current split // then modify the split in our local copy of the transaction list (*it).modifySplit(*s_it); // this does not modify the list object 'splits'! } } // for - Splits file->modifyTransaction(*it); // modify the transaction in the MyMoney object } // for - Transactions // now loop over all schedules and reassign payees for (QList::iterator it = used_schedules.begin(); it != used_schedules.end(); ++it) { // create copy of transaction in current schedule MyMoneyTransaction trans = (*it).transaction(); // create copy of lists of splits QList splits = trans.splits(); for (s_it = splits.begin(); s_it != splits.end(); ++s_it) { if (payeeInList(d->m_selectedPayees, (*s_it).payeeId())) { (*s_it).setPayeeId(payee_id); trans.modifySplit(*s_it); // does not modify the list object 'splits'! } } // for - Splits // store transaction in current schedule (*it).setTransaction(trans); file->modifySchedule(*it); // modify the schedule in the MyMoney engine } // for - Schedules // reassign the payees in the loans that reference the deleted payees foreach (const MyMoneyAccount &account, usedAccounts) { MyMoneyAccountLoan loanAccount(account); loanAccount.setPayee(payee_id); file->modifyAccount(loanAccount); } } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to reassign payee of transaction/split"), i18n("%1 thrown in %2:%3", e.what(), e.file(), e.line())); } } else { // if !translist.isEmpty() if (type == KPayeeReassignDlg::TypeMerge) { KMessageBox::sorry(this, i18n("Nothing to merge."), i18n("Merge Payees")); return false; } } bool ignorecase; QStringList payeeNames; MyMoneyPayee::payeeMatchType matchType = newPayee.matchData(ignorecase, payeeNames); QStringList deletedPayeeNames; // now loop over all selected payees and remove them for (QList::iterator it = d->m_selectedPayees.begin(); it != d->m_selectedPayees.end(); ++it) { if (newPayee.id() != (*it).id()) { if (addToMatchList) { deletedPayeeNames << (*it).name(); } file->removePayee(*it); } } // if we initially have no matching turned on, we just ignore the case (default) if (matchType == MyMoneyPayee::matchDisabled) ignorecase = true; // update the destination payee if this was requested by the user if (addToMatchList && deletedPayeeNames.count() > 0) { // add new names to the list // TODO: it would be cool to somehow shrink the list to make better use // of regular expressions at this point. For now, we leave this task // to the user himeself. QStringList::const_iterator it_n; for (it_n = deletedPayeeNames.constBegin(); it_n != deletedPayeeNames.constEnd(); ++it_n) { if (matchType == MyMoneyPayee::matchKey) { // make sure we really need it and it is not caught by an existing regexp QStringList::const_iterator it_k; for (it_k = payeeNames.constBegin(); it_k != payeeNames.constEnd(); ++it_k) { QRegExp exp(*it_k, ignorecase ? Qt::CaseInsensitive : Qt::CaseSensitive); if (exp.indexIn(*it_n) != -1) break; } if (it_k == payeeNames.constEnd()) payeeNames << QRegExp::escape(*it_n); } else if (payeeNames.contains(*it_n) == 0) payeeNames << QRegExp::escape(*it_n); } // and update the payee in the engine context // make sure to turn on matching for this payee in the right mode newPayee.setMatchData(MyMoneyPayee::matchKey, ignorecase, payeeNames); file->modifyPayee(newPayee); } ft.commit(); // If we just deleted the payees, they sure don't exist anymore slotSelectPayees(QList()); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to remove payee(s)"), i18n("%1 thrown in %2:%3", e.what(), e.file(), e.line())); } return true; } void KMyMoneyApp::slotTagNew(const QString& newnameBase, QString& id) { bool doit = true; if (newnameBase != i18n("New Tag")) { // Ask the user if that is what he intended to do? QString msg = QString("") + i18n("Do you want to add %1 as tag?", newnameBase) + QString(""); if (KMessageBox::questionYesNo(this, msg, i18n("New tag"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "NewTag") == KMessageBox::No) { doit = false; // we should not keep the 'no' setting because that can confuse people like // I have seen in some usability tests. So we just delete it right away. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); if (kconfig) { kconfig->group(QLatin1String("Notification Messages")).deleteEntry(QLatin1String("NewTag")); } } } if (doit) { MyMoneyFileTransaction ft; try { QString newname(newnameBase); // adjust name until a unique name has been created int count = 0; for (;;) { try { MyMoneyFile::instance()->tagByName(newname); newname = QString("%1 [%2]").arg(newnameBase).arg(++count); } catch (const MyMoneyException &) { break; } } MyMoneyTag ta; ta.setName(newname); MyMoneyFile::instance()->addTag(ta); id = ta.id(); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Unable to add tag"), i18n("%1 thrown in %2:%3", e.what(), e.file(), e.line())); } } } void KMyMoneyApp::slotTagNew() { QString id; slotTagNew(i18n("New Tag"), id); // the callbacks should have made sure, that the tags view has been // updated already. So we search for the id in the list of items // and select it. emit tagCreated(id); } bool KMyMoneyApp::tagInList(const QList& list, const QString& id) const { bool rc = false; QList::const_iterator it_p = list.begin(); while (it_p != list.end()) { if ((*it_p).id() == id) { rc = true; break; } ++it_p; } return rc; } void KMyMoneyApp::slotTagDelete() { if (d->m_selectedTags.isEmpty()) return; // shouldn't happen MyMoneyFile * file = MyMoneyFile::instance(); // first create list with all non-selected tags QList remainingTags = file->tagList(); QList::iterator it_ta; for (it_ta = remainingTags.begin(); it_ta != remainingTags.end();) { if (d->m_selectedTags.contains(*it_ta)) { it_ta = remainingTags.erase(it_ta); } else { ++it_ta; } } // get confirmation from user QString prompt; if (d->m_selectedTags.size() == 1) prompt = i18n("

Do you really want to remove the tag %1?

", d->m_selectedTags.front().name()); else prompt = i18n("Do you really want to remove all selected tags?"); if (KMessageBox::questionYesNo(this, prompt, i18n("Remove Tag")) == KMessageBox::No) return; MyMoneyFileTransaction ft; try { // create a transaction filter that contains all tags selected for removal MyMoneyTransactionFilter f = MyMoneyTransactionFilter(); for (QList::const_iterator it = d->m_selectedTags.constBegin(); it != d->m_selectedTags.constEnd(); ++it) { f.addTag((*it).id()); } // request a list of all transactions that still use the tags in question QList translist = file->transactionList(f); // qDebug() << "[KTagsView::slotDeleteTag] " << translist.count() << " transaction still assigned to tags"; // now get a list of all schedules that make use of one of the tags QList all_schedules = file->scheduleList(); QList used_schedules; for (QList::ConstIterator it = all_schedules.constBegin(); it != all_schedules.constEnd(); ++it) { // loop over all splits in the transaction of the schedule for (QList::ConstIterator s_it = (*it).transaction().splits().constBegin(); s_it != (*it).transaction().splits().constEnd(); ++s_it) { for (int i = 0; i < (*s_it).tagIdList().size(); i++) { // is the tag in the split to be deleted? if (tagInList(d->m_selectedTags, (*s_it).tagIdList()[i])) { used_schedules.push_back(*it); // remember this schedule break; } } } } // qDebug() << "[KTagsView::slotDeleteTag] " << used_schedules.count() << " schedules use one of the selected tags"; MyMoneyTag newTag; // if at least one tag is still referenced, we need to reassign its transactions first if (!translist.isEmpty() || !used_schedules.isEmpty()) { // show error message if no tags remain //FIXME-ALEX Tags are optional so we can delete all of them and simply delete every tagId from every transaction if (remainingTags.isEmpty()) { KMessageBox::sorry(this, i18n("At least one transaction/scheduled transaction is still referenced by a tag. " "Currently you have all tags selected. However, at least one tag must remain so " "that the transaction/scheduled transaction can be reassigned.")); return; } // show transaction reassignment dialog KTagReassignDlg * dlg = new KTagReassignDlg(this); KMyMoneyMVCCombo::setSubstringSearchForChildren(dlg, !KMyMoneySettings::stringMatchFromStart()); QString tag_id = dlg->show(remainingTags); delete dlg; // and kill the dialog if (tag_id.isEmpty()) //FIXME-ALEX Let the user choose to not reassign a to-be deleted tag to another one. return; // the user aborted the dialog, so let's abort as well newTag = file->tag(tag_id); // TODO : check if we have a report that explicitively uses one of our tags // and issue an appropriate warning try { QList::iterator s_it; // now loop over all transactions and reassign tag for (QList::iterator it = translist.begin(); it != translist.end(); ++it) { // create a copy of the splits list in the transaction QList splits = (*it).splits(); // loop over all splits for (s_it = splits.begin(); s_it != splits.end(); ++s_it) { QList tagIdList = (*s_it).tagIdList(); for (int i = 0; i < tagIdList.size(); i++) { // if the split is assigned to one of the selected tags, we need to modify it if (tagInList(d->m_selectedTags, tagIdList[i])) { tagIdList.removeAt(i); if (tagIdList.indexOf(tag_id) == -1) tagIdList.append(tag_id); i = -1; // restart from the first element } } (*s_it).setTagIdList(tagIdList); // first modify tag list in current split // then modify the split in our local copy of the transaction list (*it).modifySplit(*s_it); // this does not modify the list object 'splits'! } // for - Splits file->modifyTransaction(*it); // modify the transaction in the MyMoney object } // for - Transactions // now loop over all schedules and reassign tags for (QList::iterator it = used_schedules.begin(); it != used_schedules.end(); ++it) { // create copy of transaction in current schedule MyMoneyTransaction trans = (*it).transaction(); // create copy of lists of splits QList splits = trans.splits(); for (s_it = splits.begin(); s_it != splits.end(); ++s_it) { QList tagIdList = (*s_it).tagIdList(); for (int i = 0; i < tagIdList.size(); i++) { if (tagInList(d->m_selectedTags, tagIdList[i])) { tagIdList.removeAt(i); if (tagIdList.indexOf(tag_id) == -1) tagIdList.append(tag_id); i = -1; // restart from the first element } } (*s_it).setTagIdList(tagIdList); trans.modifySplit(*s_it); // does not modify the list object 'splits'! } // for - Splits // store transaction in current schedule (*it).setTransaction(trans); file->modifySchedule(*it); // modify the schedule in the MyMoney engine } // for - Schedules } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to reassign tag of transaction/split"), i18n("%1 thrown in %2:%3", e.what(), e.file(), e.line())); } } // if !translist.isEmpty() // now loop over all selected tags and remove them for (QList::iterator it = d->m_selectedTags.begin(); it != d->m_selectedTags.end(); ++it) { file->removeTag(*it); } ft.commit(); // If we just deleted the tags, they sure don't exist anymore slotSelectTags(QList()); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to remove tag(s)"), i18n("%1 thrown in %2:%3", e.what(), e.file(), e.line())); } } void KMyMoneyApp::slotCurrencyNew() { QString sid = QInputDialog::getText(0, i18n("New currency"), i18n("Enter ISO 4217 code for the new currency")); if (!sid.isEmpty()) { QString id(sid); MyMoneySecurity currency(id, i18n("New currency")); MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->addCurrency(currency); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("Cannot create new currency. %1", e.what()), i18n("New currency")); } emit currencyCreated(id); } } void KMyMoneyApp::slotCurrencyUpdate(const QString ¤cyId, const QString& currencyName, const QString& currencyTradingSymbol) { MyMoneyFile* file = MyMoneyFile::instance(); try { if (currencyName != d->m_selectedCurrency.name() || currencyTradingSymbol != d->m_selectedCurrency.tradingSymbol()) { MyMoneySecurity currency = file->currency(currencyId); currency.setName(currencyName); currency.setTradingSymbol(currencyTradingSymbol); MyMoneyFileTransaction ft; try { file->modifyCurrency(currency); d->m_selectedCurrency = currency; ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("Cannot update currency. %1", e.what()), i18n("Update currency")); } } } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("Cannot update currency. %1", e.what()), i18n("Update currency")); } } void KMyMoneyApp::slotCurrencyDelete() { if (!d->m_selectedCurrency.id().isEmpty()) { MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->removeCurrency(d->m_selectedCurrency); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("Cannot delete currency %1. %2", d->m_selectedCurrency.name(), e.what()), i18n("Delete currency")); } } } void KMyMoneyApp::slotCurrencySetBase() { if (!d->m_selectedCurrency.id().isEmpty()) { if (d->m_selectedCurrency.id() != MyMoneyFile::instance()->baseCurrency().id()) { MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->setBaseCurrency(d->m_selectedCurrency); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::sorry(this, i18n("Cannot set %1 as base currency: %2", d->m_selectedCurrency.name(), e.what()), i18n("Set base currency")); } } } } void KMyMoneyApp::slotBudgetNew() { QDate date = QDate::currentDate(); date.setDate(date.year(), KMyMoneyGlobalSettings::firstFiscalMonth(), KMyMoneyGlobalSettings::firstFiscalDay()); QString newname = i18n("Budget %1", date.year()); MyMoneyBudget budget; // make sure we have a unique name try { int i = 1; // Exception thrown when the name is not found while (1) { MyMoneyFile::instance()->budgetByName(newname); newname = i18n("Budget %1 %2", date.year(), i++); } } catch (const MyMoneyException &) { // all ok, the name is unique } MyMoneyFileTransaction ft; try { budget.setName(newname); budget.setBudgetStart(date); MyMoneyFile::instance()->addBudget(budget); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Error"), i18n("Unable to add budget: %1, thrown in %2:%3", e.what(), e.file(), e.line())); } } void KMyMoneyApp::slotBudgetDelete() { if (d->m_selectedBudgets.isEmpty()) return; // shouldn't happen MyMoneyFile * file = MyMoneyFile::instance(); // get confirmation from user QString prompt; if (d->m_selectedBudgets.size() == 1) prompt = i18n("

Do you really want to remove the budget %1?

", d->m_selectedBudgets.front().name()); else prompt = i18n("Do you really want to remove all selected budgets?"); if (KMessageBox::questionYesNo(this, prompt, i18n("Remove Budget")) == KMessageBox::No) return; MyMoneyFileTransaction ft; try { // now loop over all selected budgets and remove them for (QList::iterator it = d->m_selectedBudgets.begin(); it != d->m_selectedBudgets.end(); ++it) { file->removeBudget(*it); } ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Error"), i18n("Unable to remove budget: %1, thrown in %2:%3", e.what(), e.file(), e.line())); } } void KMyMoneyApp::slotBudgetCopy() { if (d->m_selectedBudgets.size() == 1) { MyMoneyFileTransaction ft; try { MyMoneyBudget budget = d->m_selectedBudgets.first(); budget.clearId(); budget.setName(i18n("Copy of %1", budget.name())); MyMoneyFile::instance()->addBudget(budget); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Error"), i18n("Unable to add budget: %1, thrown in %2:%3", e.what(), e.file(), e.line())); } } } void KMyMoneyApp::slotBudgetChangeYear() { if (d->m_selectedBudgets.size() == 1) { QStringList years; int current = 0; bool haveCurrent = false; MyMoneyBudget budget = *(d->m_selectedBudgets.begin()); for (int i = (QDate::currentDate().year() - 3); i < (QDate::currentDate().year() + 5); ++i) { years << QString("%1").arg(i); if (i == budget.budgetStart().year()) { haveCurrent = true; } if (!haveCurrent) ++current; } if (!haveCurrent) current = 0; bool ok = false; QString yearString = QInputDialog::getItem(this, i18n("Select year"), i18n("Budget year"), years, current, false, &ok); if (ok) { int year = yearString.toInt(0, 0); QDate newYear = QDate(year, budget.budgetStart().month(), budget.budgetStart().day()); if (newYear != budget.budgetStart()) { MyMoneyFileTransaction ft; try { budget.setBudgetStart(newYear); MyMoneyFile::instance()->modifyBudget(budget); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Error"), i18n("Unable to modify budget: %1, thrown in %2:%3", e.what(), e.file(), e.line())); } } } } } void KMyMoneyApp::slotBudgetForecast() { if (d->m_selectedBudgets.size() == 1) { MyMoneyFileTransaction ft; try { MyMoneyBudget budget = d->m_selectedBudgets.first(); bool calcBudget = budget.getaccounts().count() == 0; if (!calcBudget) { if (KMessageBox::warningContinueCancel(0, i18n("The current budget already contains data. Continuing will replace all current values of this budget."), i18nc("Warning message box", "Warning")) == KMessageBox::Continue) calcBudget = true; } if (calcBudget) { QDate historyStart; QDate historyEnd; QDate budgetStart; QDate budgetEnd; budgetStart = budget.budgetStart(); budgetEnd = budgetStart.addYears(1).addDays(-1); historyStart = budgetStart.addYears(-1); historyEnd = budgetEnd.addYears(-1); MyMoneyForecast forecast = KMyMoneyGlobalSettings::forecast(); forecast.createBudget(budget, historyStart, historyEnd, budgetStart, budgetEnd, true); MyMoneyFile::instance()->modifyBudget(budget); ft.commit(); } } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Error"), i18n("Unable to modify budget: %1, thrown in %2:%3", e.what(), e.file(), e.line())); } } } void KMyMoneyApp::slotNewFeature() { } void KMyMoneyApp::slotTransactionsDelete() { // since we may jump here via code, we have to make sure to react only // if the action is enabled if (!kmymoney->actionCollection()->action(s_Actions[Action::TransactionDelete])->isEnabled()) return; if (d->m_selectedTransactions.isEmpty()) return; if (d->m_selectedTransactions.warnLevel() == 1) { if (KMessageBox::warningContinueCancel(0, i18n("At least one split of the selected transactions has been reconciled. " "Do you wish to delete the transactions anyway?"), i18n("Transaction already reconciled")) == KMessageBox::Cancel) return; } QString msg = i18np("Do you really want to delete the selected transaction?", "Do you really want to delete all %1 selected transactions?", d->m_selectedTransactions.count()); if (KMessageBox::questionYesNo(this, msg, i18n("Delete transaction")) == KMessageBox::Yes) { KMSTATUS(i18n("Deleting transactions")); doDeleteTransactions(); } } void KMyMoneyApp::slotTransactionDuplicate() { // since we may jump here via code, we have to make sure to react only // if the action is enabled if (kmymoney->actionCollection()->action(s_Actions[Action::TransactionDuplicate])->isEnabled()) { KMyMoneyRegister::SelectedTransactions list = d->m_selectedTransactions; KMyMoneyRegister::SelectedTransactions::iterator it_t; int i = 0; int cnt = d->m_selectedTransactions.count(); KMSTATUS(i18n("Duplicating transactions")); slotStatusProgressBar(0, cnt); MyMoneyFileTransaction ft; MyMoneyTransaction lt; try { for (it_t = list.begin(); it_t != list.end(); ++it_t) { MyMoneyTransaction t = (*it_t).transaction(); QList::iterator it_s; // wipe out any reconciliation information for (it_s = t.splits().begin(); it_s != t.splits().end(); ++it_s) { (*it_s).setReconcileFlag(eMyMoney::Split::State::NotReconciled); (*it_s).setReconcileDate(QDate()); (*it_s).setBankID(QString()); } // clear invalid data t.setEntryDate(QDate()); t.clearId(); // and set the post date to today t.setPostDate(QDate::currentDate()); MyMoneyFile::instance()->addTransaction(t); lt = t; slotStatusProgressBar(i++, 0); } ft.commit(); // select the new transaction in the ledger if (!d->m_selectedAccount.id().isEmpty()) d->m_myMoneyView->slotLedgerSelected(d->m_selectedAccount.id(), lt.id()); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Error"), i18n("Unable to duplicate transaction(s): %1, thrown in %2:%3", e.what(), e.file(), e.line())); } // switch off the progress bar slotStatusProgressBar(-1, -1); } } void KMyMoneyApp::doDeleteTransactions() { KMyMoneyRegister::SelectedTransactions list = d->m_selectedTransactions; KMyMoneyRegister::SelectedTransactions::iterator it_t; int cnt = list.count(); int i = 0; slotStatusProgressBar(0, cnt); MyMoneyFileTransaction ft; MyMoneyFile* file = MyMoneyFile::instance(); try { it_t = list.begin(); while (it_t != list.end()) { // only remove those transactions that do not reference a closed account if (!file->referencesClosedAccount((*it_t).transaction())) { file->removeTransaction((*it_t).transaction()); // remove all those references in the list of selected transactions // that refer to the same transaction we just removed so that we // will not be caught by an exception later on (see bko #285310) KMyMoneyRegister::SelectedTransactions::iterator it_td = it_t; ++it_td; while (it_td != list.end()) { if ((*it_t).transaction().id() == (*it_td).transaction().id()) { it_td = list.erase(it_td); i++; // bump count of deleted transactions } else { ++it_td; } } } // need to ensure "nextCheckNumber" is still correct MyMoneyAccount acc = file->account((*it_t).split().accountId()); // the "lastNumberUsed" might have been the txn number deleted // so adjust it QString deletedNum = (*it_t).split().number(); // decrement deletedNum and set new "lastNumberUsed" QString num = KMyMoneyUtils::getAdjacentNumber(deletedNum, -1); acc.setValue("lastNumberUsed", num); file->modifyAccount(acc); list.erase(it_t); it_t = list.begin(); slotStatusProgressBar(i++, 0); } ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Error"), i18n("Unable to delete transaction(s): %1, thrown in %2:%3", e.what(), e.file(), e.line())); } slotStatusProgressBar(-1, -1); } void KMyMoneyApp::slotTransactionsNew() { // since we jump here via code, we have to make sure to react only // if the action is enabled if (kmymoney->actionCollection()->action(s_Actions[Action::TransactionNew])->isEnabled()) { if (d->m_myMoneyView->createNewTransaction()) { d->m_transactionEditor = d->m_myMoneyView->startEdit(d->m_selectedTransactions); if (d->m_transactionEditor) { KMyMoneyMVCCombo::setSubstringSearchForChildren(d->m_myMoneyView, !KMyMoneySettings::stringMatchFromStart()); KMyMoneyPayeeCombo* payeeEdit = dynamic_cast(d->m_transactionEditor->haveWidget("payee")); if (payeeEdit && !d->m_lastPayeeEnteredId.isEmpty()) { // in case we entered a new transaction before and used a payee, // we reuse it here. Save the text to the edit widget, select it // so that hitting any character will start entering another payee. payeeEdit->setSelectedItem(d->m_lastPayeeEnteredId); payeeEdit->lineEdit()->selectAll(); } if (d->m_transactionEditor) { connect(d->m_transactionEditor, SIGNAL(statusProgress(int,int)), this, SLOT(slotStatusProgressBar(int,int))); connect(d->m_transactionEditor, SIGNAL(statusMsg(QString)), this, SLOT(slotStatusMsg(QString))); connect(d->m_transactionEditor, SIGNAL(scheduleTransaction(MyMoneyTransaction,eMyMoney::Schedule::Occurrence)), this, SLOT(slotScheduleNew(MyMoneyTransaction,eMyMoney::Schedule::Occurrence))); } slotUpdateActions(); } } } } void KMyMoneyApp::slotTransactionsEdit() { // qDebug("KMyMoneyApp::slotTransactionsEdit()"); // since we jump here via code, we have to make sure to react only // if the action is enabled if (kmymoney->actionCollection()->action(s_Actions[Action::TransactionEdit])->isEnabled()) { // as soon as we edit a transaction, we don't remember the last payee entered d->m_lastPayeeEnteredId.clear(); d->m_transactionEditor = d->m_myMoneyView->startEdit(d->m_selectedTransactions); KMyMoneyMVCCombo::setSubstringSearchForChildren(d->m_myMoneyView, !KMyMoneySettings::stringMatchFromStart()); slotUpdateActions(); } } void KMyMoneyApp::deleteTransactionEditor() { // make sure, we don't use the transaction editor pointer // anymore from now on TransactionEditor* p = d->m_transactionEditor; d->m_transactionEditor = 0; delete p; } void KMyMoneyApp::slotTransactionsEditSplits() { // since we jump here via code, we have to make sure to react only // if the action is enabled if (kmymoney->actionCollection()->action(s_Actions[Action::TransactionEditSplits])->isEnabled()) { // as soon as we edit a transaction, we don't remember the last payee entered d->m_lastPayeeEnteredId.clear(); d->m_transactionEditor = d->m_myMoneyView->startEdit(d->m_selectedTransactions); slotUpdateActions(); if (d->m_transactionEditor) { KMyMoneyMVCCombo::setSubstringSearchForChildren(d->m_myMoneyView, !KMyMoneySettings::stringMatchFromStart()); if (d->m_transactionEditor->slotEditSplits() == QDialog::Accepted) { MyMoneyFileTransaction ft; try { QString id; connect(d->m_transactionEditor, SIGNAL(balanceWarning(QWidget*,MyMoneyAccount,QString)), d->m_balanceWarning, SLOT(slotShowMessage(QWidget*,MyMoneyAccount,QString))); d->m_transactionEditor->enterTransactions(id); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Error"), i18n("Unable to modify transaction: %1, thrown in %2:%3", e.what(), e.file(), e.line())); } } } deleteTransactionEditor(); slotUpdateActions(); } } void KMyMoneyApp::slotTransactionsCancel() { // since we jump here via code, we have to make sure to react only // if the action is enabled if (kmymoney->actionCollection()->action(s_Actions[Action::TransactionCancel])->isEnabled()) { // make sure, we block the enter function actionCollection()->action(s_Actions[Action::TransactionEnter])->setEnabled(false); // qDebug("KMyMoneyApp::slotTransactionsCancel"); deleteTransactionEditor(); slotUpdateActions(); } } void KMyMoneyApp::slotTransactionsEnter() { // since we jump here via code, we have to make sure to react only // if the action is enabled if (kmymoney->actionCollection()->action(s_Actions[Action::TransactionEnter])->isEnabled()) { // disable the action while we process it to make sure it's processed only once since // d->m_transactionEditor->enterTransactions(newId) will run QCoreApplication::processEvents // we could end up here twice which will cause a crash slotUpdateActions() will enable the action again kmymoney->actionCollection()->action(s_Actions[Action::TransactionEnter])->setEnabled(false); if (d->m_transactionEditor) { QString accountId = d->m_selectedAccount.id(); QString newId; connect(d->m_transactionEditor, SIGNAL(balanceWarning(QWidget*,MyMoneyAccount,QString)), d->m_balanceWarning, SLOT(slotShowMessage(QWidget*,MyMoneyAccount,QString))); if (d->m_transactionEditor->enterTransactions(newId)) { KMyMoneyPayeeCombo* payeeEdit = dynamic_cast(d->m_transactionEditor->haveWidget("payee")); if (payeeEdit && !newId.isEmpty()) { d->m_lastPayeeEnteredId = payeeEdit->selectedItem(); } deleteTransactionEditor(); } if (!newId.isEmpty()) { d->m_myMoneyView->slotLedgerSelected(accountId, newId); } } slotUpdateActions(); } } void KMyMoneyApp::slotTransactionsCancelOrEnter(bool& okToSelect) { static bool oneTime = false; if (!oneTime) { oneTime = true; QString dontShowAgain = "CancelOrEditTransaction"; // qDebug("KMyMoneyApp::slotCancelOrEndEdit"); if (d->m_transactionEditor) { if (KMyMoneyGlobalSettings::focusChangeIsEnter() && kmymoney->actionCollection()->action(s_Actions[Action::TransactionEnter])->isEnabled()) { slotTransactionsEnter(); if (d->m_transactionEditor) { // if at this stage the editor is still there that means that entering the transaction was cancelled // for example by pressing cancel on the exchange rate editor so we must stay in edit mode okToSelect = false; } } else { // okToSelect is preset to true if a cancel of the dialog is useful and false if it is not int rc; KGuiItem noGuiItem = KStandardGuiItem::save(); KGuiItem yesGuiItem = KStandardGuiItem::discard(); KGuiItem cancelGuiItem = KStandardGuiItem::cont(); // if the transaction can't be entered make sure that it can't be entered by pressing no either if (!kmymoney->actionCollection()->action(s_Actions[Action::TransactionEnter])->isEnabled()) { noGuiItem.setEnabled(false); noGuiItem.setToolTip(kmymoney->actionCollection()->action(s_Actions[Action::TransactionEnter])->toolTip()); } if (okToSelect == true) { rc = KMessageBox::warningYesNoCancel(0, i18n("

Please select what you want to do: discard the changes, save the changes or continue to edit the transaction.

You can also set an option to save the transaction automatically when e.g. selecting another transaction.

"), i18n("End transaction edit"), yesGuiItem, noGuiItem, cancelGuiItem, dontShowAgain); } else { rc = KMessageBox::warningYesNo(0, i18n("

Please select what you want to do: discard the changes, save the changes or continue to edit the transaction.

You can also set an option to save the transaction automatically when e.g. selecting another transaction.

"), i18n("End transaction edit"), yesGuiItem, noGuiItem, dontShowAgain); } switch (rc) { case KMessageBox::Yes: slotTransactionsCancel(); break; case KMessageBox::No: slotTransactionsEnter(); // make sure that we'll see this message the next time no matter // if the user has chosen the 'Don't show again' checkbox KMessageBox::enableMessage(dontShowAgain); if (d->m_transactionEditor) { // if at this stage the editor is still there that means that entering the transaction was cancelled // for example by pressing cancel on the exchange rate editor so we must stay in edit mode okToSelect = false; } break; case KMessageBox::Cancel: // make sure that we'll see this message the next time no matter // if the user has chosen the 'Don't show again' checkbox KMessageBox::enableMessage(dontShowAgain); okToSelect = false; break; } } } oneTime = false; } } void KMyMoneyApp::slotToggleReconciliationFlag() { markTransaction(eMyMoney::Split::State::Unknown); } void KMyMoneyApp::slotMarkTransactionCleared() { markTransaction(eMyMoney::Split::State::Cleared); } void KMyMoneyApp::slotMarkTransactionReconciled() { markTransaction(eMyMoney::Split::State::Reconciled); } void KMyMoneyApp::slotMarkTransactionNotReconciled() { markTransaction(eMyMoney::Split::State::NotReconciled); } void KMyMoneyApp::markTransaction(eMyMoney::Split::State flag) { KMyMoneyRegister::SelectedTransactions list = d->m_selectedTransactions; KMyMoneyRegister::SelectedTransactions::const_iterator it_t; int cnt = list.count(); int i = 0; slotStatusProgressBar(0, cnt); MyMoneyFileTransaction ft; try { for (it_t = list.constBegin(); it_t != list.constEnd(); ++it_t) { // turn on signals before we modify the last entry in the list cnt--; MyMoneyFile::instance()->blockSignals(cnt != 0); // get a fresh copy MyMoneyTransaction t = MyMoneyFile::instance()->transaction((*it_t).transaction().id()); MyMoneySplit sp = t.splitById((*it_t).split().id()); if (sp.reconcileFlag() != flag) { if (flag == eMyMoney::Split::State::Unknown) { if (d->m_reconciliationAccount.id().isEmpty()) { // in normal mode we cycle through all states switch (sp.reconcileFlag()) { case eMyMoney::Split::State::NotReconciled: sp.setReconcileFlag(eMyMoney::Split::State::Cleared); break; case eMyMoney::Split::State::Cleared: sp.setReconcileFlag(eMyMoney::Split::State::Reconciled); break; case eMyMoney::Split::State::Reconciled: sp.setReconcileFlag(eMyMoney::Split::State::NotReconciled); break; default: break; } } else { // in reconciliation mode we skip the reconciled state switch (sp.reconcileFlag()) { case eMyMoney::Split::State::NotReconciled: sp.setReconcileFlag(eMyMoney::Split::State::Cleared); break; case eMyMoney::Split::State::Cleared: sp.setReconcileFlag(eMyMoney::Split::State::NotReconciled); break; default: break; } } } else { sp.setReconcileFlag(flag); } t.modifySplit(sp); MyMoneyFile::instance()->modifyTransaction(t); } slotStatusProgressBar(i++, 0); } slotStatusProgressBar(-1, -1); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Error"), i18n("Unable to modify transaction: %1, thrown in %2:%3", e.what(), e.file(), e.line())); } } void KMyMoneyApp::slotTransactionsAccept() { KMyMoneyRegister::SelectedTransactions list = d->m_selectedTransactions; KMyMoneyRegister::SelectedTransactions::const_iterator it_t; int cnt = list.count(); int i = 0; slotStatusProgressBar(0, cnt); MyMoneyFileTransaction ft; try { for (it_t = list.constBegin(); it_t != list.constEnd(); ++it_t) { // reload transaction in case it got changed during the course of this loop MyMoneyTransaction t = MyMoneyFile::instance()->transaction((*it_t).transaction().id()); if (t.isImported()) { t.setImported(false); if (!d->m_selectedAccount.id().isEmpty()) { QList list = t.splits(); QList::const_iterator it_s; for (it_s = list.constBegin(); it_s != list.constEnd(); ++it_s) { if ((*it_s).accountId() == d->m_selectedAccount.id()) { if ((*it_s).reconcileFlag() == eMyMoney::Split::State::NotReconciled) { MyMoneySplit s = (*it_s); s.setReconcileFlag(eMyMoney::Split::State::Cleared); t.modifySplit(s); } } } } MyMoneyFile::instance()->modifyTransaction(t); } if ((*it_t).split().isMatched()) { // reload split in case it got changed during the course of this loop MyMoneySplit s = t.splitById((*it_t).split().id()); TransactionMatcher matcher(d->m_selectedAccount); matcher.accept(t, s); } slotStatusProgressBar(i++, 0); } slotStatusProgressBar(-1, -1); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Error"), i18n("Unable to accept transaction: %1, thrown in %2:%3", e.what(), e.file(), e.line())); } } void KMyMoneyApp::slotTransactionGotoAccount() { if (!d->m_accountGoto.isEmpty()) { try { QString transactionId; if (d->m_selectedTransactions.count() == 1) { transactionId = d->m_selectedTransactions[0].transaction().id(); } // make sure to pass a copy, as d->myMoneyView->slotLedgerSelected() overrides // d->m_accountGoto while calling slotUpdateActions() QString accountId = d->m_accountGoto; d->m_myMoneyView->slotLedgerSelected(accountId, transactionId); } catch (const MyMoneyException &) { } } } void KMyMoneyApp::slotTransactionGotoPayee() { if (!d->m_payeeGoto.isEmpty()) { try { QString transactionId; if (d->m_selectedTransactions.count() == 1) { transactionId = d->m_selectedTransactions[0].transaction().id(); } // make sure to pass copies, as d->myMoneyView->slotPayeeSelected() overrides // d->m_payeeGoto and d->m_selectedAccount while calling slotUpdateActions() QString payeeId = d->m_payeeGoto; QString accountId = d->m_selectedAccount.id(); d->m_myMoneyView->slotPayeeSelected(payeeId, accountId, transactionId); } catch (const MyMoneyException &) { } } } void KMyMoneyApp::slotTransactionCreateSchedule() { if (d->m_selectedTransactions.count() == 1) { // make sure to have the current selected split as first split in the schedule MyMoneyTransaction t = d->m_selectedTransactions[0].transaction(); MyMoneySplit s = d->m_selectedTransactions[0].split(); QString splitId = s.id(); s.clearId(); s.setReconcileFlag(eMyMoney::Split::State::NotReconciled); s.setReconcileDate(QDate()); t.removeSplits(); t.addSplit(s); const QList& splits = d->m_selectedTransactions[0].transaction().splits(); QList::const_iterator it_s; for (it_s = splits.begin(); it_s != splits.end(); ++it_s) { if ((*it_s).id() != splitId) { MyMoneySplit s0 = (*it_s); s0.clearId(); s0.setReconcileFlag(eMyMoney::Split::State::NotReconciled); s0.setReconcileDate(QDate()); t.addSplit(s0); } } slotScheduleNew(t); } } void KMyMoneyApp::slotTransactionAssignNumber() { if (d->m_transactionEditor) d->m_transactionEditor->assignNextNumber(); } void KMyMoneyApp::slotTransactionCombine() { qDebug("slotTransactionCombine() not implemented yet"); } void KMyMoneyApp::slotTransactionCopySplits() { MyMoneyFile* file = MyMoneyFile::instance(); if (d->m_selectedTransactions.count() >= 2) { int singleSplitTransactions = 0; int multipleSplitTransactions = 0; KMyMoneyRegister::SelectedTransaction selectedSourceTransaction; foreach (const KMyMoneyRegister::SelectedTransaction& st, d->m_selectedTransactions) { switch (st.transaction().splitCount()) { case 0: break; case 1: singleSplitTransactions++; break; default: selectedSourceTransaction = st; multipleSplitTransactions++; break; } } if (singleSplitTransactions > 0 && multipleSplitTransactions == 1) { MyMoneyFileTransaction ft; try { const MyMoneyTransaction& sourceTransaction = selectedSourceTransaction.transaction(); const MyMoneySplit& sourceSplit = selectedSourceTransaction.split(); foreach (const KMyMoneyRegister::SelectedTransaction& st, d->m_selectedTransactions) { MyMoneyTransaction t = st.transaction(); // don't process the source transaction if (sourceTransaction.id() == t.id()) { continue; } const MyMoneySplit& baseSplit = st.split(); if (t.splitCount() == 1) { foreach (const MyMoneySplit& split, sourceTransaction.splits()) { // Don't copy the source split, as we already have that // as part of the destination transaction if (split.id() == sourceSplit.id()) { continue; } MyMoneySplit sp(split); // clear the ID and reconciliation state sp.clearId(); sp.setReconcileFlag(eMyMoney::Split::State::NotReconciled); sp.setReconcileDate(QDate()); // in case it is a simple transaction consisting of two splits, // we can adjust the share and value part of the second split we // just created. We need to keep a possible price in mind in case // of different currencies if (sourceTransaction.splitCount() == 2) { sp.setValue(-baseSplit.value()); sp.setShares(-(baseSplit.shares() * baseSplit.price())); } t.addSplit(sp); } file->modifyTransaction(t); } } ft.commit(); } catch (const MyMoneyException &) { qDebug() << "transactionCopySplits() failed"; } } } } void KMyMoneyApp::slotMoveToAccount(const QString& id) { // close the menu, if it is still open QWidget* w = factory()->container("transaction_context_menu", this); if (w && w->isVisible()) { w->close(); } if (!d->m_selectedTransactions.isEmpty()) { MyMoneyFileTransaction ft; try { KMyMoneyRegister::SelectedTransactions::const_iterator it_t; for (it_t = d->m_selectedTransactions.constBegin(); it_t != d->m_selectedTransactions.constEnd(); ++it_t) { if (d->m_selectedAccount.accountType() == eMyMoney::Account::Investment) { d->moveInvestmentTransaction(d->m_selectedAccount.id(), id, (*it_t).transaction()); } else { QList::const_iterator it_s; bool changed = false; MyMoneyTransaction t = (*it_t).transaction(); for (it_s = (*it_t).transaction().splits().constBegin(); it_s != (*it_t).transaction().splits().constEnd(); ++it_s) { if ((*it_s).accountId() == d->m_selectedAccount.id()) { MyMoneySplit s = (*it_s); s.setAccountId(id); t.modifySplit(s); changed = true; } } if (changed) { MyMoneyFile::instance()->modifyTransaction(t); } } } ft.commit(); } catch (const MyMoneyException &) { } } } // move a stock transaction from one investment account to another void KMyMoneyApp::Private::moveInvestmentTransaction(const QString& /*fromId*/, const QString& toId, const MyMoneyTransaction& tx) { MyMoneyAccount toInvAcc = MyMoneyFile::instance()->account(toId); MyMoneyTransaction t(tx); // first determine which stock we are dealing with. // fortunately, investment transactions have only one stock involved QString stockAccountId; QString stockSecurityId; MyMoneySplit s; for (QList::const_iterator it_s = t.splits().constBegin(); it_s != t.splits().constEnd(); ++it_s) { stockAccountId = (*it_s).accountId(); stockSecurityId = MyMoneyFile::instance()->account(stockAccountId).currencyId(); if (!MyMoneyFile::instance()->security(stockSecurityId).isCurrency()) { s = *it_s; break; } } // Now check the target investment account to see if it // contains a stock with this id QString newStockAccountId; QStringList accountList = toInvAcc.accountList(); for (QStringList::const_iterator it_a = accountList.constBegin(); it_a != accountList.constEnd(); ++it_a) { if (MyMoneyFile::instance()->account((*it_a)).currencyId() == stockSecurityId) { newStockAccountId = (*it_a); break; } } // if it doesn't exist, we need to add it as a copy of the old one // no 'copyAccount()' function?? if (newStockAccountId.isEmpty()) { MyMoneyAccount stockAccount = MyMoneyFile::instance()->account(stockAccountId); MyMoneyAccount newStock; newStock.setName(stockAccount.name()); newStock.setNumber(stockAccount.number()); newStock.setDescription(stockAccount.description()); newStock.setInstitutionId(stockAccount.institutionId()); newStock.setOpeningDate(stockAccount.openingDate()); newStock.setAccountType(stockAccount.accountType()); newStock.setCurrencyId(stockAccount.currencyId()); newStock.setClosed(stockAccount.isClosed()); MyMoneyFile::instance()->addAccount(newStock, toInvAcc); newStockAccountId = newStock.id(); } // now update the split and the transaction s.setAccountId(newStockAccountId); t.modifySplit(s); MyMoneyFile::instance()->modifyTransaction(t); } void KMyMoneyApp::slotUpdateMoveToAccountMenu() { createTransactionMoveMenu(); // in case we were not able to create the selector, we // better get out of here. Anything else would cause // a crash later on (accountSet.load) if (!d->m_moveToAccountSelector) return; if (!d->m_selectedAccount.id().isEmpty()) { AccountSet accountSet; if (d->m_selectedAccount.accountType() == eMyMoney::Account::Investment) { accountSet.addAccountType(eMyMoney::Account::Investment); } else if (d->m_selectedAccount.isAssetLiability()) { accountSet.addAccountType(eMyMoney::Account::Checkings); accountSet.addAccountType(eMyMoney::Account::Savings); accountSet.addAccountType(eMyMoney::Account::Cash); accountSet.addAccountType(eMyMoney::Account::AssetLoan); accountSet.addAccountType(eMyMoney::Account::CertificateDep); accountSet.addAccountType(eMyMoney::Account::MoneyMarket); accountSet.addAccountType(eMyMoney::Account::Asset); accountSet.addAccountType(eMyMoney::Account::Currency); accountSet.addAccountType(eMyMoney::Account::CreditCard); accountSet.addAccountType(eMyMoney::Account::Loan); accountSet.addAccountType(eMyMoney::Account::Liability); } else if (d->m_selectedAccount.isIncomeExpense()) { accountSet.addAccountType(eMyMoney::Account::Income); accountSet.addAccountType(eMyMoney::Account::Expense); } accountSet.load(d->m_moveToAccountSelector); // remove those accounts that we currently reference KMyMoneyRegister::SelectedTransactions::const_iterator it_t; for (it_t = d->m_selectedTransactions.constBegin(); it_t != d->m_selectedTransactions.constEnd(); ++it_t) { QList::const_iterator it_s; for (it_s = (*it_t).transaction().splits().constBegin(); it_s != (*it_t).transaction().splits().constEnd(); ++it_s) { d->m_moveToAccountSelector->removeItem((*it_s).accountId()); } } // remove those accounts from the list that are denominated // in a different currency QStringList list = d->m_moveToAccountSelector->accountList(); QList::const_iterator it_a; for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) { MyMoneyAccount acc = MyMoneyFile::instance()->account(*it_a); if (acc.currencyId() != d->m_selectedAccount.currencyId()) d->m_moveToAccountSelector->removeItem((*it_a)); } } } void KMyMoneyApp::slotTransactionMatch() { // if the menu action is retrieved it can contain an '&' character for the accelerator causing the comparison to fail if not removed QString transactionActionText = actionCollection()->action(s_Actions[Action::TransactionMatch])->text(); transactionActionText.remove('&'); if (transactionActionText == i18nc("Button text for match transaction", "Match")) transactionMatch(); else transactionUnmatch(); } void KMyMoneyApp::transactionUnmatch() { KMyMoneyRegister::SelectedTransactions::const_iterator it; MyMoneyFileTransaction ft; try { for (it = d->m_selectedTransactions.constBegin(); it != d->m_selectedTransactions.constEnd(); ++it) { if ((*it).split().isMatched()) { TransactionMatcher matcher(d->m_selectedAccount); matcher.unmatch((*it).transaction(), (*it).split()); } } ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to unmatch the selected transactions"), e.what()); } } void KMyMoneyApp::transactionMatch() { if (d->m_selectedTransactions.count() != 2) return; MyMoneyTransaction startMatchTransaction; MyMoneyTransaction endMatchTransaction; MyMoneySplit startSplit; MyMoneySplit endSplit; KMyMoneyRegister::SelectedTransactions::const_iterator it; KMyMoneyRegister::SelectedTransactions toBeDeleted; for (it = d->m_selectedTransactions.constBegin(); it != d->m_selectedTransactions.constEnd(); ++it) { if ((*it).transaction().isImported()) { if (endMatchTransaction.id().isEmpty()) { endMatchTransaction = (*it).transaction(); endSplit = (*it).split(); toBeDeleted << *it; } else { //This is a second imported transaction, we still want to merge startMatchTransaction = (*it).transaction(); startSplit = (*it).split(); } } else if (!(*it).split().isMatched()) { if (startMatchTransaction.id().isEmpty()) { startMatchTransaction = (*it).transaction(); startSplit = (*it).split(); } else { endMatchTransaction = (*it).transaction(); endSplit = (*it).split(); toBeDeleted << *it; } } } #if 0 KMergeTransactionsDlg dlg(d->m_selectedAccount); dlg.addTransaction(startMatchTransaction); dlg.addTransaction(endMatchTransaction); if (dlg.exec() == QDialog::Accepted) #endif { MyMoneyFileTransaction ft; try { if (startMatchTransaction.id().isEmpty()) throw MYMONEYEXCEPTION(i18n("No manually entered transaction selected for matching")); if (endMatchTransaction.id().isEmpty()) throw MYMONEYEXCEPTION(i18n("No imported transaction selected for matching")); TransactionMatcher matcher(d->m_selectedAccount); matcher.match(startMatchTransaction, startSplit, endMatchTransaction, endSplit, true); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to match the selected transactions"), e.what()); } } } void KMyMoneyApp::showContextMenu(const QString& containerName) { QWidget* w = factory()->container(containerName, this); QMenu *menu = dynamic_cast(w); if (menu) menu->exec(QCursor::pos()); else qDebug("menu '%s' not found: w = %p, menu = %p", qPrintable(containerName), w, menu); } void KMyMoneyApp::slotShowTransactionContextMenu() { if (d->m_selectedTransactions.isEmpty() && d->m_selectedSchedule != MyMoneySchedule()) { showContextMenu("schedule_context_menu"); } else { showContextMenu("transaction_context_menu"); } } void KMyMoneyApp::slotShowInvestmentContextMenu() { showContextMenu("investment_context_menu"); } void KMyMoneyApp::slotShowScheduleContextMenu() { showContextMenu("schedule_context_menu"); } void KMyMoneyApp::slotShowAccountContextMenu(const MyMoneyObject& obj) { // qDebug("KMyMoneyApp::slotShowAccountContextMenu"); if (typeid(obj) != typeid(MyMoneyAccount)) return; const MyMoneyAccount& acc = dynamic_cast(obj); // if the selected account is actually a stock account, we // call the right slot instead if (acc.isInvest()) { showContextMenu("investment_context_menu"); } else if (acc.isIncomeExpense()) { showContextMenu("category_context_menu"); } else { showContextMenu("account_context_menu"); } } void KMyMoneyApp::slotShowInstitutionContextMenu(const MyMoneyObject& obj) { if (typeid(obj) != typeid(MyMoneyInstitution)) return; showContextMenu("institution_context_menu"); } void KMyMoneyApp::slotShowPayeeContextMenu() { showContextMenu("payee_context_menu"); } void KMyMoneyApp::slotShowTagContextMenu() { showContextMenu("tag_context_menu"); } void KMyMoneyApp::slotShowBudgetContextMenu() { showContextMenu("budget_context_menu"); } void KMyMoneyApp::slotShowCurrencyContextMenu() { showContextMenu("currency_context_menu"); } void KMyMoneyApp::slotShowPriceContextMenu() { showContextMenu("price_context_menu"); } void KMyMoneyApp::slotShowOnlineJobContextMenu() { showContextMenu("onlinejob_context_menu"); } void KMyMoneyApp::slotPrintView() { d->m_myMoneyView->slotPrintView(); } void KMyMoneyApp::updateCaption(bool skipActions) { QString caption; caption = d->m_fileName.fileName(); if (caption.isEmpty() && d->m_myMoneyView && d->m_myMoneyView->fileOpen()) caption = i18n("Untitled"); // MyMoneyFile::instance()->dirty() throws an exception, if // there's no storage object available. In this case, we // assume that the storage object is not changed. Actually, // this can only happen if we are newly created early on. bool modified; try { modified = MyMoneyFile::instance()->dirty(); } catch (const MyMoneyException &) { modified = false; skipActions = true; } #ifdef KMM_DEBUG caption += QString(" (%1 x %2)").arg(width()).arg(height()); #endif setCaption(caption, modified); if (!skipActions) { d->m_myMoneyView->enableViewsIfFileOpen(); slotUpdateActions(); } } void KMyMoneyApp::slotUpdateActions() { MyMoneyFile* file = MyMoneyFile::instance(); const bool fileOpen = d->m_myMoneyView->fileOpen(); const bool modified = file->dirty(); const bool importRunning = (d->m_smtReader != 0); QWidget* w; KActionCollection *aC = actionCollection(); // ************* // Disabling actions to be disabled at this point // ************* { static const QVector disabledActions { Action::AccountStartReconciliation, Action::AccountFinishReconciliation, Action::AccountPostponeReconciliation, Action::AccountEdit, Action::AccountDelete, Action::AccountOpen, Action::AccountClose, Action::AccountReopen, Action::AccountTransactionReport, Action::AccountOnlineMap, Action::AccountOnlineUnmap, Action::AccountUpdate, Action::AccountUpdateAll, Action::AccountBalanceChart, Action::CategoryEdit, Action::CategoryDelete, Action::InstitutionEdit, Action::InstitutionDelete, Action::InvestmentEdit, Action::InvestmentNew, Action::InvestmentDelete, Action::InvestmentOnlinePrice, Action::InvestmentManualPrice, Action::ScheduleEdit, Action::ScheduleDelete, Action::ScheduleEnter, Action::ScheduleSkip, Action::PayeeDelete, Action::PayeeRename, Action::PayeeMerge, Action::TagDelete, Action::TagRename, Action::BudgetDelete, Action::BudgetRename, Action::BudgetChangeYear, Action::BudgetNew, Action::BudgetCopy, Action::BudgetForecast, Action::TransactionEdit, Action::TransactionEditSplits, Action::TransactionEnter, Action::TransactionCancel, Action::TransactionDelete, Action::TransactionMatch, Action::TransactionAccept, Action::TransactionDuplicate, Action::TransactionToggleReconciled, Action::TransactionToggleCleared, Action::TransactionGoToAccount, Action::TransactionGoToPayee, Action::TransactionAssignNumber, Action::TransactionCreateSchedule, Action::TransactionCombine, Action::TransactionSelectAll, Action::TransactionCopySplits, Action::ScheduleEdit, Action::ScheduleDelete, Action::ScheduleDuplicate, Action::ScheduleEnter, Action::ScheduleSkip, Action::CurrencyRename, Action::CurrencyDelete, Action::CurrencySetBase, Action::PriceEdit, Action::PriceDelete, Action::PriceUpdate }; foreach (const auto a, disabledActions) aC->action(s_Actions.value(a))->setEnabled(false); } // ************* // Disabling actions based on conditions // ************* { QString tooltip = i18n("Create a new transaction"); const QVector> actionStates { {qMakePair(Action::FileOpenDatabase, true)}, {qMakePair(Action::FileSaveAsDatabase, fileOpen)}, {qMakePair(Action::FilePersonalData, fileOpen)}, {qMakePair(Action::FileBackup, (fileOpen && !d->m_myMoneyView->isDatabase()))}, {qMakePair(Action::FileInformation, fileOpen)}, {qMakePair(Action::FileImportGNC, !importRunning)}, {qMakePair(Action::FileImportTemplate, fileOpen && !importRunning)}, {qMakePair(Action::FileExportTemplate, fileOpen && !importRunning)}, #ifdef KMM_DEBUG {qMakePair(Action::FileDump, fileOpen)}, #endif {qMakePair(Action::EditFindTransaction, fileOpen)}, {qMakePair(Action::ToolCurrencies, fileOpen)}, {qMakePair(Action::ToolPrices, fileOpen)}, {qMakePair(Action::ToolUpdatePrices, fileOpen)}, {qMakePair(Action::ToolConsistency, fileOpen)}, {qMakePair(Action::AccountNew, fileOpen)}, {qMakePair(Action::AccountCreditTransfer, onlineJobAdministration::instance()->canSendCreditTransfer())}, {qMakePair(Action::CategoryDelete, fileOpen)}, {qMakePair(Action::InstitutionNew, fileOpen)}, {qMakePair(Action::TransactionNew, (fileOpen && d->m_myMoneyView->canCreateTransactions(KMyMoneyRegister::SelectedTransactions(), tooltip)))}, {qMakePair(Action::ScheduleNew, fileOpen)}, {qMakePair(Action::CurrencyNew, fileOpen)}, {qMakePair(Action::PriceNew, fileOpen)}, }; foreach (const auto a, actionStates) aC->action(s_Actions.value(a.first))->setEnabled(a.second); } // ************* // Disabling standard actions based on conditions // ************* aC->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Save)))->setEnabled(modified && !d->m_myMoneyView->isDatabase()); aC->action(QString::fromLatin1(KStandardAction::name(KStandardAction::SaveAs)))->setEnabled(fileOpen); aC->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Close)))->setEnabled(fileOpen); aC->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Print)))->setEnabled(fileOpen && d->m_myMoneyView->canPrint()); aC->action(s_Actions[Action::TransactionMatch])->setText(i18nc("Button text for match transaction", "Match")); aC->action(s_Actions[Action::TransactionNew])->setToolTip(i18n("Create a new transaction")); w = factory()->container(s_Actions[Action::TransactionMoveMenu], this); if (w) w->setEnabled(false); w = factory()->container(s_Actions[Action::TransactionMarkMenu], this); if (w) w->setEnabled(false); w = factory()->container(s_Actions[Action::TransactionContextMarkMenu], this); if (w) w->setEnabled(false); // ************* // Enabling actions based on conditions // ************* // FIXME for now it's always on, but we should only allow it, if we // can select at least a single transaction aC->action(s_Actions[Action::TransactionSelectAll])->setEnabled(true); if (!d->m_selectedTransactions.isEmpty()) { // enable 'delete transaction' only if at least one of the // selected transactions does not reference a closed account bool enable = false; KMyMoneyRegister::SelectedTransactions::const_iterator it_t; for (it_t = d->m_selectedTransactions.constBegin(); (enable == false) && (it_t != d->m_selectedTransactions.constEnd()); ++it_t) { enable = !(*it_t).transaction().id().isEmpty() && !file->referencesClosedAccount((*it_t).transaction()); } aC->action(s_Actions[Action::TransactionDelete])->setEnabled(enable); if (!d->m_transactionEditor) { QString tooltip = i18n("Duplicate the current selected transactions"); aC->action(s_Actions[Action::TransactionDuplicate])->setEnabled(d->m_myMoneyView->canDuplicateTransactions(d->m_selectedTransactions, tooltip) && !d->m_selectedTransactions[0].transaction().id().isEmpty()); aC->action(s_Actions[Action::TransactionDuplicate])->setToolTip(tooltip); if (d->m_myMoneyView->canEditTransactions(d->m_selectedTransactions, tooltip)) { aC->action(s_Actions[Action::TransactionEdit])->setEnabled(true); // editing splits is allowed only if we have one transaction selected if (d->m_selectedTransactions.count() == 1) { aC->action(s_Actions[Action::TransactionEditSplits])->setEnabled(true); } if (d->m_selectedAccount.isAssetLiability() && d->m_selectedAccount.accountType() != eMyMoney::Account::Investment) { aC->action(s_Actions[Action::TransactionCreateSchedule])->setEnabled(d->m_selectedTransactions.count() == 1); } } aC->action(s_Actions[Action::TransactionEdit])->setToolTip(tooltip); if (!d->m_selectedAccount.isClosed()) { w = factory()->container(s_Actions[Action::TransactionMoveMenu], this); if (w) w->setEnabled(true); } w = factory()->container(s_Actions[Action::TransactionMarkMenu], this); if (w) w->setEnabled(true); w = factory()->container(s_Actions[Action::TransactionContextMarkMenu], this); if (w) w->setEnabled(true); // Allow marking the transaction if at least one is selected aC->action(s_Actions[Action::TransactionToggleCleared])->setEnabled(true); aC->action(s_Actions[Action::TransactionReconciled])->setEnabled(true); aC->action(s_Actions[Action::TransactionNotReconciled])->setEnabled(true); aC->action(s_Actions[Action::TransactionToggleReconciled])->setEnabled(true); if (!d->m_accountGoto.isEmpty()) aC->action(s_Actions[Action::TransactionGoToAccount])->setEnabled(true); if (!d->m_payeeGoto.isEmpty()) aC->action(s_Actions[Action::TransactionGoToPayee])->setEnabled(true); // Matching is enabled as soon as one regular and one imported transaction is selected int matchedCount = 0; int importedCount = 0; KMyMoneyRegister::SelectedTransactions::const_iterator it; for (it = d->m_selectedTransactions.constBegin(); it != d->m_selectedTransactions.constEnd(); ++it) { if ((*it).transaction().isImported()) ++importedCount; if ((*it).split().isMatched()) ++matchedCount; } if (d->m_selectedTransactions.count() == 2 /* && aC->action(s_Actions[Action::TransactionEdit])->isEnabled() */) { aC->action(s_Actions[Action::TransactionMatch])->setEnabled(true); } if (importedCount != 0 || matchedCount != 0) aC->action(s_Actions[Action::TransactionAccept])->setEnabled(true); if (matchedCount != 0) { aC->action(s_Actions[Action::TransactionMatch])->setEnabled(true); aC->action(s_Actions[Action::TransactionMatch])->setText(i18nc("Button text for unmatch transaction", "Unmatch")); aC->action(s_Actions[Action::TransactionMatch])->setIcon(QIcon("process-stop")); } if (d->m_selectedTransactions.count() > 1) { aC->action(s_Actions[Action::TransactionCombine])->setEnabled(true); } if (d->m_selectedTransactions.count() >= 2) { int singleSplitTransactions = 0; int multipleSplitTransactions = 0; foreach (const KMyMoneyRegister::SelectedTransaction& st, d->m_selectedTransactions) { switch (st.transaction().splitCount()) { case 0: break; case 1: singleSplitTransactions++; break; default: multipleSplitTransactions++; break; } } if (singleSplitTransactions > 0 && multipleSplitTransactions == 1) { aC->action(s_Actions[Action::TransactionCopySplits])->setEnabled(true); } } if (d->m_selectedTransactions.count() >= 2) { int singleSplitTransactions = 0; int multipleSplitTransactions = 0; foreach(const KMyMoneyRegister::SelectedTransaction& st, d->m_selectedTransactions) { switch(st.transaction().splitCount()) { case 0: break; case 1: singleSplitTransactions++; break; default: multipleSplitTransactions++; break; } } if(singleSplitTransactions > 0 && multipleSplitTransactions == 1) { aC->action(s_Actions[Action::TransactionCopySplits])->setEnabled(true); } } } else { aC->action(s_Actions[Action::TransactionAssignNumber])->setEnabled(d->m_transactionEditor->canAssignNumber()); aC->action(s_Actions[Action::TransactionNew])->setEnabled(false); aC->action(s_Actions[Action::TransactionDelete])->setEnabled(false); QString reason; aC->action(s_Actions[Action::TransactionEnter])->setEnabled(d->m_transactionEditor->isComplete(reason)); //FIXME: Port to KDE4 // the next line somehow worked in KDE3 but does not have // any influence under KDE4 /// Works for me when 'reason' is set. Allan aC->action(s_Actions[Action::TransactionEnter])->setToolTip(reason); aC->action(s_Actions[Action::TransactionCancel])->setEnabled(true); } } QList accList; file->accountList(accList); QList::const_iterator it_a; QMap::const_iterator it_p = d->m_onlinePlugins.constEnd(); for (it_a = accList.constBegin(); (it_p == d->m_onlinePlugins.constEnd()) && (it_a != accList.constEnd()); ++it_a) { if (!(*it_a).onlineBankingSettings().value("provider").isEmpty()) { // check if provider is available it_p = d->m_onlinePlugins.constFind((*it_a).onlineBankingSettings().value("provider")); if (it_p != d->m_onlinePlugins.constEnd()) { QStringList protocols; (*it_p)->protocols(protocols); if (protocols.count() > 0) { aC->action(s_Actions[Action::AccountUpdateAll])->setEnabled(true); aC->action(s_Actions[Action::AccountUpdateMenu])->setEnabled(true); } } } } QBitArray skip((int)eStorage::Reference::Count); if (!d->m_selectedAccount.id().isEmpty()) { if (!file->isStandardAccount(d->m_selectedAccount.id())) { switch (d->m_selectedAccount.accountGroup()) { case eMyMoney::Account::Asset: case eMyMoney::Account::Liability: case eMyMoney::Account::Equity: aC->action(s_Actions[Action::AccountTransactionReport])->setEnabled(true); aC->action(s_Actions[Action::AccountEdit])->setEnabled(true); aC->action(s_Actions[Action::AccountDelete])->setEnabled(!file->isReferenced(d->m_selectedAccount)); aC->action(s_Actions[Action::AccountOpen])->setEnabled(true); if (d->m_selectedAccount.accountGroup() != eMyMoney::Account::Equity) { if (d->m_reconciliationAccount.id().isEmpty()) { aC->action(s_Actions[Action::AccountStartReconciliation])->setEnabled(true); aC->action(s_Actions[Action::AccountStartReconciliation])->setToolTip(i18n("Reconcile")); } else { QString tip; tip = i18n("Reconcile - disabled because you are currently reconciling %1", d->m_reconciliationAccount.name()); aC->action(s_Actions[Action::AccountStartReconciliation])->setToolTip(tip); if (!d->m_transactionEditor) { aC->action(s_Actions[Action::AccountFinishReconciliation])->setEnabled(d->m_selectedAccount.id() == d->m_reconciliationAccount.id()); aC->action(s_Actions[Action::AccountPostponeReconciliation])->setEnabled(d->m_selectedAccount.id() == d->m_reconciliationAccount.id()); } } } if (d->m_selectedAccount.accountType() == eMyMoney::Account::Investment) aC->action(s_Actions[Action::InvestmentNew])->setEnabled(true); if (d->m_selectedAccount.isClosed()) aC->action(s_Actions[Action::AccountReopen])->setEnabled(true); else enableCloseAccountAction(d->m_selectedAccount); if (!d->m_selectedAccount.onlineBankingSettings().value("provider").isEmpty()) { aC->action(s_Actions[Action::AccountOnlineUnmap])->setEnabled(true); // check if provider is available QMap::const_iterator it_p; it_p = d->m_onlinePlugins.constFind(d->m_selectedAccount.onlineBankingSettings().value("provider")); if (it_p != d->m_onlinePlugins.constEnd()) { QStringList protocols; (*it_p)->protocols(protocols); if (protocols.count() > 0) { aC->action(s_Actions[Action::AccountUpdate])->setEnabled(true); aC->action(s_Actions[Action::AccountUpdateMenu])->setEnabled(true); } } } else { aC->action(s_Actions[Action::AccountOnlineMap])->setEnabled(d->m_onlinePlugins.count() > 0); } aC->action(s_Actions[Action::AccountBalanceChart])->setEnabled(true); break; case eMyMoney::Account::Income : case eMyMoney::Account::Expense : aC->action(s_Actions[Action::CategoryEdit])->setEnabled(true); // enable delete action, if category/account itself is not referenced // by any object except accounts, because we want to allow // deleting of sub-categories. Also, we allow transactions, schedules and budgets // to be present because we can re-assign them during the delete process skip.fill(false); skip.setBit((int)eStorage::Reference::Transaction); skip.setBit((int)eStorage::Reference::Account); skip.setBit((int)eStorage::Reference::Schedule); skip.setBit((int)eStorage::Reference::Budget); aC->action(s_Actions[Action::CategoryDelete])->setEnabled(!file->isReferenced(d->m_selectedAccount, skip)); aC->action(s_Actions[Action::AccountOpen])->setEnabled(true); break; default: break; } } } if (!d->m_selectedInstitution.id().isEmpty()) { aC->action(s_Actions[Action::InstitutionEdit])->setEnabled(true); aC->action(s_Actions[Action::InstitutionDelete])->setEnabled(!file->isReferenced(d->m_selectedInstitution)); } if (!d->m_selectedInvestment.id().isEmpty()) { aC->action(s_Actions[Action::InvestmentEdit])->setEnabled(true); aC->action(s_Actions[Action::InvestmentDelete])->setEnabled(!file->isReferenced(d->m_selectedInvestment)); aC->action(s_Actions[Action::InvestmentManualPrice])->setEnabled(true); try { MyMoneySecurity security = MyMoneyFile::instance()->security(d->m_selectedInvestment.currencyId()); if (!security.value("kmm-online-source").isEmpty()) aC->action(s_Actions[Action::InvestmentOnlinePrice])->setEnabled(true); } catch (const MyMoneyException &e) { qDebug("Error retrieving security for investment %s: %s", qPrintable(d->m_selectedInvestment.name()), qPrintable(e.what())); } if (d->m_selectedInvestment.isClosed()) aC->action(s_Actions[Action::AccountReopen])->setEnabled(true); else enableCloseAccountAction(d->m_selectedInvestment); } if (!d->m_selectedSchedule.id().isEmpty()) { aC->action(s_Actions[Action::ScheduleEdit])->setEnabled(true); aC->action(s_Actions[Action::ScheduleDuplicate])->setEnabled(true); aC->action(s_Actions[Action::ScheduleDelete])->setEnabled(!file->isReferenced(d->m_selectedSchedule)); if (!d->m_selectedSchedule.isFinished()) { aC->action(s_Actions[Action::ScheduleEnter])->setEnabled(true); // a schedule with a single occurrence cannot be skipped if (d->m_selectedSchedule.occurrence() != eMyMoney::Schedule::Occurrence::Once) { aC->action(s_Actions[Action::ScheduleSkip])->setEnabled(true); } } } if (d->m_selectedPayees.count() >= 1) { aC->action(s_Actions[Action::PayeeRename])->setEnabled(d->m_selectedPayees.count() == 1); aC->action(s_Actions[Action::PayeeMerge])->setEnabled(d->m_selectedPayees.count() > 1); aC->action(s_Actions[Action::PayeeDelete])->setEnabled(true); } if (d->m_selectedTags.count() >= 1) { aC->action(s_Actions[Action::TagRename])->setEnabled(d->m_selectedTags.count() == 1); aC->action(s_Actions[Action::TagDelete])->setEnabled(true); } aC->action(s_Actions[Action::BudgetNew])->setEnabled(true); if (d->m_selectedBudgets.count() >= 1) { aC->action(s_Actions[Action::BudgetDelete])->setEnabled(true); if (d->m_selectedBudgets.count() == 1) { aC->action(s_Actions[Action::BudgetChangeYear])->setEnabled(true); aC->action(s_Actions[Action::BudgetCopy])->setEnabled(true); aC->action(s_Actions[Action::BudgetRename])->setEnabled(true); aC->action(s_Actions[Action::BudgetForecast])->setEnabled(true); } } if (!d->m_selectedCurrency.id().isEmpty()) { aC->action(s_Actions[Action::CurrencyRename])->setEnabled(true); // no need to check each transaction. accounts are enough in this case skip.fill(false); skip.setBit((int)eStorage::Reference::Transaction); aC->action(s_Actions[Action::CurrencyDelete])->setEnabled(!file->isReferenced(d->m_selectedCurrency, skip)); if (d->m_selectedCurrency.id() != file->baseCurrency().id()) aC->action(s_Actions[Action::CurrencySetBase])->setEnabled(true); } if (!d->m_selectedPrice.from().isEmpty() && d->m_selectedPrice.source() != QLatin1String("KMyMoney")) { aC->action(s_Actions[Action::PriceEdit])->setEnabled(true); aC->action(s_Actions[Action::PriceDelete])->setEnabled(true); //enable online update if it is a currency MyMoneySecurity security = MyMoneyFile::instance()->security(d->m_selectedPrice.from()); aC->action(s_Actions[Action::PriceUpdate])->setEnabled(security.isCurrency()); } } void KMyMoneyApp::slotResetSelections() { slotSelectAccount(MyMoneyAccount()); slotSelectInstitution(MyMoneyInstitution()); slotSelectInvestment(MyMoneyAccount()); slotSelectSchedule(); slotSelectCurrency(); slotSelectPrice(); slotSelectPayees(QList()); slotSelectTags(QList()); slotSelectBudget(QList()); slotSelectTransactions(KMyMoneyRegister::SelectedTransactions()); slotUpdateActions(); } void KMyMoneyApp::slotSelectCurrency() { slotSelectCurrency(MyMoneySecurity()); } void KMyMoneyApp::slotSelectCurrency(const MyMoneySecurity& currency) { d->m_selectedCurrency = currency; slotUpdateActions(); emit currencySelected(d->m_selectedCurrency); } void KMyMoneyApp::slotSelectPrice() { slotSelectPrice(MyMoneyPrice()); } void KMyMoneyApp::slotSelectPrice(const MyMoneyPrice& price) { d->m_selectedPrice = price; slotUpdateActions(); emit priceSelected(d->m_selectedPrice); } void KMyMoneyApp::slotSelectBudget(const QList& list) { d->m_selectedBudgets = list; slotUpdateActions(); emit budgetSelected(d->m_selectedBudgets); } void KMyMoneyApp::slotSelectPayees(const QList& list) { d->m_selectedPayees = list; slotUpdateActions(); emit payeesSelected(d->m_selectedPayees); } void KMyMoneyApp::slotSelectTags(const QList& list) { d->m_selectedTags = list; slotUpdateActions(); emit tagsSelected(d->m_selectedTags); } void KMyMoneyApp::slotSelectTransactions(const KMyMoneyRegister::SelectedTransactions& list) { // list can either contain a list of transactions or a single selected scheduled transaction // in the latter case, the transaction id is actually the one of the schedule. In order // to differentiate between the two, we just ask for the schedule. If we don't find one - because // we passed the id of a real transaction - then we know that fact. We use the schedule here, // because the list of schedules is kept in a cache by MyMoneyFile. This way, we save some trips // to the backend which we would have to do if we check for the transaction. d->m_selectedTransactions.clear(); d->m_selectedSchedule = MyMoneySchedule(); d->m_accountGoto.clear(); d->m_payeeGoto.clear(); if (!list.isEmpty() && !list.first().isScheduled()) { d->m_selectedTransactions = list; if (list.count() == 1) { const MyMoneySplit& sp = d->m_selectedTransactions[0].split(); if (!sp.payeeId().isEmpty()) { try { MyMoneyPayee payee = MyMoneyFile::instance()->payee(sp.payeeId()); if (!payee.name().isEmpty()) { d->m_payeeGoto = payee.id(); QString name = payee.name(); name.replace(QRegExp("&(?!&)"), "&&"); actionCollection()->action(s_Actions[Action::TransactionGoToPayee])->setText(i18n("Go to '%1'", name)); } } catch (const MyMoneyException &) { } } try { QList::const_iterator it_s; const MyMoneyTransaction& t = d->m_selectedTransactions[0].transaction(); // search the first non-income/non-expense accunt and use it for the 'goto account' const MyMoneySplit& sp = d->m_selectedTransactions[0].split(); for (it_s = t.splits().begin(); it_s != t.splits().end(); ++it_s) { if ((*it_s).id() != sp.id()) { MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId()); if (!acc.isIncomeExpense()) { // for stock accounts we show the portfolio account if (acc.isInvest()) { acc = MyMoneyFile::instance()->account(acc.parentAccountId()); } d->m_accountGoto = acc.id(); QString name = acc.name(); name.replace(QRegExp("&(?!&)"), "&&"); actionCollection()->action(s_Actions[Action::TransactionGoToAccount])->setText(i18n("Go to '%1'", name)); break; } } } } catch (const MyMoneyException &) { } } slotUpdateActions(); emit transactionsSelected(d->m_selectedTransactions); } else if (!list.isEmpty()) { slotSelectSchedule(MyMoneyFile::instance()->schedule(list.first().scheduleId())); } else { slotUpdateActions(); } // make sure, we show some neutral menu entry if we don't have an object if (d->m_payeeGoto.isEmpty()) actionCollection()->action(s_Actions[Action::TransactionGoToPayee])->setText(i18n("Go to payee")); if (d->m_accountGoto.isEmpty()) actionCollection()->action(s_Actions[Action::TransactionGoToAccount])->setText(i18n("Go to account")); } void KMyMoneyApp::slotSelectInstitution(const MyMoneyObject& institution) { if (typeid(institution) != typeid(MyMoneyInstitution)) return; d->m_selectedInstitution = dynamic_cast(institution); // qDebug("slotSelectInstitution('%s')", d->m_selectedInstitution.name().data()); slotUpdateActions(); emit institutionSelected(d->m_selectedInstitution); } void KMyMoneyApp::slotSelectAccount(const MyMoneyObject& obj) { if (typeid(obj) != typeid(MyMoneyAccount)) return; d->m_selectedAccount = MyMoneyAccount(); const MyMoneyAccount& acc = dynamic_cast(obj); if (!acc.isInvest()) d->m_selectedAccount = acc; // qDebug("slotSelectAccount('%s')", d->m_selectedAccount.name().data()); slotUpdateActions(); emit accountSelected(d->m_selectedAccount); } void KMyMoneyApp::slotSelectInvestment(const MyMoneyObject& obj) { if (typeid(obj) != typeid(MyMoneyAccount)) return; // qDebug("slotSelectInvestment('%s')", account.name().data()); d->m_selectedInvestment = MyMoneyAccount(); const MyMoneyAccount& acc = dynamic_cast(obj); if (acc.isInvest()) d->m_selectedInvestment = acc; slotUpdateActions(); emit investmentSelected(d->m_selectedInvestment); } void KMyMoneyApp::slotSelectSchedule() { slotSelectSchedule(MyMoneySchedule()); } void KMyMoneyApp::slotSelectSchedule(const MyMoneySchedule& schedule) { // qDebug("slotSelectSchedule('%s')", schedule.name().data()); d->m_selectedSchedule = schedule; slotUpdateActions(); emit scheduleSelected(d->m_selectedSchedule); } void KMyMoneyApp::slotDataChanged() { // As this method is called every time the MyMoneyFile instance // notifies a modification, it's the perfect place to start the timer if needed if (d->m_autoSaveEnabled && !d->m_autoSaveTimer->isActive()) { d->m_autoSaveTimer->setSingleShot(true); d->m_autoSaveTimer->start(d->m_autoSavePeriod * 60 * 1000); //miliseconds } updateCaption(); } void KMyMoneyApp::slotCurrencyDialog() { QPointer dlg = new KCurrencyEditDlg(this); connect(dlg, SIGNAL(selectObject(MyMoneySecurity)), this, SLOT(slotSelectCurrency(MyMoneySecurity))); connect(dlg, SIGNAL(openContextMenu(MyMoneySecurity)), this, SLOT(slotShowCurrencyContextMenu())); connect(this, SIGNAL(currencyRename()), dlg, SLOT(slotStartRename())); connect(dlg, SIGNAL(updateCurrency(QString,QString,QString)), this, SLOT(slotCurrencyUpdate(QString,QString,QString))); connect(this, SIGNAL(currencyCreated(QString)), dlg, SLOT(slotSelectCurrency(QString))); connect(dlg, SIGNAL(selectBaseCurrency(MyMoneySecurity)), this, SLOT(slotCurrencySetBase())); dlg->exec(); delete dlg; slotSelectCurrency(MyMoneySecurity()); } void KMyMoneyApp::slotPriceDialog() { QPointer dlg = new KMyMoneyPriceDlg(this); connect(dlg, SIGNAL(selectObject(MyMoneyPrice)), this, SLOT(slotSelectPrice(MyMoneyPrice))); connect(dlg, SIGNAL(openContextMenu(MyMoneyPrice)), this, SLOT(slotShowPriceContextMenu())); connect(this, SIGNAL(priceNew()), dlg, SLOT(slotNewPrice())); connect(this, SIGNAL(priceEdit()), dlg, SLOT(slotEditPrice())); connect(this, SIGNAL(priceDelete()), dlg, SLOT(slotDeletePrice())); connect(this, SIGNAL(priceOnlineUpdate()), dlg, SLOT(slotOnlinePriceUpdate())); dlg->exec(); } void KMyMoneyApp::slotFileConsistencyCheck() { d->consistencyCheck(true); updateCaption(); } void KMyMoneyApp::Private::consistencyCheck(bool alwaysDisplayResult) { KMSTATUS(i18n("Running consistency check...")); MyMoneyFileTransaction ft; try { m_consistencyCheckResult = MyMoneyFile::instance()->consistencyCheck(); ft.commit(); } catch (const MyMoneyException &e) { m_consistencyCheckResult.append(i18n("Consistency check failed: %1", e.what())); // always display the result if the check failed alwaysDisplayResult = true; } // in case the consistency check was OK, we get a single line as result // in all errneous cases, we get more than one line and force the // display of them. if (alwaysDisplayResult || m_consistencyCheckResult.size() > 1) { QString msg = i18n("The consistency check has found no issues in your data. Details are presented below."); if (m_consistencyCheckResult.size() > 1) msg = i18n("The consistency check has found some issues in your data. Details are presented below. Those issues that could not be corrected automatically need to be solved by the user."); // install a context menu for the list after the dialog is displayed QTimer::singleShot(500, q, SLOT(slotInstallConsistencyCheckContextMenu())); KMessageBox::informationList(0, msg, m_consistencyCheckResult, i18n("Consistency check result")); } // this data is no longer needed m_consistencyCheckResult.clear(); } void KMyMoneyApp::Private::copyConsistencyCheckResults() { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(m_consistencyCheckResult.join(QLatin1String("\n"))); } void KMyMoneyApp::Private::saveConsistencyCheckResults() { QUrl fileUrl = QFileDialog::getSaveFileUrl(q); if (!fileUrl.isEmpty()) { QFile file(fileUrl.toLocalFile()); if (file.open(QFile::WriteOnly | QFile::Append | QFile::Text)) { QTextStream out(&file); out << m_consistencyCheckResult.join(QLatin1String("\n")); file.close(); } } } void KMyMoneyApp::Private::setThemedCSS() { const QStringList CSSnames {QStringLiteral("kmymoney.css"), QStringLiteral("welcome.css")}; const QString rcDir("/html/"); const QStringList defaultCSSDirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, rcDir, QStandardPaths::LocateDirectory); // scan the list of directories to find the ones that really // contains all files we look for QString defaultCSSDir; foreach (const auto dir, defaultCSSDirs) { defaultCSSDir = dir; foreach (const auto CSSname, CSSnames) { QFileInfo fileInfo(defaultCSSDir + CSSname); if (!fileInfo.exists()) { defaultCSSDir.clear(); break; } } if (!defaultCSSDir.isEmpty()) { break; } } // make sure we have the local directory where the themed version is stored const QString themedCSSDir = QStandardPaths::standardLocations(QStandardPaths::AppConfigLocation).first() + rcDir; QDir().mkpath(themedCSSDir); foreach (const auto CSSname, CSSnames) { const QString defaultCSSFilename = defaultCSSDir + CSSname; QFileInfo fileInfo(defaultCSSFilename); if (fileInfo.exists()) { const QString themedCSSFilename = themedCSSDir + CSSname; QFile::remove(themedCSSFilename); if (QFile::copy(defaultCSSFilename, themedCSSFilename)) { QFile cssFile (themedCSSFilename); if (cssFile.open(QIODevice::ReadWrite)) { QTextStream cssStream(&cssFile); auto cssText = cssStream.readAll(); cssText.replace(QLatin1String("./"), defaultCSSDir, Qt::CaseSensitive); cssText.replace(QLatin1String("WindowText"), KMyMoneyGlobalSettings::schemeColor(SchemeColor::WindowText).name(), Qt::CaseSensitive); cssText.replace(QLatin1String("Window"), KMyMoneyGlobalSettings::schemeColor(SchemeColor::WindowBackground).name(), Qt::CaseSensitive); cssText.replace(QLatin1String("HighlightText"), KMyMoneyGlobalSettings::schemeColor(SchemeColor::ListHighlightText).name(), Qt::CaseSensitive); cssText.replace(QLatin1String("Highlight"), KMyMoneyGlobalSettings::schemeColor(SchemeColor::ListHighlight).name(), Qt::CaseSensitive); cssText.replace(QLatin1String("black"), KMyMoneyGlobalSettings::schemeColor(SchemeColor::ListGrid).name(), Qt::CaseSensitive); cssStream.seek(0); cssStream << cssText; cssFile.close(); } } } } } void KMyMoneyApp::slotCheckSchedules() { if (KMyMoneyGlobalSettings::checkSchedule() == true) { KMSTATUS(i18n("Checking for overdue scheduled transactions...")); MyMoneyFile *file = MyMoneyFile::instance(); QDate checkDate = QDate::currentDate().addDays(KMyMoneyGlobalSettings::checkSchedulePreview()); QList scheduleList = file->scheduleList(); QList::Iterator it; eDialogs::ScheduleResultCode rc = eDialogs::ScheduleResultCode::Enter; for (it = scheduleList.begin(); (it != scheduleList.end()) && (rc != eDialogs::ScheduleResultCode::Cancel); ++it) { // Get the copy in the file because it might be modified by commitTransaction MyMoneySchedule schedule = file->schedule((*it).id()); if (schedule.autoEnter()) { try { while (!schedule.isFinished() && (schedule.adjustedNextDueDate() <= checkDate) && rc != eDialogs::ScheduleResultCode::Ignore && rc != eDialogs::ScheduleResultCode::Cancel) { rc = enterSchedule(schedule, true, true); schedule = file->schedule((*it).id()); // get a copy of the modified schedule } } catch (const MyMoneyException &) { } } if (rc == eDialogs::ScheduleResultCode::Ignore) { // if the current schedule was ignored then we must make sure that the user can still enter the next scheduled transaction rc = eDialogs::ScheduleResultCode::Enter; } } updateCaption(); } } void KMyMoneyApp::writeLastUsedDir(const QString& directory) { //get global config object for our app. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); if (kconfig) { KConfigGroup grp = kconfig->group("General Options"); //write path entry, no error handling since its void. grp.writeEntry("LastUsedDirectory", directory); } } void KMyMoneyApp::writeLastUsedFile(const QString& fileName) { //get global config object for our app. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); if (kconfig) { KConfigGroup grp = d->m_config->group("General Options"); // write path entry, no error handling since its void. // use a standard string, as fileName could contain a protocol // e.g. file:/home/thb/.... grp.writeEntry("LastUsedFile", fileName); } } QString KMyMoneyApp::readLastUsedDir() const { QString str; //get global config object for our app. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); if (kconfig) { KConfigGroup grp = d->m_config->group("General Options"); //read path entry. Second parameter is the default if the setting is not found, which will be the default document path. str = grp.readEntry("LastUsedDirectory", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); // if the path stored is empty, we use the default nevertheless if (str.isEmpty()) str = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); } return str; } QString KMyMoneyApp::readLastUsedFile() const { QString str; // get global config object for our app. KSharedConfigPtr kconfig = KSharedConfig::openConfig(); if (kconfig) { KConfigGroup grp = d->m_config->group("General Options"); // read filename entry. str = grp.readEntry("LastUsedFile", ""); } return str; } QString KMyMoneyApp::filename() const { return d->m_fileName.url(); } WebConnect* KMyMoneyApp::webConnect() const { return d->m_webConnect; } QList KMyMoneyApp::instanceList() const { QList list; #ifdef KMM_DBUS QDBusReply reply = QDBusConnection::sessionBus().interface()->registeredServiceNames(); if (reply.isValid()) { QStringList apps = reply.value(); QStringList::ConstIterator it; // build a list of service names of all running kmymoney applications without this one for (it = apps.constBegin(); it != apps.constEnd(); ++it) { // please change this method of creating a list of 'all the other kmymoney instances that are running on the system' // since assuming that D-Bus creates service names with org.kde.kmymoney-PID is an observation I don't think that it's documented somwhere if ((*it).indexOf("org.kde.kmymoney-") == 0) { uint thisProcPid = platformTools::processId(); if ((*it).indexOf(QString("org.kde.kmymoney-%1").arg(thisProcPid)) != 0) list += (*it); } } } else { qDebug("D-Bus returned the following error while obtaining instances: %s", qPrintable(reply.error().message())); } #endif return list; } void KMyMoneyApp::slotEquityPriceUpdate() { QPointer dlg = new KEquityPriceUpdateDlg(this); if (dlg->exec() == QDialog::Accepted && dlg != 0) dlg->storePrices(); delete dlg; } void KMyMoneyApp::webConnect(const QString& sourceUrl, const QByteArray& asn_id) { // // Web connect attempts to go through the known importers and see if the file // can be importing using that method. If so, it will import it using that // plugin // Q_UNUSED(asn_id) d->m_importUrlsQueue.enqueue(sourceUrl); // only start processing if this is the only import so far if (d->m_importUrlsQueue.count() == 1) { while (!d->m_importUrlsQueue.isEmpty()) { // get the value of the next item from the queue // but leave it on the queue for now QString url = d->m_importUrlsQueue.head(); // Bring this window to the forefront. This method was suggested by // Lubos Lunak of the KDE core development team. // TODO: port KF5 (WebConnect) //KStartupInfo::setNewStartupId(this, asn_id); // Make sure we have an open file if (! d->m_myMoneyView->fileOpen() && KMessageBox::warningContinueCancel(kmymoney, i18n("You must first select a KMyMoney file before you can import a statement.")) == KMessageBox::Continue) kmymoney->slotFileOpen(); // only continue if the user really did open a file. if (d->m_myMoneyView->fileOpen()) { KMSTATUS(i18n("Importing a statement via Web Connect")); // remove the statement files d->unlinkStatementXML(); QMap::const_iterator it_plugin = d->m_importerPlugins.constBegin(); while (it_plugin != d->m_importerPlugins.constEnd()) { if ((*it_plugin)->isMyFormat(url)) { QList statements; if (!(*it_plugin)->import(url)) { KMessageBox::error(this, i18n("Unable to import %1 using %2 plugin. The plugin returned the following error: %3", url, (*it_plugin)->formatName(), (*it_plugin)->lastError()), i18n("Importing error")); } break; } ++it_plugin; } // If we did not find a match, try importing it as a KMM statement file, // which is really just for testing. the statement file is not exposed // to users. if (it_plugin == d->m_importerPlugins.constEnd()) if (MyMoneyStatement::isStatementFile(url)) slotStatementImport(url); } // remove the current processed item from the queue d->m_importUrlsQueue.dequeue(); } } } void KMyMoneyApp::slotEnableMessages() { KMessageBox::enableAllMessages(); KMessageBox::information(this, i18n("All messages have been enabled."), i18n("All messages")); } void KMyMoneyApp::createInterfaces() { // Sets up the plugin interface KMyMoneyPlugin::pluginInterfaces().importInterface = new KMyMoneyPlugin::KMMImportInterface(this, this); KMyMoneyPlugin::pluginInterfaces().statementInterface = new KMyMoneyPlugin::KMMStatementInterface(this, this); KMyMoneyPlugin::pluginInterfaces().viewInterface = new KMyMoneyPlugin::KMMViewInterface(this, d->m_myMoneyView, this); // setup the calendar interface for schedules MyMoneySchedule::setProcessingCalendar(this); } void KMyMoneyApp::loadPlugins() { Q_ASSERT(!d->m_pluginLoader); d->m_pluginLoader = new KMyMoneyPlugin::PluginLoader(this); //! @todo Junior Job: Improve the config read system KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup group{ config->group("Plugins") }; const auto plugins = KPluginLoader::findPlugins("kmymoney"); d->m_pluginLoader->addPluginInfo(plugins); for (const KPluginMetaData & pluginData : plugins) { // Only load plugins which are enabled and have the right serviceType. Other serviceTypes are loaded on demand. if (isPluginEnabled(pluginData, group)) slotPluginLoad(pluginData); } connect(d->m_pluginLoader, &KMyMoneyPlugin::PluginLoader::pluginEnabled, this, &KMyMoneyApp::slotPluginLoad); connect(d->m_pluginLoader, &KMyMoneyPlugin::PluginLoader::pluginDisabled, this, &KMyMoneyApp::slotPluginUnload); } void KMyMoneyApp::unloadPlugins() { Q_ASSERT(d->m_pluginLoader); delete d->m_pluginLoader; } inline bool KMyMoneyApp::isPluginEnabled(const KPluginMetaData& metaData, const KConfigGroup& configGroup) { //! @fixme: there is a function in KMyMoneyPlugin::PluginLoader which has to have the same content if (metaData.serviceTypes().contains("KMyMoney/Plugin")) { const QString keyName{metaData.name() + "Enabled"}; if (configGroup.hasKey(keyName)) return configGroup.readEntry(keyName, true); return metaData.isEnabledByDefault(); } return false; } void KMyMoneyApp::slotPluginLoad(const KPluginMetaData& metaData) { std::unique_ptr loader{new QPluginLoader{metaData.fileName()}}; QObject* plugin = loader->instance(); if (!plugin) { qWarning("Could not load plugin '%s', error: %s", qPrintable(metaData.fileName()), qPrintable(loader->errorString())); return; } if ( d->m_plugins.contains(metaData.fileName()) ) { /** @fixme Handle a reload e.g. objectNames are equal but the object are different (plugin != d->m_plugins[plugin->objectName()]) * Also it could be usefull to drop the dependence on objectName() */ /* Note: there is nothing to delete here because if the plugin was loaded already, plugin points to the same object as the previously loaded one. */ return; } KMyMoneyPlugin::Plugin* kmmPlugin = qobject_cast(plugin); if (!kmmPlugin) { qWarning("Could not load plugin '%s'.", qPrintable(metaData.fileName())); return; } // check for online plugin KMyMoneyPlugin::OnlinePlugin* op = dynamic_cast(plugin); // check for extended online plugin KMyMoneyPlugin::OnlinePluginExtended* ope = dynamic_cast(plugin); // check for importer plugin KMyMoneyPlugin::ImporterPlugin* ip = dynamic_cast(plugin); // Tell the plugin it is about to get plugged kmmPlugin->plug(); // plug the plugin guiFactory()->addClient(kmmPlugin); d->m_plugins[metaData.fileName()] = kmmPlugin; if (op) d->m_onlinePlugins[plugin->objectName()] = op; if (ope) onlineJobAdministration::instance()->addPlugin(plugin->objectName(), ope); if (ip) d->m_importerPlugins[plugin->objectName()] = ip; slotUpdateActions(); } void KMyMoneyApp::slotPluginUnload(const KPluginMetaData& metaData) { KMyMoneyPlugin::Plugin* plugin = d->m_plugins[metaData.fileName()]; // Remove and test if the plugin was actually loaded if (!d->m_plugins.remove(metaData.fileName()) || plugin == nullptr) return; // check for online plugin KMyMoneyPlugin::OnlinePlugin* op = dynamic_cast(plugin); // check for importer plugin KMyMoneyPlugin::ImporterPlugin* ip = dynamic_cast(plugin); // unplug the plugin guiFactory()->removeClient(plugin); if (op) d->m_onlinePlugins.remove(plugin->objectName()); if (ip) d->m_importerPlugins.remove(plugin->objectName()); plugin->unplug(); slotUpdateActions(); } void KMyMoneyApp::slotAutoSave() { if (!d->m_inAutoSaving) { // store the focus widget so we can restore it after save QPointer focusWidget = qApp->focusWidget(); d->m_inAutoSaving = true; KMSTATUS(i18n("Auto saving...")); //calls slotFileSave if needed, and restart the timer //it the file is not saved, reinitializes the countdown. if (d->m_myMoneyView->dirty() && d->m_autoSaveEnabled) { if (!slotFileSave() && d->m_autoSavePeriod > 0) { d->m_autoSaveTimer->setSingleShot(true); d->m_autoSaveTimer->start(d->m_autoSavePeriod * 60 * 1000); } } d->m_inAutoSaving = false; if (focusWidget && focusWidget != qApp->focusWidget()) { // we have a valid focus widget so restore it focusWidget->setFocus(); } } } void KMyMoneyApp::slotDateChanged() { QDateTime dt = QDateTime::currentDateTime(); QDateTime nextDay(QDate(dt.date().addDays(1)), QTime(0, 0, 0)); // +1 is to make sure that we're already in the next day when the // signal is sent (this way we also avoid setting the timer to 0) QTimer::singleShot((dt.secsTo(nextDay) + 1)*1000, this, SLOT(slotDateChanged())); d->m_myMoneyView->slotRefreshViews(); } const MyMoneyAccount& KMyMoneyApp::account(const QString& key, const QString& value) const { QList list; QList::const_iterator it_a; MyMoneyFile::instance()->accountList(list); QString accId; for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) { // search in the account's kvp container const QString& accountKvpValue = (*it_a).value(key); // search in the account's online settings kvp container const QString& onlineSettingsKvpValue = (*it_a).onlineBankingSettings().value(key); if (accountKvpValue.contains(value) || onlineSettingsKvpValue.contains(value)) { if(accId.isEmpty()) { accId = (*it_a).id(); } } if (accountKvpValue == value || onlineSettingsKvpValue == value) { accId = (*it_a).id(); break; } } // return the account found or an empty element return MyMoneyFile::instance()->account(accId); } void KMyMoneyApp::setAccountOnlineParameters(const MyMoneyAccount& _acc, const MyMoneyKeyValueContainer& kvps) { MyMoneyFileTransaction ft; try { MyMoneyAccount acc = MyMoneyFile::instance()->account(_acc.id()); acc.setOnlineBankingSettings(kvps); MyMoneyFile::instance()->modifyAccount(acc); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to setup online parameters for account '%1'", _acc.name()), e.what()); } } void KMyMoneyApp::slotAccountUnmapOnline() { // no account selected if (d->m_selectedAccount.id().isEmpty()) return; // not a mapped account if (d->m_selectedAccount.onlineBankingSettings().value("provider").isEmpty()) return; if (KMessageBox::warningYesNo(this, QString("%1").arg(i18n("Do you really want to remove the mapping of account %1 to an online account? Depending on the details of the online banking method used, this action cannot be reverted.", d->m_selectedAccount.name())), i18n("Remove mapping to online account")) == KMessageBox::Yes) { MyMoneyFileTransaction ft; try { d->m_selectedAccount.setOnlineBankingSettings(MyMoneyKeyValueContainer()); // delete the kvp that is used in MyMoneyStatementReader too // we should really get rid of it, but since I don't know what it // is good for, I'll keep it around. (ipwizard) d->m_selectedAccount.deletePair("StatementKey"); MyMoneyFile::instance()->modifyAccount(d->m_selectedAccount); ft.commit(); // The mapping could disable the online task system onlineJobAdministration::instance()->updateOnlineTaskProperties(); } catch (const MyMoneyException &e) { KMessageBox::error(this, i18n("Unable to unmap account from online account: %1", e.what())); } } } void KMyMoneyApp::slotAccountMapOnline() { // no account selected if (d->m_selectedAccount.id().isEmpty()) return; // already an account mapped if (!d->m_selectedAccount.onlineBankingSettings().value("provider").isEmpty()) return; // check if user tries to map a brokerageAccount if (d->m_selectedAccount.name().contains(i18n(" (Brokerage)"))) { if (KMessageBox::warningContinueCancel(this, i18n("You try to map a brokerage account to an online account. This is usually not advisable. In general, the investment account should be mapped to the online account. Please cancel if you intended to map the investment account, continue otherwise"), i18n("Mapping brokerage account")) == KMessageBox::Cancel) { return; } } // if we have more than one provider let the user select the current provider QString provider; QMap::const_iterator it_p; switch (d->m_onlinePlugins.count()) { case 0: break; case 1: provider = d->m_onlinePlugins.begin().key(); break; default: { QMenu popup(this); popup.setTitle(i18n("Select online banking plugin")); // Populate the pick list with all the provider for (it_p = d->m_onlinePlugins.constBegin(); it_p != d->m_onlinePlugins.constEnd(); ++it_p) { popup.addAction(it_p.key())->setData(it_p.key()); } QAction *item = popup.actions()[0]; if (item) { popup.setActiveAction(item); } // cancelled if ((item = popup.exec(QCursor::pos(), item)) == 0) { return; } provider = item->data().toString(); } break; } if (provider.isEmpty()) return; // find the provider it_p = d->m_onlinePlugins.constFind(provider); if (it_p != d->m_onlinePlugins.constEnd()) { // plugin found, call it MyMoneyKeyValueContainer settings; if ((*it_p)->mapAccount(d->m_selectedAccount, settings)) { settings["provider"] = provider; MyMoneyAccount acc(d->m_selectedAccount); acc.setOnlineBankingSettings(settings); MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->modifyAccount(acc); ft.commit(); // The mapping could enable the online task system onlineJobAdministration::instance()->updateOnlineTaskProperties(); } catch (const MyMoneyException &e) { KMessageBox::error(this, i18n("Unable to map account to online account: %1", e.what())); } } } } void KMyMoneyApp::slotAccountUpdateOnlineAll() { QList accList; MyMoneyFile::instance()->accountList(accList); QList::iterator it_a; QMap::const_iterator it_p; d->m_statementResults.clear(); d->m_collectingStatements = true; // remove all those from the list, that don't have a 'provider' or the // provider is not currently present for (it_a = accList.begin(); it_a != accList.end();) { if ((*it_a).onlineBankingSettings().value("provider").isEmpty() || d->m_onlinePlugins.find((*it_a).onlineBankingSettings().value("provider")) == d->m_onlinePlugins.end()) { it_a = accList.erase(it_a); } else ++it_a; } QVector disabledActions {Action::AccountUpdate, Action::AccountUpdateMenu, Action::AccountUpdateAll}; foreach (const auto a, disabledActions) actionCollection()->action(s_Actions.value(a))->setEnabled(false); // now work on the remaining list of accounts int cnt = accList.count() - 1; for (it_a = accList.begin(); it_a != accList.end(); ++it_a) { it_p = d->m_onlinePlugins.constFind((*it_a).onlineBankingSettings().value("provider")); (*it_p)->updateAccount(*it_a, cnt != 0); --cnt; } d->m_collectingStatements = false; if (!d->m_statementResults.isEmpty()) KMessageBox::informationList(this, i18n("The statements have been processed with the following results:"), d->m_statementResults, i18n("Statement stats")); // re-enable the disabled actions slotUpdateActions(); } void KMyMoneyApp::slotAccountUpdateOnline() { // no account selected if (d->m_selectedAccount.id().isEmpty()) return; // no online account mapped if (d->m_selectedAccount.onlineBankingSettings().value("provider").isEmpty()) return; QVector disabledActions {Action::AccountUpdate, Action::AccountUpdateMenu, Action::AccountUpdateAll}; foreach (const auto a, disabledActions) actionCollection()->action(s_Actions.value(a))->setEnabled(false); // find the provider QMap::const_iterator it_p; it_p = d->m_onlinePlugins.constFind(d->m_selectedAccount.onlineBankingSettings().value("provider")); if (it_p != d->m_onlinePlugins.constEnd()) { // plugin found, call it d->m_collectingStatements = true; d->m_statementResults.clear(); (*it_p)->updateAccount(d->m_selectedAccount); d->m_collectingStatements = false; if (!d->m_statementResults.isEmpty()) KMessageBox::informationList(this, i18n("The statements have been processed with the following results:"), d->m_statementResults, i18n("Statement stats")); } // re-enable the disabled actions slotUpdateActions(); } void KMyMoneyApp::slotNewOnlineTransfer() { kOnlineTransferForm *transferForm = new kOnlineTransferForm(this); if (!d->m_selectedAccount.id().isEmpty()) { transferForm->setCurrentAccount(d->m_selectedAccount.id()); } connect(transferForm, SIGNAL(rejected()), transferForm, SLOT(deleteLater())); connect(transferForm, SIGNAL(acceptedForSave(onlineJob)), this, SLOT(slotOnlineJobSave(onlineJob))); connect(transferForm, SIGNAL(acceptedForSend(onlineJob)), this, SLOT(slotOnlineJobSend(onlineJob))); connect(transferForm, SIGNAL(accepted()), transferForm, SLOT(deleteLater())); transferForm->show(); } void KMyMoneyApp::slotEditOnlineJob(const QString jobId) { try { const onlineJob constJob = MyMoneyFile::instance()->getOnlineJob(jobId); slotEditOnlineJob(constJob); } catch (MyMoneyException&) { // Prevent a crash in very rare cases } } void KMyMoneyApp::slotEditOnlineJob(onlineJob job) { try { slotEditOnlineJob(onlineJobTyped(job)); } catch (MyMoneyException&) { } } void KMyMoneyApp::slotEditOnlineJob(const onlineJobTyped job) { kOnlineTransferForm *transferForm = new kOnlineTransferForm(this); transferForm->setOnlineJob(job); connect(transferForm, SIGNAL(rejected()), transferForm, SLOT(deleteLater())); connect(transferForm, SIGNAL(acceptedForSave(onlineJob)), this, SLOT(slotOnlineJobSave(onlineJob))); connect(transferForm, SIGNAL(acceptedForSend(onlineJob)), this, SLOT(slotOnlineJobSend(onlineJob))); connect(transferForm, SIGNAL(accepted()), transferForm, SLOT(deleteLater())); transferForm->show(); } void KMyMoneyApp::slotOnlineJobSave(onlineJob job) { MyMoneyFileTransaction fileTransaction; if (job.id() == MyMoneyObject::emptyId()) MyMoneyFile::instance()->addOnlineJob(job); else MyMoneyFile::instance()->modifyOnlineJob(job); fileTransaction.commit(); } /** @todo when onlineJob queue is used, continue here */ void KMyMoneyApp::slotOnlineJobSend(onlineJob job) { MyMoneyFileTransaction fileTransaction; if (job.id() == MyMoneyObject::emptyId()) MyMoneyFile::instance()->addOnlineJob(job); else MyMoneyFile::instance()->modifyOnlineJob(job); fileTransaction.commit(); QList jobList; jobList.append(job); slotOnlineJobSend(jobList); } void KMyMoneyApp::slotOnlineJobSend(QList jobs) { MyMoneyFile *const kmmFile = MyMoneyFile::instance(); QMultiMap jobsByPlugin; // Sort jobs by online plugin & lock them foreach (onlineJob job, jobs) { Q_ASSERT(job.id() != MyMoneyObject::emptyId()); // find the provider const MyMoneyAccount originAcc = job.responsibleMyMoneyAccount(); job.setLock(); job.addJobMessage(onlineJobMessage(onlineJobMessage::debug, "KMyMoneyApp::slotOnlineJobSend", "Added to queue for plugin '" + originAcc.onlineBankingSettings().value("provider") + '\'')); MyMoneyFileTransaction fileTransaction; kmmFile->modifyOnlineJob(job); fileTransaction.commit(); jobsByPlugin.insert(originAcc.onlineBankingSettings().value("provider"), job); } // Send onlineJobs to plugins QList usedPlugins = jobsByPlugin.keys(); std::sort(usedPlugins.begin(), usedPlugins.end()); const QList::iterator newEnd = std::unique(usedPlugins.begin(), usedPlugins.end()); usedPlugins.erase(newEnd, usedPlugins.end()); foreach (const QString& pluginKey, usedPlugins) { QMap::const_iterator it_p = d->m_onlinePlugins.constFind(pluginKey); if (it_p != d->m_onlinePlugins.constEnd()) { // plugin found, call it KMyMoneyPlugin::OnlinePluginExtended *pluginExt = dynamic_cast< KMyMoneyPlugin::OnlinePluginExtended* >(*it_p); if (pluginExt == 0) { qWarning("Job given for plugin which is not an extended plugin"); continue; } //! @fixme remove debug message qDebug() << "Sending " << jobsByPlugin.count(pluginKey) << " job(s) to online plugin " << pluginKey; QList jobsToExecute = jobsByPlugin.values(pluginKey); QList executedJobs = jobsToExecute; pluginExt->sendOnlineJob(executedJobs); // Save possible changes of the online job and remove lock MyMoneyFileTransaction fileTransaction; foreach (onlineJob job, executedJobs) { fileTransaction.restart(); job.setLock(false); kmmFile->modifyOnlineJob(job); fileTransaction.commit(); } if (Q_UNLIKELY(executedJobs.size() != jobsToExecute.size())) { // OnlinePlugin did not return all jobs qWarning() << "Error saving send online tasks. After restart you should see at minimum all successfully executed jobs marked send. Imperfect plugin: " << pluginExt->objectName(); } } else { qWarning() << "Error, got onlineJob for an account without online plugin."; /** @FIXME can this actually happen? */ } } } void KMyMoneyApp::slotRemoveJob() { } void KMyMoneyApp::slotEditJob() { } void KMyMoneyApp::slotOnlineJobLog() { QStringList jobIds = d->m_myMoneyView->getOnlineJobOutbox()->selectedOnlineJobs(); slotOnlineJobLog(jobIds); } void KMyMoneyApp::slotOnlineJobLog(const QStringList& onlineJobIds) { onlineJobMessagesView *const dialog = new onlineJobMessagesView(); onlineJobMessagesModel *const model = new onlineJobMessagesModel(dialog); model->setOnlineJob(MyMoneyFile::instance()->getOnlineJob(onlineJobIds.first())); dialog->setModel(model); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); // Note: Objects are not deleted here, Qt's parent-child system has to do that. } void KMyMoneyApp::setHolidayRegion(const QString& holidayRegion) { #ifdef KF5Holidays_FOUND //since the cost of updating the cache is now not negligible //check whether the region has been modified if (!d->m_holidayRegion || d->m_holidayRegion->regionCode() != holidayRegion) { // Delete the previous holidayRegion before creating a new one. delete d->m_holidayRegion; // Create a new holidayRegion. d->m_holidayRegion = new KHolidays::HolidayRegion(holidayRegion); //clear and update the holiday cache preloadHolidays(); } #else Q_UNUSED(holidayRegion); #endif } bool KMyMoneyApp::isProcessingDate(const QDate& date) const { #ifdef KF5Holidays_FOUND if (!d->m_processingDays.testBit(date.dayOfWeek())) return false; if (!d->m_holidayRegion || !d->m_holidayRegion->isValid()) return true; //check first whether it's already in cache if (d->m_holidayMap.contains(date)) { return d->m_holidayMap.value(date, true); } else { bool processingDay = !d->m_holidayRegion->isHoliday(date); d->m_holidayMap.insert(date, processingDay); return processingDay; } #else Q_UNUSED(date); return true; #endif } void KMyMoneyApp::preloadHolidays() { #ifdef KF5Holidays_FOUND //clear the cache before loading d->m_holidayMap.clear(); //only do this if it is a valid region if (d->m_holidayRegion && d->m_holidayRegion->isValid()) { //load holidays for the forecast days plus 1 cycle, to be on the safe side int forecastDays = KMyMoneyGlobalSettings::forecastDays() + KMyMoneyGlobalSettings::forecastAccountCycle(); QDate endDate = QDate::currentDate().addDays(forecastDays); //look for holidays for the next 2 years as a minimum. That should give a good margin for the cache if (endDate < QDate::currentDate().addYears(2)) endDate = QDate::currentDate().addYears(2); KHolidays::Holiday::List holidayList = d->m_holidayRegion->holidays(QDate::currentDate(), endDate); KHolidays::Holiday::List::const_iterator holiday_it; for (holiday_it = holidayList.constBegin(); holiday_it != holidayList.constEnd(); ++holiday_it) { for (QDate holidayDate = (*holiday_it).observedStartDate(); holidayDate <= (*holiday_it).observedEndDate(); holidayDate = holidayDate.addDays(1)) d->m_holidayMap.insert(holidayDate, false); } for (QDate date = QDate::currentDate(); date <= endDate; date = date.addDays(1)) { //if it is not a processing day, set it to false if (!d->m_processingDays.testBit(date.dayOfWeek())) { d->m_holidayMap.insert(date, false); } else if (!d->m_holidayMap.contains(date)) { //if it is not a holiday nor a weekend, it is a processing day d->m_holidayMap.insert(date, true); } } } #endif } KMStatus::KMStatus(const QString &text) { m_prevText = kmymoney->slotStatusMsg(text); } KMStatus::~KMStatus() { kmymoney->slotStatusMsg(m_prevText); } void KMyMoneyApp::Private::unlinkStatementXML() { QDir d(KMyMoneySettings::logPath(), "kmm-statement*"); for (uint i = 0; i < d.count(); ++i) { qDebug("Remove %s", qPrintable(d[i])); d.remove(KMyMoneySettings::logPath() + QString("/%1").arg(d[i])); } m_statementXMLindex = 0; } void KMyMoneyApp::Private::closeFile() { q->slotSelectAccount(MyMoneyAccount()); q->slotSelectInstitution(MyMoneyInstitution()); q->slotSelectInvestment(MyMoneyAccount()); q->slotSelectSchedule(); q->slotSelectCurrency(); q->slotSelectBudget(QList()); q->slotSelectPayees(QList()); q->slotSelectTags(QList()); q->slotSelectTransactions(KMyMoneyRegister::SelectedTransactions()); m_reconciliationAccount = MyMoneyAccount(); m_myMoneyView->finishReconciliation(m_reconciliationAccount); m_myMoneyView->closeFile(); m_fileName = QUrl(); q->updateCaption(); // just create a new balance warning object delete m_balanceWarning; m_balanceWarning = new KBalanceWarning(q); emit q->fileLoaded(m_fileName); } diff --git a/kmymoney/kmymoney.h b/kmymoney/kmymoney.h index 04efb8400..6232bd60f 100644 --- a/kmymoney/kmymoney.h +++ b/kmymoney/kmymoney.h @@ -1,1475 +1,1473 @@ /*************************************************************************** kmymoney.h ------------------- copyright : (C) 2000-2001 by Michael Edwardes ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEY_H #define KMYMONEY_H // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include #include "kmymoneyutils.h" #include "mymoneyaccount.h" #include "mymoney/onlinejob.h" #include "onlinejobtyped.h" #include "mymoneykeyvaluecontainer.h" #include "mymoneymoney.h" -#include "selectedtransactions.h" -#include "mymoneysplit.h" -#include "mymoneytransaction.h" +#include "selectedtransaction.h" class QResizeEvent; class KPluginMetaData; class MyMoneyObject; class MyMoneyInstitution; class MyMoneyAccount; class MyMoneySecurity; class MyMoneyBudget; class MyMoneyPayee; class MyMoneyPrice; class MyMoneyStatement; class MyMoneyTag; class MyMoneySplit; class MyMoneyTransaction; class WebConnect; class creditTransfer; template class onlineJobTyped; enum class Action { FileOpenDatabase, FileSaveAsDatabase, FileBackup, FileImportGNC, FileImportStatement, FileImportTemplate, FileExportTemplate, #ifdef KMM_DEBUG FileDump, #endif FilePersonalData, FileInformation, EditFindTransaction, ViewTransactionDetail, ViewHideReconciled, ViewHideCategories, ViewShowAll, InstitutionNew, InstitutionEdit, InstitutionDelete, AccountNew, AccountOpen, AccountStartReconciliation, AccountFinishReconciliation, AccountPostponeReconciliation, AccountEdit, AccountDelete, AccountClose, AccountReopen, AccountTransactionReport, AccountBalanceChart, AccountUpdateMenu, AccountOnlineMap, AccountOnlineUnmap, AccountUpdate, AccountUpdateAll, AccountCreditTransfer, CategoryNew, CategoryEdit, CategoryDelete, ToolCurrencies, ToolPrices, ToolUpdatePrices, ToolConsistency, ToolPerformance, ToolSQL, ToolCalculator, SettingsAllMessages, HelpShow, TransactionNew, TransactionEdit, TransactionEnter, TransactionEditSplits, TransactionCancel, TransactionDelete, TransactionDuplicate, TransactionMatch, TransactionAccept, TransactionToggleReconciled, TransactionToggleCleared, TransactionReconciled, TransactionNotReconciled, TransactionSelectAll, TransactionGoToAccount, TransactionGoToPayee, TransactionCreateSchedule, TransactionAssignNumber, TransactionCombine, TransactionCopySplits, TransactionMoveMenu, TransactionMarkMenu, TransactionContextMarkMenu, InvestmentNew, InvestmentEdit, InvestmentDelete, InvestmentOnlinePrice, InvestmentManualPrice, ScheduleNew, ScheduleEdit, ScheduleDelete, ScheduleDuplicate, ScheduleEnter, ScheduleSkip, PayeeNew, PayeeRename, PayeeDelete, PayeeMerge, TagNew, TagRename, TagDelete, BudgetNew, BudgetRename, BudgetDelete, BudgetCopy, BudgetChangeYear, BudgetForecast, CurrencyNew, CurrencyRename, CurrencyDelete, CurrencySetBase, PriceNew, PriceDelete, PriceUpdate, PriceEdit, #ifdef KMM_DEBUG WizardNewUser, DebugTraces, #endif DebugTimers, OnlineJobDelete, OnlineJobEdit, OnlineJobLog }; namespace eDialogs { enum class ScheduleResultCode; } inline uint qHash(const Action key, uint seed) { return ::qHash(static_cast(key), seed); } /*! \mainpage KMyMoney Main Page for API documentation. * * \section intro Introduction * * This is the API documentation for KMyMoney. It should be used as a reference * for KMyMoney developers and users who wish to see how KMyMoney works. This * documentation will be kept up-to-date as development progresses and should be * read for new features that have been developed in KMyMoney. */ /** * The base class for KMyMoney application windows. It sets up the main * window and reads the config file as well as providing a menubar, toolbar * and statusbar. * * @see KMyMoneyView * * @author Michael Edwardes 2000-2001 * @author Thomas Baumgart 2006-2008 * * @short Main application class. */ class KMyMoneyApp : public KXmlGuiWindow, public IMyMoneyProcessingCalendar { Q_OBJECT private slots: /** * Keep track of objects that are destroyed by external events */ void slotObjectDestroyed(QObject* o); /** * Add a context menu to the list used by KMessageBox::informationList to display the consistency check results. */ void slotInstallConsistencyCheckContextMenu(); /** * Handle the context menu of the list used by KMessageBox::informationList to display the consistency check results. */ void slotShowContextMenuForConsistencyCheck(const QPoint &); protected slots: void slotFileSaveAsFilterChanged(const QString& filter); /** * This slot is intended to be used as part of auto saving. This is used when the * QTimer emits the timeout signal and simply checks that the file is dirty (has * received modifications to its contents), and call the appropriate method to * save the file. Furthermore, re-starts the timer (possibly not needed). * @author mvillarino 2005 * @see KMyMoneyApp::slotDataChanged() */ void slotAutoSave(); /** * This slot re-enables all message for which the "Don't show again" * option had been selected. */ void slotEnableMessages(); /** * Called when the user asks for file information. */ void slotFileFileInfo(); /** * Called to run performance test. */ void slotPerformanceTest(); /** * Called to generate the sql to create kmymoney database tables etc. */ void slotGenerateSql(); #ifdef KMM_DEBUG /** * Debugging only: turn on/off traces */ void slotToggleTraces(); #endif /** * Debugging only: turn on/off timers */ void slotToggleTimers(); /** * Called when the user asks for the personal information. */ void slotFileViewPersonal(); /** * Opens a file selector dialog for the user to choose an existing OFX * file from the file system to be imported. This slot is expected to * be called from the UI. */ void slotGncImport(); /** * Open a dialog with a chart of the balance for the currently selected * account (m_selectedAccount). Return once the dialog is closed. Don't do * anything if no account is selected or charts are not available. */ void slotAccountChart(); /** * Opens a file selector dialog for the user to choose an existing KMM * statement file from the file system to be imported. This is for testing * only. KMM statement files are not designed to be exposed to the user. */ void slotStatementImport(); void slotLoadAccountTemplates(); void slotSaveAccountTemplates(); /** * Open up the application wide settings dialog. * * @see KSettingsDlg */ void slotSettings(); /** * Called to show credits window. */ void slotShowCredits(); /** * Called when the user wishes to backup the current file */ void slotBackupFile(); /** * Perform mount operation before making a backup of the current file */ void slotBackupMount(); /** * Perform the backup write operation */ bool slotBackupWriteFile(); /** * Perform unmount operation after making a backup of the current file */ void slotBackupUnmount(); /** * Finish backup of the current file */ void slotBackupFinish(); /** * Handle events on making a backup of the current file */ void slotBackupHandleEvents(); void slotShowTipOfTheDay(); void slotShowPreviousView(); void slotShowNextView(); /** * Brings up a dialog to let the user search for specific transaction(s). It then * opens a results window to display those transactions. */ void slotFindTransaction(); /** * Destroys a possibly open the search dialog */ void slotCloseSearchDialog(); /** * Brings up the new category editor and saves the information. * The dialog will be preset with the name. The parent defaults to * MyMoneyFile::expense() * * @param name Name of the account to be created. Could include a full hierarchy * @param id reference to storage which will receive the id after successful creation * * @note Typically, this slot can be connected to the * StdTransactionEditor::createCategory(const QString&, QString&) or * KMyMoneyCombo::createItem(const QString&, QString&) signal. */ void slotCategoryNew(const QString& name, QString& id); /** * Calls the print logic for the current view */ void slotPrintView(); /** * Performs online update for currently selected investment */ void slotOnlinePriceUpdate(); /** * Performs manual update for currently selected investment */ void slotManualPriceUpdate(); /** * Call this slot, if any configuration parameter has changed */ void slotUpdateConfiguration(); /** */ void slotPayeeDelete(); /** * Slot that merges two or more selected payess into a new payee */ void slotPayeeMerge(); /** */ void slotBudgetNew(); /** */ void slotBudgetDelete(); /** */ void slotBudgetCopy(); /** */ void slotBudgetChangeYear(); /** */ void slotBudgetForecast(); /** */ void slotCurrencyNew(); /** */ void slotCurrencyUpdate(const QString ¤cyId, const QString& currencyName, const QString& currencyTradingSymbol); /** */ void slotCurrencyDelete(); /** */ void slotCurrencySetBase(); /** * This slot is used to start new features during the development cycle */ void slotNewFeature(); /** */ void slotTransactionsNew(); /** */ void slotTransactionsEdit(); /** */ void slotTransactionsEditSplits(); /** */ void slotTransactionsDelete(); /** */ void slotTransactionsEnter(); /** */ void slotTransactionsCancel(); /** */ void slotTransactionsCancelOrEnter(bool& okToSelect); /** */ void slotTransactionDuplicate(); /** */ void slotToggleReconciliationFlag(); /** */ void slotMarkTransactionCleared(); /** */ void slotMarkTransactionReconciled(); /** */ void slotMarkTransactionNotReconciled(); /** */ void slotTransactionGotoAccount(); /** */ void slotTransactionGotoPayee(); /** */ void slotTransactionCreateSchedule(); /** */ void slotTransactionAssignNumber(); /** */ void slotTransactionCombine(); /** * This method takes the selected splits and checks that only one transaction (src) * has more than one split and all others have only a single one. It then copies the * splits of the @b src transaction to all others. */ void slotTransactionCopySplits(); /** * Accept the selected transactions that are marked as 'imported' and remove the flag */ void slotTransactionsAccept(); /** * This slot triggers an update of all views and restarts * a single shot timer to call itself again at beginning of * the next day. */ void slotDateChanged(); /** * This slot will be called when the engine data changed * and the application object needs to update its state. */ void slotDataChanged(); void slotMoveToAccount(const QString& id); void slotUpdateMoveToAccountMenu(); /** * This slot collects information for a new scheduled transaction * based on transaction @a t and @a occurrence and saves it in the engine. */ void slotScheduleNew(const MyMoneyTransaction& t, eMyMoney::Schedule::Occurrence occurrence = eMyMoney::Schedule::Occurrence::Monthly); /** */ void slotScheduleDuplicate(); void slotAccountMapOnline(); void slotAccountUnmapOnline(); void slotAccountUpdateOnline(); void slotAccountUpdateOnlineAll(); /** * @brief Start dialog for an online banking transfer */ void slotNewOnlineTransfer(); /** * @brief Start dialog to edit onlineJob if possible * @param onlineJob id to edit */ void slotEditOnlineJob(const QString); /** * @brief Start dialog to edit onlineJob if possible */ void slotEditOnlineJob(const onlineJob); /** * @brief Start dialog to edit onlineJob if possible */ void slotEditOnlineJob(const onlineJobTyped); /** * @brief Saves an online banking job */ void slotOnlineJobSave(onlineJob job); /** * @brief Queue an online banking job */ void slotOnlineJobSend(onlineJob job); /** * @brief Send a list of onlineJobs */ void slotOnlineJobSend(QList jobs); /** * dummy method needed just for initialization */ void slotRemoveJob(); void slotEditJob(); /** * @brief Show the log currently selected online job */ void slotOnlineJobLog(); void slotOnlineJobLog(const QStringList& onlineJobIds); void slotManageGpgKeys(); void slotKeySelected(int idx); void slotStatusProgressDone(); public: /** * This method checks if there is at least one asset or liability account * in the current storage object. If not, it starts the new account wizard. */ void createInitialAccount(); /** * This method returns the last URL used or an empty URL * depending on the option setting if the last file should * be opened during startup or the open file dialog should * be displayed. * * @return URL of last opened file or empty if the program * should start with the open file dialog */ QUrl lastOpenedURL(); /** * construtor of KMyMoneyApp, calls all init functions to create the application. */ explicit KMyMoneyApp(QWidget* parent = 0); /** * Destructor */ ~KMyMoneyApp(); static void progressCallback(int current, int total, const QString&); void writeLastUsedDir(const QString& directory); QString readLastUsedDir() const; void writeLastUsedFile(const QString& fileName); QString readLastUsedFile() const; /** * Returns whether there is an importer available that can handle this file */ bool isImportableFile(const QUrl &url); /** * This method is used to update the caption of the application window. * It sets the caption to "filename [modified] - KMyMoney". * * @param skipActions if true, the actions will not be updated. This * is usually onyl required by some early calls when * these widgets are not yet created (the default is false). */ void updateCaption(bool skipActions = false); /** * This method returns a list of all 'other' dcop registered kmymoney processes. * It's a subset of the return of DCOPclient()->registeredApplications(). * * @retval QStringList of process ids */ QList instanceList() const; #ifdef KMM_DEBUG /** * Dump a list of the names of all defined KActions to stdout. */ void dumpActions() const; #endif /** * Popup the context menu with the respective @p containerName. * Valid container names are defined in kmymoneyui.rc */ void showContextMenu(const QString& containerName); /** * This method opens the category editor with the data found in @a account. The * parent account is preset to @a parent but can be modified. If the user * acknowledges, the category is created. */ void createCategory(MyMoneyAccount& account, const MyMoneyAccount& parent); /** * This method returns the account for a given @a key - @a value pair. * If the account is not found in the list of accounts, MyMoneyAccount() * is returned. The @a key - @a value pair can be in the account's kvp * container or the account's online settings kvp container. */ const MyMoneyAccount& account(const QString& key, const QString& value) const; /** * This method set the online parameters stored in @a kvps with the * account referenced by @a acc. */ void setAccountOnlineParameters(const MyMoneyAccount& acc, const MyMoneyKeyValueContainer& kvps); QUrl selectFile(const QString& title, const QString& path, const QString& mask, QFileDialog::FileMode, QWidget *widget); void createAccount(MyMoneyAccount& newAccount, MyMoneyAccount& parentAccount, MyMoneyAccount& brokerageAccount, MyMoneyMoney openingBal); QString filename() const; /** * Checks if the file with the @a url already exists. If so, * the user is asked if he/she wants to override the file. * If the user's answer is negative, @p false will be returned. * @p true will be returned in all other cases. */ bool okToWriteFile(const QUrl &url); /** * Return pointer to the WebConnect object */ WebConnect* webConnect() const; protected: /** save general Options like all bar positions and status as well as the geometry and the recent file list to the configuration * file */ void saveOptions(); /** * Creates the interfaces necessary for the plugins to work. Therefore, * this method must be called prior to loadPlugins(). */ void createInterfaces(); /** * load all available plugins. Make sure you have called createInterfaces() * before you call this one. */ void loadPlugins(); /** * unload all available plugins. Make sure you have called loadPlugins() * before you call this one. */ void unloadPlugins(); /** * @brief Checks if the given plugin is loaded on start up * * This filters plugins which are loaded on demand only and deactivated plugins. * The configGroup must point to the correct group already. */ bool isPluginEnabled(const KPluginMetaData& metaData, const KConfigGroup& configGroup); /** * read general options again and initialize all variables like the recent file list */ void readOptions(); /** initializes the KActions of the application */ void initActions(); /** initializes the dynamic menus (account selectors) */ void initDynamicMenus(); /** * sets up the statusbar for the main window by initialzing a statuslabel. */ void initStatusBar(); /** * @brief Establish connections between actions and views * * Must be called after creation of actions and views. */ void connectActionsAndViews(); /** queryClose is called by KMainWindow on each closeEvent of a window. Against the * default implementation (only returns true), this calls saveModified() on the document object to ask if the document shall * be saved if Modified; on cancel the closeEvent is rejected. * The settings are saved using saveOptions() if we are about to close. * @see KMainWindow#queryClose * @see QWidget#closeEvent */ virtual bool queryClose(); void slotCheckSchedules(); virtual void resizeEvent(QResizeEvent*); void createSchedule(MyMoneySchedule newSchedule, MyMoneyAccount& newAccount); /** * This method checks, if an account can be closed or not. An account * can be closed if: * * - the balance is zero and * - all children are already closed and * - there is no unfinished schedule referencing the account * * @param acc reference to MyMoneyAccount object in question * @retval true account can be closed * @retval false account cannot be closed */ KMyMoneyUtils::CanCloseAccountCodeE canCloseAccount(const MyMoneyAccount& acc) const; /** * This method checks if an account can be closed and enables/disables * the close account action * If disabled, it sets a tooltip explaning why it cannot be closed * @brief enableCloseAccountAction * @param acc reference to MyMoneyAccount object in question */ void enableCloseAccountAction(const MyMoneyAccount& acc); /** * Check if a list contains a payee with a given id * * @param list const reference to value list * @param id const reference to id * * @retval true object has been found * @retval false object is not in list */ bool payeeInList(const QList& list, const QString& id) const; /** * Check if a list contains a tag with a given id * * @param list const reference to value list * @param id const reference to id * * @retval true object has been found * @retval false object is not in list */ bool tagInList(const QList& list, const QString& id) const; /** * Mark the selected transactions as provided by @a flag. If * flag is @a MyMoneySplit::Unknown, the future state depends * on the current stat of the split's flag accoring to the * following table: * * - NotReconciled --> Cleared * - Cleared --> Reconciled * - Reconciled --> NotReconciled */ void markTransaction(eMyMoney::Split::State flag); /** * This method allows to skip the next scheduled transaction of * the given schedule @a s. * */ void skipSchedule(MyMoneySchedule& s); /** * This method allows to enter the next scheduled transaction of * the given schedule @a s. In case @a extendedKeys is @a true, * the given schedule can also be skipped or ignored. * If @a autoEnter is @a true and the schedule does not contain * an estimated value, the schedule is entered as is without further * interaction with the user. In all other cases, the user will * be presented a dialog and allowed to adjust the values for this * instance of the schedule. * * The transaction will be created and entered into the ledger * and the schedule updated. */ eDialogs::ScheduleResultCode enterSchedule(MyMoneySchedule& s, bool autoEnter = false, bool extendedKeys = false); /** * Creates a new institution entry in the MyMoneyFile engine * * @param institution MyMoneyInstitution object containing the data of * the institution to be created. */ void createInstitution(MyMoneyInstitution& institution); /** * This method unmatches the currently selected transactions */ void transactionUnmatch(); /** * This method matches the currently selected transactions */ void transactionMatch(); /** * This method preloads the holidays for the duration of the default forecast period */ void preloadHolidays(); public slots: void slotFileInfoDialog(); /** */ void slotFileNew(); /** open a file and load it into the document*/ void slotFileOpen(); /** opens a file from the recent files menu */ void slotFileOpenRecent(const QUrl &url); /** open a SQL database */ void slotOpenDatabase(); /** * saves the current document. If it has no name yet, the user * will be queried for it. * * @retval false save operation failed * @retval true save operation was successful */ bool slotFileSave(); /** * ask the user for the filename and save the current document * * @retval false save operation failed * @retval true save operation was successful */ bool slotFileSaveAs(); /** * ask the user to select a database and save the current document * * @retval false save operation failed * @retval true save operation was successful */ bool saveAsDatabase(); void slotSaveAsDatabase(); /** asks for saving if the file is modified, then closes the actual file and window */ void slotFileCloseWindow(); /** asks for saving if the file is modified, then closes the actual file */ void slotFileClose(); /** * closes all open windows by calling close() on each memberList item * until the list is empty, then quits the application. * If queryClose() returns false because the user canceled the * saveModified() dialog, the closing breaks. */ void slotFileQuit(); void slotFileConsistencyCheck(); /** * fires up the price table editor */ void slotPriceDialog(); /** * fires up the currency table editor */ void slotCurrencyDialog(); /** * dummy method needed just for initialization */ void slotShowTransactionDetail(); /** * Toggles the hide reconciled transactions setting */ void slotHideReconciledTransactions(); /** * Toggles the hide unused categories setting */ void slotHideUnusedCategories(); /** * Toggles the show all accounts setting */ void slotShowAllAccounts(); /** * changes the statusbar contents for the standard label permanently, * used to indicate current actions. Returns the previous value for * 'stacked' usage. * * @param text the text that is displayed in the statusbar */ QString slotStatusMsg(const QString &text); /** * This method changes the progress bar in the status line according * to the parameters @p current and @p total. The following special * cases exist: * * - current = -1 and total = -1 will reset the progress bar * - current = ?? and total != 0 will setup the 100% mark to @p total * - current = xx and total == 0 will set the percentage * * @param current the current value with respect to the initialised * 100% mark * @param total the total value (100%) */ void slotStatusProgressBar(int current, int total = 0); /** * Called to update stock and currency prices from the user menu */ void slotEquityPriceUpdate(); /** * Imports a KMM statement into the engine, triggering the appropriate * UI to handle account matching, payee creation, and someday * payee and transaction matching. */ bool slotStatementImport(const MyMoneyStatement& s, bool silent = false); /** * Essentially similar to the above slot, except this will load the file * from disk first, given the URL. */ bool slotStatementImport(const QString& url); /** * This slot starts the reconciliation of the currently selected account */ void slotAccountReconcileStart(); /** * This slot finishes a previously started reconciliation */ void slotAccountReconcileFinish(); /** * This slot postpones a previously started reconciliations */ void slotAccountReconcilePostpone(); /** * This slot deletes the currently selected account if possible */ void slotAccountDelete(); /** * This slot opens the account editor to edit the currently * selected account if possible */ void slotAccountEdit(); /** * This slot opens the selected account in the ledger view */ void slotAccountOpenEmpty(); void slotAccountOpen(const MyMoneyObject&); /** * Preloads the input dialog with the data of the current * selected institution and brings up the input dialog * and saves the information entered. */ void slotInstitutionEditEmpty(); void slotInstitutionEdit(const MyMoneyObject &obj); /** * Deletes the current selected institution. */ void slotInstitutionDelete(); /** * This slot closes the currently selected account if possible */ void slotAccountClose(); /** * This slot re-openes the currently selected account if possible */ void slotAccountReopen(); /** * This slot reparents account @p src to be a child of account @p dest * * @param src account to be reparented * @param dest new parent */ void slotReparentAccount(const MyMoneyAccount& src, const MyMoneyAccount& dest); /** * This slot reparents account @p src to be a held at institution @p dest * * @param src account to be reparented * @param dest new parent institution */ void slotReparentAccount(const MyMoneyAccount& src, const MyMoneyInstitution& dest); /** * This slot creates a transaction report for the selected account * and opens it in the reports view. */ void slotAccountTransactionReport(); /** * This slot opens the account options menu at the current cursor * position. */ void slotShowAccountContextMenu(const MyMoneyObject&); /** * This slot opens the schedule options menu at the current cursor * position. */ void slotShowScheduleContextMenu(); /** * This slot opens the institution options menu at the current cursor * position. */ void slotShowInstitutionContextMenu(const MyMoneyObject&); /** * This slot opens the investment options menu at the current cursor * position. */ void slotShowInvestmentContextMenu(); /** * This slot opens the payee options menu at the current cursor * position. */ void slotShowPayeeContextMenu(); /** * This slot opens the tag options menu at the current cursor * position. */ void slotShowTagContextMenu(); /** * This slot opens the budget options menu at the current cursor * position. */ void slotShowBudgetContextMenu(); /** * This slot opens the transaction options menu at the current cursor * position. */ void slotShowTransactionContextMenu(); /** * This slot opens the currency options menu at the current cursor * position. */ void slotShowCurrencyContextMenu(); /** * This slot opens the price options menu at the current cursor * position. */ void slotShowPriceContextMenu(); /** * Open onlineJob options menu at current cursor position. */ void slotShowOnlineJobContextMenu(); /** * Brings up the new category editor and saves the information. */ void slotCategoryNew(); /** * Brings up the new category editor and saves the information. * The dialog will be preset with the name and parent account. * * @param account reference of category to be created. The @p name member * should be filled by the caller. The object will be filled * with additional information during the creation process * esp. the @p id member. * @param parent reference to parent account (defaults to none) */ void slotCategoryNew(MyMoneyAccount& account, const MyMoneyAccount& parent); void slotCategoryNew(MyMoneyAccount& account); /** * Create a new investment */ void slotInvestmentNew(); /** * Create a new investment in a given @p parent investment account */ void slotInvestmentNew(MyMoneyAccount& account, const MyMoneyAccount& parent); /** * This slot opens the investment editor to edit the currently * selected investment if possible */ void slotInvestmentEdit(); /** * Deletes the current selected investment. */ void slotInvestmentDelete(); /** */ void slotPayeeNew(const QString& newnameBase, QString& id); bool createPayeeNew(const QString& newnameBase, QString& id); void slotPayeeNew(); /** * This slot collects information for a new scheduled transaction * and saves it in the engine. @sa slotScheduleNew(const MyMoneyTransaction&) */ void slotScheduleNew(); /** * This slot allows to edit information the currently selected schedule */ void slotScheduleEdit(); /** * This slot allows to delete the currently selected schedule */ void slotScheduleDelete(); /** * This slot allows to enter the next scheduled transaction of * the currently selected schedule */ void slotScheduleEnter(); /** * This slot allows to skip the next scheduled transaction of * the currently selected schedule */ void slotScheduleSkip(); /** */ void slotTagNew(const QString& newnameBase, QString& id); void slotTagNew(); /** */ void slotTagDelete(); /** * This slot fires up the KCalc application */ void slotToolsStartKCalc(); void slotResetSelections(); void slotSelectAccount(const MyMoneyObject& account); void slotSelectInstitution(const MyMoneyObject& institution); void slotSelectInvestment(const MyMoneyObject& account); void slotSelectSchedule(); void slotSelectSchedule(const MyMoneySchedule& schedule); void slotSelectPayees(const QList& list); void slotSelectTags(const QList& list); void slotSelectBudget(const QList& list); void slotSelectTransactions(const KMyMoneyRegister::SelectedTransactions& list); void slotSelectCurrency(); void slotSelectCurrency(const MyMoneySecurity& currency); void slotSelectPrice(); void slotSelectPrice(const MyMoneyPrice& price); void slotTransactionMatch(); /** * Brings up the new account wizard and saves the information. */ void slotAccountNew(); void slotAccountNew(MyMoneyAccount&); /** * This method updates all KAction items to the current state. */ void slotUpdateActions(); /** * Brings up the input dialog and saves the information. */ void slotInstitutionNew(); /** * Brings up the input dialog and saves the information. If * the institution has been created, the @a id member is filled, * otherwise it is empty. * * @param institution reference to data to be used to create the * institution. id member will be updated. */ void slotInstitutionNew(MyMoneyInstitution& institution); /** * Loads a plugin */ void slotPluginLoad(const KPluginMetaData& metaData); /** * Unloads a plugin */ void slotPluginUnload(const KPluginMetaData& metaData); void webConnect(const QString& sourceUrl, const QByteArray &asn_id); void webConnect(const QUrl url) { webConnect(url.path(), QByteArray()); } private: /** * Create the transaction move menu and setup necessary connections. */ void createTransactionMoveMenu(); /** * This method sets the holidayRegion for use by the processing calendar. */ void setHolidayRegion(const QString& holidayRegion); /** * Load the status bar with the 'ready' message. This is hold in a single * place, so that is consistent with isReady(). */ void ready(); /** * Check if the status bar contains the 'ready' message. The return * value is used e.g. to detect if a quit operation is allowed or not. * * @retval true application is idle * @retval false application is active working on a longer operation */ bool isReady(); /** * Delete a possibly existing transaction editor but make sure to remove * any reference to it so that we avoid using a half-dead object */ void deleteTransactionEditor(); /** * delete all selected transactions w/o further questions */ void doDeleteTransactions(); /** * Re-implemented from IMyMoneyProcessingCalendar */ bool isProcessingDate(const QDate& date) const; /** * Depending on the setting of AutoSaveOnQuit, this method * asks the user to save the file or not. * * @returns see return values of KMessageBox::warningYesNoCancel() */ int askSaveOnClose(); /** * Implement common task when deleting or merging payees */ bool payeeReassign(int type); signals: /** * This signal is emitted when a new file is loaded. In the case file * is closed, this signal is also emitted with an empty url. */ void fileLoaded(const QUrl &url); /** * This signal is emitted when a payee/list of payees has been selected by * the GUI. If no payee is selected or the selection is removed, * @p payees is identical to an empty QList. This signal is used * by plugins to get information about changes. */ void payeesSelected(const QList& payees); /** * This signal is emitted when a tag/list of tags has been selected by * the GUI. If no tag is selected or the selection is removed, * @p tags is identical to an empty QList. This signal is used * by plugins to get information about changes. */ void tagsSelected(const QList& tags); /** * This signal is emitted when a transaction/list of transactions has been selected by * the GUI. If no transaction is selected or the selection is removed, * @p transactions is identical to an empty QList. This signal is used * by plugins to get information about changes. */ void transactionsSelected(const KMyMoneyRegister::SelectedTransactions& transactions); /** * This signal is sent out, when the user presses Ctrl+A or activates * the Select all transactions action. */ void selectAllTransactions(); /** * This signal is emitted when a list of budgets has been selected by * the GUI. If no budget is selected or the selection is removed, * @a budget is identical to an empty QList. This signal is used * by plugins to get information about changes. */ void budgetSelected(const QList& budget); void budgetRename(); /** * This signal is emitted when a new account has been selected by * the GUI. If no account is selected or the selection is removed, * @a account is identical to MyMoneyAccount(). This signal is used * by plugins to get information about changes. */ void accountSelected(const MyMoneyAccount& account); void investmentSelected(const MyMoneyAccount& account); /** * This signal is emitted when a new institution has been selected by * the GUI. If no institution is selected or the selection is removed, * @a institution is identical to MyMoneyInstitution(). This signal is used * by plugins to get information about changes. */ void institutionSelected(const MyMoneyInstitution& institution); /** * This signal is emitted when a new schedule has been selected by * the GUI. If no schedule is selected or the selection is removed, * @a schedule is identical to MyMoneySchedule(). This signal is used * by plugins to get information about changes. */ void scheduleSelected(const MyMoneySchedule& schedule); /** * This signal is emitted when a new currency has been selected by * the GUI. If no currency is selected or the selection is removed, * @a currency is identical to MyMoneySecurity(). This signal is used * by plugins to get information about changes. */ void currencySelected(const MyMoneySecurity& currency); /** * This signal is emitted when a new price has been selected by * the GUI. If no price is selected or the selection is removed, * @a price is identical to MyMoneyPrice(). */ void priceSelected(const MyMoneyPrice& price); void payeeRename(); void payeeCreated(const QString& id); void slotTagRename(); void tagCreated(const QString& id); void currencyRename(); void currencyCreated(const QString& id); void priceEdit(); void priceNew(); void priceDelete(); void priceOnlineUpdate(); void startMatchTransaction(const MyMoneyTransaction& t); void cancelMatchTransaction(); /** * This signal is emitted when an account has been successfully reconciled * and all transactions are updated in the engine. It can be used by plugins * to create reconciliation reports. * * @param account the account data * @param date the reconciliation date as provided through the dialog * @param startingBalance the starting balance as provided through the dialog * @param endingBalance the ending balance as provided through the dialog * @param transactionList reference to QList of QPair containing all * transaction/split pairs processed by the reconciliation. */ void accountReconciled(const MyMoneyAccount& account, const QDate& date, const MyMoneyMoney& startingBalance, const MyMoneyMoney& endingBalance, const QList >& transactionList); public: bool isActionToggled(const Action _a); static const QHash s_Actions; private: /// \internal d-pointer class. class Private; /* * Actually, one should write "Private * const d" but that confuses the KIDL * compiler in this context. It complains about the const keyword. So we leave * it out here */ /// \internal d-pointer instance. Private* d; }; extern KMyMoneyApp *kmymoney; typedef void(KMyMoneyApp::*KMyMoneyAppFunc)(); class KMStatus { public: KMStatus(const QString &text); ~KMStatus(); private: QString m_prevText; }; #define KMSTATUS(msg) KMStatus _thisStatus(msg) #endif // KMYMONEY_H diff --git a/kmymoney/plugins/interfaces/kmmviewinterface.cpp b/kmymoney/plugins/interfaces/kmmviewinterface.cpp index bb60d72b0..8b2e7b9e7 100644 --- a/kmymoney/plugins/interfaces/kmmviewinterface.cpp +++ b/kmymoney/plugins/interfaces/kmmviewinterface.cpp @@ -1,60 +1,60 @@ /*************************************************************************** viewinterface.cpp ------------------- begin : Wed Jan 5 2005 copyright : (C) 2005 Thomas Baumgart email : ipwizard@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kmmviewinterface.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmymoney.h" #include "kmymoneyview.h" -#include "selectedtransactions.h" +#include "selectedtransaction.h" KMyMoneyPlugin::KMMViewInterface::KMMViewInterface(KMyMoneyApp* app, KMyMoneyView* view, QObject* parent, const char* name) : ViewInterface(parent, name), m_view(view) { connect(app, SIGNAL(accountSelected(MyMoneyAccount)), this, SIGNAL(accountSelected(MyMoneyAccount))); connect(app, SIGNAL(transactionsSelected(KMyMoneyRegister::SelectedTransactions)), this, SIGNAL(transactionsSelected(KMyMoneyRegister::SelectedTransactions))); connect(app, SIGNAL(accountReconciled(MyMoneyAccount,QDate,MyMoneyMoney,MyMoneyMoney,QList >)), this, SIGNAL(accountReconciled(MyMoneyAccount,QDate,MyMoneyMoney,MyMoneyMoney,QList >))); connect(app, SIGNAL(institutionSelected(MyMoneyInstitution)), this, SIGNAL(institutionSelected(MyMoneyInstitution))); connect(m_view, SIGNAL(viewStateChanged(bool)), this, SIGNAL(viewStateChanged(bool))); connect(m_view, SIGNAL(kmmFilePlugin(uint)), this, SIGNAL(kmmFilePlugin(uint))); } //KMyMoneyViewBase* KMyMoneyPlugin::KMMViewInterface::addPage(const QString& item, const QString& icon) //{ // return m_view->addBasePage(item, icon); //} //void KMyMoneyPlugin::KMMViewInterface::addWidget(KMyMoneyViewBase* view, QWidget* w) //{ // if (view && w) // view->addWidget(w); //} diff --git a/kmymoney/plugins/printcheck/CMakeLists.txt b/kmymoney/plugins/printcheck/CMakeLists.txt index a2965aece..74c5aa3cd 100644 --- a/kmymoney/plugins/printcheck/CMakeLists.txt +++ b/kmymoney/plugins/printcheck/CMakeLists.txt @@ -1,72 +1,71 @@ # patch the version with the version defined in the build system configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kmm_printcheck.json.in ${CMAKE_CURRENT_BINARY_DIR}/kmm_printcheck.json @ONLY) set(kmm_printcheck_PART_SRCS numbertowords.cpp printcheck.cpp - ../../widgets/selectedtransaction.cpp ) kconfig_add_kcfg_files(kmm_printcheck_PART_SRCS pluginsettings.kcfgc) add_library(kmm_printcheck MODULE ${kmm_printcheck_PART_SRCS}) target_link_libraries(kmm_printcheck Qt5::PrintSupport KF5::I18n kmm_mymoney kmm_plugin ) if(ENABLE_WEBENGINE) target_link_libraries(kmm_printcheck Qt5::WebEngineWidgets) else(ENABLE_WEBENGINE) target_link_libraries(kmm_printcheck KF5::WebKit) endif(ENABLE_WEBENGINE) install(TARGETS kmm_printcheck DESTINATION "${KDE_INSTALL_PLUGINDIR}/kmymoney/") install(FILES kmm_printcheck.rc DESTINATION "${KXMLGUI_INSTALL_DIR}/kmm_printcheck") install(FILES check_template.html DESTINATION "${DATA_INSTALL_DIR}/kmm_printcheck") install(FILES check_template_green_linen.html DESTINATION "${DATA_INSTALL_DIR}/kmm_printcheck") # the KCM module set(kcm_kmm_printcheck_PART_SRCS kcm_printcheck.cpp ) kconfig_add_kcfg_files(kcm_kmm_printcheck_PART_SRCS pluginsettings.kcfgc) ki18n_wrap_ui(kcm_kmm_printcheck_PART_SRCS pluginsettingsdecl.ui) add_library(kcm_kmm_printcheck MODULE ${kcm_kmm_printcheck_PART_SRCS}) kcoreaddons_desktop_to_json(kcm_kmm_printcheck kcm_kmm_printcheck.desktop) target_link_libraries(kcm_kmm_printcheck Qt5::PrintSupport KF5::I18n KF5::ConfigWidgets KF5::Completion KF5::KIOWidgets KF5::CoreAddons ) if(ENABLE_WEBENGINE) target_link_libraries(kcm_kmm_printcheck Qt5::WebEngineWidgets) else(ENABLE_WEBENGINE) target_link_libraries(kcm_kmm_printcheck KF5::WebKit) endif(ENABLE_WEBENGINE) install(TARGETS kcm_kmm_printcheck DESTINATION "${KDE_INSTALL_PLUGINDIR}") install(FILES kcm_kmm_printcheck.desktop DESTINATION "${SERVICES_INSTALL_DIR}") diff --git a/kmymoney/plugins/printcheck/printcheck.cpp b/kmymoney/plugins/printcheck/printcheck.cpp index 2ec6a18ed..9d407f00e 100644 --- a/kmymoney/plugins/printcheck/printcheck.cpp +++ b/kmymoney/plugins/printcheck/printcheck.cpp @@ -1,239 +1,238 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ #include "printcheck.h" #include "config-kmymoney.h" // QT includes #include #include #include #ifdef ENABLE_WEBENGINE #include #else #include #endif #include #include // KDE includes #include #include #include #include // KMyMoney includes #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "mymoneyinstitution.h" #include "mymoneymoney.h" #include "mymoneypayee.h" #include "mymoneysecurity.h" #include "mymoneysplit.h" #include "mymoneytransaction.h" #include "mymoneyutils.h" #include "viewinterface.h" -#include "selectedtransactions.h" #include "numbertowords.h" #include "pluginsettings.h" struct KMMPrintCheckPlugin::Private { QAction* m_action; QString m_checkTemplateHTML; QStringList m_printedTransactionIdList; KMyMoneyRegister::SelectedTransactions m_transactions; }; KMMPrintCheckPlugin::KMMPrintCheckPlugin() : KMyMoneyPlugin::Plugin(nullptr, "Print check"/*must be the same as X-KDE-PluginInfo-Name*/) { // Tell the host application to load my GUI component setComponentName("kmm_printcheck", i18n("Print check")); setXMLFile("kmm_printcheck.rc"); // For ease announce that we have been loaded. qDebug("KMyMoney printcheck plugin loaded"); d = std::unique_ptr(new Private); // Create the actions of this plugin QString actionName = i18n("Print check"); d->m_action = actionCollection()->addAction("transaction_printcheck", this, SLOT(slotPrintCheck())); d->m_action->setText(actionName); // wait until a transaction is selected before enableing the action d->m_action->setEnabled(false); d->m_printedTransactionIdList = PluginSettings::printedChecks(); readCheckTemplate(); //! @todo Christian: Replace #if 0 connect(KMyMoneyPlugin::PluginLoader::instance(), SIGNAL(plug(KPluginInfo*)), this, SLOT(slotPlug(KPluginInfo*))); connect(KMyMoneyPlugin::PluginLoader::instance(), SIGNAL(configChanged(Plugin*)), this, SLOT(slotUpdateConfig())); #endif } /** * @internal Destructor is needed because destructor call of unique_ptr must be in this compile unit */ KMMPrintCheckPlugin::~KMMPrintCheckPlugin() { } void KMMPrintCheckPlugin::readCheckTemplate() { QString checkTemplateHTMLPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kmm_printcheck/check_template.html"); if (PluginSettings::checkTemplateFile().isEmpty()) { PluginSettings::setCheckTemplateFile(checkTemplateHTMLPath); PluginSettings::self()->save(); } QFile checkTemplateHTMLFile(PluginSettings::checkTemplateFile()); checkTemplateHTMLFile.open(QIODevice::ReadOnly); QTextStream stream(&checkTemplateHTMLFile); d->m_checkTemplateHTML = stream.readAll(); checkTemplateHTMLFile.close(); } bool KMMPrintCheckPlugin::canBePrinted(const KMyMoneyRegister::SelectedTransaction & selectedTransaction) const { MyMoneyFile* file = MyMoneyFile::instance(); bool isACheck = file->account(selectedTransaction.split().accountId()).accountType() == eMyMoney::Account::Checkings && selectedTransaction.split().shares().isNegative(); return isACheck && d->m_printedTransactionIdList.contains(selectedTransaction.transaction().id()) == 0; } void KMMPrintCheckPlugin::markAsPrinted(const KMyMoneyRegister::SelectedTransaction & selectedTransaction) { d->m_printedTransactionIdList.append(selectedTransaction.transaction().id()); } void KMMPrintCheckPlugin::slotPrintCheck() { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyMoneyToWordsConverter converter; #ifdef ENABLE_WEBENGINE auto htmlPart = new QWebEngineView(); #else auto htmlPart = new KWebView(); #endif KMyMoneyRegister::SelectedTransactions::const_iterator it; for (it = d->m_transactions.constBegin(); it != d->m_transactions.constEnd(); ++it) { if (!canBePrinted(*it)) continue; // skip this check since it was already printed QString checkHTML = d->m_checkTemplateHTML; const MyMoneyAccount account = file->account((*it).split().accountId()); const MyMoneySecurity currency = file->currency(account.currencyId()); const MyMoneyInstitution institution = file->institution(file->account((*it).split().accountId()).institutionId()); // replace the predefined tokens // data about the user checkHTML.replace("$OWNER_NAME", file->user().name()); checkHTML.replace("$OWNER_ADDRESS", file->user().address()); checkHTML.replace("$OWNER_CITY", file->user().city()); checkHTML.replace("$OWNER_STATE", file->user().state()); // data about the account institution checkHTML.replace("$INSTITUTION_NAME", institution.name()); checkHTML.replace("$INSTITUTION_STREET", institution.street()); checkHTML.replace("$INSTITUTION_TELEPHONE", institution.telephone()); checkHTML.replace("$INSTITUTION_TOWN", institution.town()); checkHTML.replace("$INSTITUTION_CITY", institution.city()); checkHTML.replace("$INSTITUTION_POSTCODE", institution.postcode()); checkHTML.replace("$INSTITUTION_MANAGER", institution.manager()); // data about the transaction checkHTML.replace("$DATE", QLocale().toString((*it).transaction().postDate(), QLocale::ShortFormat)); checkHTML.replace("$CHECK_NUMBER", (*it).split().number()); checkHTML.replace("$PAYEE_NAME", file->payee((*it).split().payeeId()).name()); checkHTML.replace("$PAYEE_ADDRESS", file->payee((*it).split().payeeId()).address()); checkHTML.replace("$PAYEE_CITY", file->payee((*it).split().payeeId()).city()); checkHTML.replace("$PAYEE_POSTCODE", file->payee((*it).split().payeeId()).postcode()); checkHTML.replace("$PAYEE_STATE", file->payee((*it).split().payeeId()).state()); checkHTML.replace("$AMOUNT_STRING", converter.convert((*it).split().shares().abs(), currency.smallestAccountFraction())); checkHTML.replace("$AMOUNT_DECIMAL", MyMoneyUtils::formatMoney((*it).split().shares().abs(), currency)); checkHTML.replace("$MEMO", (*it).split().memo()); // print the check htmlPart->setHtml(checkHTML, QUrl("file://")); m_currentPrinter = new QPrinter(); QPointer dialog = new QPrintDialog(m_currentPrinter); dialog->setWindowTitle(QString()); if (dialog->exec() != QDialog::Accepted) { delete m_currentPrinter; m_currentPrinter = nullptr; continue; } else { #ifdef ENABLE_WEBENGINE htmlPart->page()->print(m_currentPrinter, [=] (bool) {delete m_currentPrinter; m_currentPrinter = nullptr;}); #else htmlPart->print(m_currentPrinter); #endif } delete dialog; // mark the transaction as printed markAsPrinted(*it); } PluginSettings::setPrintedChecks(d->m_printedTransactionIdList); delete htmlPart; } void KMMPrintCheckPlugin::slotTransactionsSelected(const KMyMoneyRegister::SelectedTransactions& transactions) { d->m_transactions = transactions; bool actionEnabled = false; // enable/disable the action depending if there are transactions selected or not // and whether they can be printed or not KMyMoneyRegister::SelectedTransactions::const_iterator it; for (it = d->m_transactions.constBegin(); it != d->m_transactions.constEnd(); ++it) { if (canBePrinted(*it)) { actionEnabled = true; break; } } d->m_action->setEnabled(actionEnabled); } // the plugin loader plugs in a plugin void KMMPrintCheckPlugin::slotPlug(KPluginInfo *info) { if (info->name() == objectName()) { connect(viewInterface(), SIGNAL(transactionsSelected(KMyMoneyRegister::SelectedTransactions)), this, SLOT(slotTransactionsSelected(KMyMoneyRegister::SelectedTransactions))); } } // the plugin's configurations has changed void KMMPrintCheckPlugin::slotUpdateConfig() { PluginSettings::self()->load(); // re-read the data because the configuration has changed readCheckTemplate(); d->m_printedTransactionIdList = PluginSettings::printedChecks(); } diff --git a/kmymoney/plugins/printcheck/printcheck.h b/kmymoney/plugins/printcheck/printcheck.h index a47ce9e30..6b937d1d4 100644 --- a/kmymoney/plugins/printcheck/printcheck.h +++ b/kmymoney/plugins/printcheck/printcheck.h @@ -1,61 +1,61 @@ /*************************************************************************** * Copyright 2009 Cristian Onet onet.cristian@gmail.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ #ifndef PRINTCHECK_H #define PRINTCHECK_H #include #include "kmymoneyplugin.h" -#include "selectedtransactions.h" +#include "selectedtransaction.h" class KPluginInfo; class QPrinter; class QObject; class KMMPrintCheckPlugin: public KMyMoneyPlugin::Plugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.kmymoney.plugins.printcheck" FILE "kmm_printcheck.json") public: KMMPrintCheckPlugin(); ~KMMPrintCheckPlugin(); private: void readCheckTemplate(); bool canBePrinted(const KMyMoneyRegister::SelectedTransaction & selectedTransaction) const; void markAsPrinted(const KMyMoneyRegister::SelectedTransaction & selectedTransaction); protected slots: void slotPrintCheck(); void slotTransactionsSelected(const KMyMoneyRegister::SelectedTransactions& transactions); // the plugin loader plugs in a plugin void slotPlug(KPluginInfo*); // the plugin's configurations has changed void slotUpdateConfig(); private: struct Private; std::unique_ptr d; QPrinter *m_currentPrinter; }; #endif // PRINTCHECK_H diff --git a/kmymoney/views/kgloballedgerview.cpp b/kmymoney/views/kgloballedgerview.cpp index 3d90840ff..d505afb3c 100644 --- a/kmymoney/views/kgloballedgerview.cpp +++ b/kmymoney/views/kgloballedgerview.cpp @@ -1,1656 +1,1640 @@ /*************************************************************************** kgloballedgerview.cpp - description ------------------- begin : Wed Jul 26 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kgloballedgerview.h" #include // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyaccount.h" #include "mymoneyfile.h" #include "kmymoneyaccountcombo.h" #include "register.h" #include "transactioneditor.h" -#include "selectedtransactions.h" +#include "selectedtransaction.h" #include "kmymoneyglobalsettings.h" #include "registersearchline.h" #include "kfindtransactiondlg.h" #include "kmymoney.h" #include "scheduledtransaction.h" #include "accountsmodel.h" #include "models.h" #include "mymoneyprice.h" #include "mymoneyschedule.h" #include "mymoneysecurity.h" #include "mymoneytransaction.h" #include "mymoneytransactionfilter.h" #include "mymoneysplit.h" #include "mymoneyutils.h" #include "transaction.h" #include "transactionform.h" -#include "widgetenums.h" class KGlobalLedgerView::Private { public: Private(); // used to store the id of an item and the id of an immeadiate unselected sibling void storeId(KMyMoneyRegister::RegisterItem *item, QString &id, QString &backupId) { if (item) { // the id of the item id = item->id(); // the id of the item's previous/next unselected item for (KMyMoneyRegister::RegisterItem *it = item->prevItem(); it != 0 && backupId.isEmpty(); it = it->prevItem()) { if (!it->isSelected()) { backupId = it->id(); } } // if we didn't found previous unselected items search trough the next items for (KMyMoneyRegister::RegisterItem *it = item->nextItem(); it != 0 && backupId.isEmpty(); it = it->nextItem()) { if (!it->isSelected()) { backupId = it->id(); } } } } // use to match an item by it's id or a backup id which has a lower precedence void matchItemById(KMyMoneyRegister::RegisterItem **item, KMyMoneyRegister::Transaction* t, QString &id, QString &backupId) { if (!backupId.isEmpty() && t->id() == backupId) *item = t; if (!id.isEmpty() && t->id() == id) { // we found the real thing there's no need for the backup anymore backupId.clear(); *item = t; } } MousePressFilter* m_mousePressFilter; KMyMoneyRegister::RegisterSearchLineWidget* m_registerSearchLine; QString m_reconciliationAccount; QDate m_reconciliationDate; MyMoneyMoney m_endingBalance; int m_precision; bool m_recursion; bool m_showDetails; KMyMoneyRegister::Action m_action; // models AccountNamesFilterProxyModel *m_filterProxyModel; // widgets KMyMoneyAccountCombo* m_accountComboBox; MyMoneyMoney m_totalBalance; bool m_balanceIsApproximated; }; MousePressFilter::MousePressFilter(QWidget* parent) : QObject(parent), m_lastMousePressEvent(0), m_filterActive(true) { } void MousePressFilter::addWidget(QWidget* w) { m_parents.append(w); } void MousePressFilter::setFilterActive(bool state) { m_filterActive = state; } bool MousePressFilter::isChildOf(QWidget* child, QWidget *parent) { // QDialogs cannot be detected directly, but it can be assumed, // that events on a widget that do not have a parent widget within // our application are dialogs. if (!child->parentWidget()) return true; while (child) { if (child == parent) return true; // If one of the ancestors is a KPassivePopup or a KDialog or a popup widget then // it's as if it is a child of our own because these widgets could // appear during transaction entry (message boxes, completer widgets) if (dynamic_cast(child) || ((child->windowFlags() & Qt::Popup) && child != kmymoney)) return true; child = child->parentWidget(); } return false; } bool MousePressFilter::eventFilter(QObject* o, QEvent* e) { if (m_filterActive) { if (e->type() == QEvent::MouseButtonPress && !m_lastMousePressEvent) { QList::const_iterator it_w; for (it_w = m_parents.constBegin(); it_w != m_parents.constEnd(); ++it_w) { if (isChildOf((QWidget*)o, (*it_w))) { m_lastMousePressEvent = e; break; } } if (it_w == m_parents.constEnd()) { m_lastMousePressEvent = e; bool rc = false; emit mousePressedOnExternalWidget(rc); } } if (e->type() != QEvent::MouseButtonPress) { m_lastMousePressEvent = 0; } } return false; } KGlobalLedgerView::Private::Private() : m_mousePressFilter(0), m_registerSearchLine(0), m_recursion(false), m_showDetails(false), m_filterProxyModel(0), m_accountComboBox(0), m_balanceIsApproximated(false) { } QDate KGlobalLedgerView::m_lastPostDate; KGlobalLedgerView::KGlobalLedgerView(QWidget *parent) : KMyMoneyViewBase(parent), d(new Private), m_needReload(false), m_needLoad(true), m_newAccountLoaded(true), m_inEditMode(false) { } KGlobalLedgerView::~KGlobalLedgerView() { delete d; } void KGlobalLedgerView::setDefaultFocus() { QTimer::singleShot(0, d->m_registerSearchLine->searchLine(), SLOT(setFocus())); } void KGlobalLedgerView::init() { m_needLoad = false; auto vbox = new QVBoxLayout(this); setLayout(vbox); vbox->setSpacing(6); vbox->setMargin(0); d->m_mousePressFilter = new MousePressFilter((QWidget*)this); d->m_action = KMyMoneyRegister::ActionNone; // the proxy filter model d->m_filterProxyModel = new AccountNamesFilterProxyModel(this); d->m_filterProxyModel->addAccountGroup(QVector {eMyMoney::Account::Asset, eMyMoney::Account::Liability, eMyMoney::Account::Equity}); auto const model = Models::instance()->accountsModel(); d->m_filterProxyModel->setSourceModel(model); d->m_filterProxyModel->setSourceColumns(model->getColumns()); d->m_filterProxyModel->sort((int)eAccountsModel::Column::Account); // create the toolbar frame at the top of the view m_toolbarFrame = new QFrame(); QHBoxLayout* toolbarLayout = new QHBoxLayout(m_toolbarFrame); toolbarLayout->setContentsMargins(0, 0, 0, 0); toolbarLayout->setSpacing(6); // the account selector widget d->m_accountComboBox = new KMyMoneyAccountCombo(); d->m_accountComboBox->setModel(d->m_filterProxyModel); toolbarLayout->addWidget(d->m_accountComboBox); vbox->addWidget(m_toolbarFrame); toolbarLayout->setStretchFactor(d->m_accountComboBox, 60); // create the register frame m_registerFrame = new QFrame(); QVBoxLayout* registerFrameLayout = new QVBoxLayout(m_registerFrame); registerFrameLayout->setContentsMargins(0, 0, 0, 0); registerFrameLayout->setSpacing(0); vbox->addWidget(m_registerFrame); vbox->setStretchFactor(m_registerFrame, 2); m_register = new KMyMoneyRegister::Register(m_registerFrame); m_register->setUsedWithEditor(true); registerFrameLayout->addWidget(m_register); m_register->installEventFilter(this); connect(m_register, SIGNAL(openContextMenu()), this, SIGNAL(openContextMenu())); connect(m_register, SIGNAL(transactionsSelected(KMyMoneyRegister::SelectedTransactions)), this, SLOT(slotUpdateSummaryLine(KMyMoneyRegister::SelectedTransactions))); connect(m_register->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotSortOptions())); connect(m_register, SIGNAL(reconcileStateColumnClicked(KMyMoneyRegister::Transaction*)), this, SLOT(slotToggleTransactionMark(KMyMoneyRegister::Transaction*))); // insert search line widget d->m_registerSearchLine = new KMyMoneyRegister::RegisterSearchLineWidget(m_register, m_toolbarFrame); toolbarLayout->addWidget(d->m_registerSearchLine); toolbarLayout->setStretchFactor(d->m_registerSearchLine, 100); // create the summary frame m_summaryFrame = new QFrame(); QHBoxLayout* summaryFrameLayout = new QHBoxLayout(m_summaryFrame); summaryFrameLayout->setContentsMargins(0, 0, 0, 0); summaryFrameLayout->setSpacing(0); m_leftSummaryLabel = new QLabel(m_summaryFrame); m_centerSummaryLabel = new QLabel(m_summaryFrame); m_rightSummaryLabel = new QLabel(m_summaryFrame); summaryFrameLayout->addWidget(m_leftSummaryLabel); QSpacerItem* spacer = new QSpacerItem(20, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); summaryFrameLayout->addItem(spacer); summaryFrameLayout->addWidget(m_centerSummaryLabel); spacer = new QSpacerItem(20, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); summaryFrameLayout->addItem(spacer); summaryFrameLayout->addWidget(m_rightSummaryLabel); vbox->addWidget(m_summaryFrame); // create the button frame m_buttonFrame = new QFrame(this); QVBoxLayout* buttonLayout = new QVBoxLayout(m_buttonFrame); buttonLayout->setContentsMargins(0, 0, 0, 0); buttonLayout->setSpacing(0); vbox->addWidget(m_buttonFrame); m_buttonbar = new KToolBar(m_buttonFrame, 0, true); m_buttonbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); buttonLayout->addWidget(m_buttonbar); m_buttonbar->addAction(kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::TransactionNew])); m_buttonbar->addAction(kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::TransactionDelete])); m_buttonbar->addAction(kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::TransactionEdit])); m_buttonbar->addAction(kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::TransactionEnter])); m_buttonbar->addAction(kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::TransactionCancel])); m_buttonbar->addAction(kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::TransactionAccept])); m_buttonbar->addAction(kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::TransactionMatch])); // create the transaction form frame m_formFrame = new QFrame(this); QVBoxLayout* frameLayout = new QVBoxLayout(m_formFrame); frameLayout->setContentsMargins(5, 5, 5, 5); frameLayout->setSpacing(0); m_form = new KMyMoneyTransactionForm::TransactionForm(m_formFrame); frameLayout->addWidget(m_form->tabBar(m_formFrame)); frameLayout->addWidget(m_form); m_formFrame->setFrameShape(QFrame::Panel); m_formFrame->setFrameShadow(QFrame::Raised); vbox->addWidget(m_formFrame); connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotLoadView())); connect(m_register, SIGNAL(focusChanged(KMyMoneyRegister::Transaction*)), m_form, SLOT(slotSetTransaction(KMyMoneyRegister::Transaction*))); connect(m_register, SIGNAL(focusChanged()), kmymoney, SLOT(slotUpdateActions())); connect(d->m_accountComboBox, SIGNAL(accountSelected(QString)), this, SLOT(slotSelectAccount(QString))); connect(m_register, SIGNAL(transactionsSelected(KMyMoneyRegister::SelectedTransactions)), this, SIGNAL(transactionsSelected(KMyMoneyRegister::SelectedTransactions))); connect(m_register, SIGNAL(editTransaction()), this, SIGNAL(startEdit())); connect(m_register, SIGNAL(emptyItemSelected()), this, SLOT(slotNewTransaction())); connect(m_register, SIGNAL(aboutToSelectItem(KMyMoneyRegister::RegisterItem*,bool&)), this, SLOT(slotAboutToSelectItem(KMyMoneyRegister::RegisterItem*,bool&))); connect(d->m_mousePressFilter, SIGNAL(mousePressedOnExternalWidget(bool&)), this, SIGNAL(cancelOrEndEdit(bool&))); connect(m_form, SIGNAL(newTransaction(KMyMoneyRegister::Action)), this, SLOT(slotNewTransaction(KMyMoneyRegister::Action))); // setup mouse press filter d->m_mousePressFilter->addWidget(m_formFrame); d->m_mousePressFilter->addWidget(m_buttonFrame); d->m_mousePressFilter->addWidget(m_summaryFrame); d->m_mousePressFilter->addWidget(m_registerFrame); m_tooltipPosn = QPoint(); } void KGlobalLedgerView::slotAboutToSelectItem(KMyMoneyRegister::RegisterItem* item, bool& okToSelect) { Q_UNUSED(item); emit cancelOrEndEdit(okToSelect); } void KGlobalLedgerView::slotLoadView() { m_needReload = true; if (isVisible()) { if (!m_inEditMode) { setUpdatesEnabled(false); loadView(); setUpdatesEnabled(true); m_needReload = false; // force a new account if the current one is empty m_newAccountLoaded = m_account.id().isEmpty(); } } } void KGlobalLedgerView::clear() { // clear current register contents m_register->clear(); // setup header font QFont font = KMyMoneyGlobalSettings::listHeaderFont(); QFontMetrics fm(font); int height = fm.lineSpacing() + 6; m_register->horizontalHeader()->setMinimumHeight(height); m_register->horizontalHeader()->setMaximumHeight(height); m_register->horizontalHeader()->setFont(font); // setup cell font font = KMyMoneyGlobalSettings::listCellFont(); m_register->setFont(font); // clear the form m_form->clear(); // the selected transactions list m_transactionList.clear(); // and the selected account in the combo box d->m_accountComboBox->setSelected(QString()); // fraction defaults to two digits d->m_precision = 2; } void KGlobalLedgerView::loadView() { MYMONEYTRACER(tracer); // setup form visibility m_formFrame->setVisible(KMyMoneyGlobalSettings::transactionForm()); // no account selected emit accountSelected(MyMoneyAccount()); // no transaction selected KMyMoneyRegister::SelectedTransactions list; emit transactionsSelected(list); QMap isSelected; QString focusItemId; QString backUpFocusItemId; // in case the focus item is removed QString anchorItemId; QString backUpAnchorItemId; // in case the anchor item is removed if (!m_newAccountLoaded) { // remember the current selected transactions KMyMoneyRegister::RegisterItem* item = m_register->firstItem(); for (; item; item = item->nextItem()) { if (item->isSelected()) { isSelected[item->id()] = true; } } // remember the item that has the focus d->storeId(m_register->focusItem(), focusItemId, backUpFocusItemId); // and the one that has the selection anchor d->storeId(m_register->anchorItem(), anchorItemId, backUpAnchorItemId); } else { d->m_registerSearchLine->searchLine()->reset(); } // clear the current contents ... clear(); // ... load the combobox widget and select current account ... loadAccounts(); // ... setup the register columns ... m_register->setupRegister(m_account); // ... setup the form ... m_form->setupForm(m_account); if (m_account.id().isEmpty()) { // if we don't have an account we bail out setEnabled(false); return; } setEnabled(true); m_register->setUpdatesEnabled(false); // ... and recreate it KMyMoneyRegister::RegisterItem* focusItem = 0; KMyMoneyRegister::RegisterItem* anchorItem = 0; QMap actBalance, clearedBalance, futureBalance; QMap::iterator it_b; try { // setup the filter to select the transactions we want to display // and update the sort order QString sortOrder; QString key; QDate reconciliationDate = d->m_reconciliationDate; MyMoneyTransactionFilter filter(m_account.id()); // if it's an investment account, we also take care of // the sub-accounts (stock accounts) if (m_account.accountType() == eMyMoney::Account::Investment) filter.addAccount(m_account.accountList()); if (isReconciliationAccount()) { key = "kmm-sort-reconcile"; sortOrder = KMyMoneyGlobalSettings::sortReconcileView(); filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled); filter.addState((int)eMyMoney::TransactionFilter::State::Cleared); } else { filter.setDateFilter(KMyMoneyGlobalSettings::startDate().date(), QDate()); key = "kmm-sort-std"; sortOrder = KMyMoneyGlobalSettings::sortNormalView(); if (KMyMoneyGlobalSettings::hideReconciledTransactions() && !m_account.isIncomeExpense()) { filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled); filter.addState((int)eMyMoney::TransactionFilter::State::Cleared); } } filter.setReportAllSplits(true); // check if we have an account override of the sort order if (!m_account.value(key).isEmpty()) sortOrder = m_account.value(key); // setup sort order m_register->setSortOrder(sortOrder); // retrieve the list from the engine MyMoneyFile::instance()->transactionList(m_transactionList, filter); kmymoney->slotStatusProgressBar(0, m_transactionList.count()); // create the elements for the register QList >::const_iterator it; QMapuniqueMap; int i = 0; for (it = m_transactionList.constBegin(); it != m_transactionList.constEnd(); ++it) { uniqueMap[(*it).first.id()]++; KMyMoneyRegister::Transaction* t = KMyMoneyRegister::Register::transactionFactory(m_register, (*it).first, (*it).second, uniqueMap[(*it).first.id()]); actBalance[t->split().accountId()] = MyMoneyMoney(); kmymoney->slotStatusProgressBar(++i, 0); // if we're in reconciliation and the state is cleared, we // force the item to show in dimmed intensity to get a visual focus // on those items, that we need to work on if (isReconciliationAccount() && (*it).second.reconcileFlag() == eMyMoney::Split::State::Cleared) { t->setReducedIntensity(true); } } // create dummy entries for the scheduled transactions if sorted by postdate int period = KMyMoneyGlobalSettings::schedulePreview(); - if (m_register->primarySortKey() == eWidgets::SortField::PostDate) { + if (m_register->primarySortKey() == KMyMoneyRegister::PostDateSort) { // show scheduled transactions which have a scheduled postdate // within the next 'period' days. In reconciliation mode, the // period starts on the statement date. QDate endDate = QDate::currentDate().addDays(period); if (isReconciliationAccount()) endDate = reconciliationDate.addDays(period); QList scheduleList = MyMoneyFile::instance()->scheduleList(m_account.id()); while (scheduleList.count() > 0) { MyMoneySchedule& s = scheduleList.first(); for (;;) { if (s.isFinished() || s.adjustedNextDueDate() > endDate) { break; } MyMoneyTransaction t(s.id(), KMyMoneyUtils::scheduledTransaction(s)); // if the transaction is scheduled and overdue, it can't // certainly be posted in the past. So we take today's date // as the alternative if (s.isOverdue()) { t.setPostDate(s.adjustedDate(QDate::currentDate(), s.weekendOption())); } else { t.setPostDate(s.adjustedNextDueDate()); } const QList& splits = t.splits(); QList::const_iterator it_s; for (it_s = splits.begin(); it_s != splits.end(); ++it_s) { if ((*it_s).accountId() == m_account.id()) { new KMyMoneyRegister::StdTransactionScheduled(m_register, t, *it_s, uniqueMap[t.id()]); } } // keep track of this payment locally (not in the engine) if (s.isOverdue()) { s.setLastPayment(QDate::currentDate()); } else { s.setLastPayment(s.nextDueDate()); } // if this is a one time schedule, we can bail out here as we're done if (s.occurrence() == eMyMoney::Schedule::Occurrence::Once) break; // for all others, we check if the next payment date is still 'in range' QDate nextDueDate = s.nextPayment(s.nextDueDate()); if (nextDueDate.isValid()) { s.setNextDueDate(nextDueDate); } else { break; } } scheduleList.pop_front(); } } // add the group markers m_register->addGroupMarkers(); // sort the transactions according to the sort setting m_register->sortItems(); // remove trailing and adjacent markers m_register->removeUnwantedGroupMarkers(); // add special markers for reconciliation now so that they do not get // removed by m_register->removeUnwantedGroupMarkers(). Needs resorting // of items but that's ok. KMyMoneyRegister::StatementGroupMarker* statement = 0; KMyMoneyRegister::StatementGroupMarker* dStatement = 0; KMyMoneyRegister::StatementGroupMarker* pStatement = 0; if (isReconciliationAccount()) { switch (m_register->primarySortKey()) { - case eWidgets::SortField::PostDate: + case KMyMoneyRegister::PostDateSort: statement = new KMyMoneyRegister::StatementGroupMarker(m_register, KMyMoneyRegister::Deposit, reconciliationDate, i18n("Statement Details")); m_register->sortItems(); break; - case eWidgets::SortField::Type: + case KMyMoneyRegister::TypeSort: dStatement = new KMyMoneyRegister::StatementGroupMarker(m_register, KMyMoneyRegister::Deposit, reconciliationDate, i18n("Statement Deposit Details")); pStatement = new KMyMoneyRegister::StatementGroupMarker(m_register, KMyMoneyRegister::Payment, reconciliationDate, i18n("Statement Payment Details")); m_register->sortItems(); break; default: break; } } // we need at least the balance for the account we currently show actBalance[m_account.id()] = MyMoneyMoney(); if (m_account.accountType() == eMyMoney::Account::Investment) foreach (const auto accountID, m_account.accountList()) actBalance[accountID] = MyMoneyMoney(); // determine balances (actual, cleared). We do this by getting the actual // balance of all entered transactions from the engine and walk the list // of transactions backward. Also re-select a transaction if it was // selected before and setup the focus item. MyMoneyMoney factor(1, 1); if (m_account.accountGroup() == eMyMoney::Account::Liability || m_account.accountGroup() == eMyMoney::Account::Equity) factor = -factor; QMap deposits; QMap payments; QMap depositAmount; QMap paymentAmount; for (it_b = actBalance.begin(); it_b != actBalance.end(); ++it_b) { MyMoneyMoney balance = MyMoneyFile::instance()->balance(it_b.key()); balance = balance * factor; clearedBalance[it_b.key()] = futureBalance[it_b.key()] = (*it_b) = balance; deposits[it_b.key()] = payments[it_b.key()] = 0; depositAmount[it_b.key()] = MyMoneyMoney(); paymentAmount[it_b.key()] = MyMoneyMoney(); } tracer.printf("total balance of %s = %s", qPrintable(m_account.name()), qPrintable(actBalance[m_account.id()].formatMoney("", 2))); tracer.printf("future balance of %s = %s", qPrintable(m_account.name()), qPrintable(futureBalance[m_account.id()].formatMoney("", 2))); tracer.printf("cleared balance of %s = %s", qPrintable(m_account.name()), qPrintable(clearedBalance[m_account.id()].formatMoney("", 2))); KMyMoneyRegister::RegisterItem* p = m_register->lastItem(); focusItem = 0; // take care of possibly trailing scheduled transactions (bump up the future balance) while (p) { if (p->isSelectable()) { KMyMoneyRegister::Transaction* t = dynamic_cast(p); if (t && t->isScheduled()) { MyMoneyMoney balance = futureBalance[t->split().accountId()]; const MyMoneySplit& split = t->split(); // if this split is a stock split, we can't just add the amount of shares if (t->transaction().isStockSplit()) { balance = balance * split.shares(); } else { balance += split.shares() * factor; } futureBalance[split.accountId()] = balance; } else if (t && !focusItem) focusItem = p; } p = p->prevItem(); } p = m_register->lastItem(); while (p) { KMyMoneyRegister::Transaction* t = dynamic_cast(p); if (t) { if (isSelected.contains(t->id())) t->setSelected(true); d->matchItemById(&focusItem, t, focusItemId, backUpFocusItemId); d->matchItemById(&anchorItem, t, anchorItemId, backUpAnchorItemId); const MyMoneySplit& split = t->split(); MyMoneyMoney balance = futureBalance[split.accountId()]; t->setBalance(balance); // if this split is a stock split, we can't just add the amount of shares if (t->transaction().isStockSplit()) { balance /= split.shares(); } else { balance -= split.shares() * factor; } if (!t->isScheduled()) { if (isReconciliationAccount() && t->transaction().postDate() <= reconciliationDate && split.reconcileFlag() == eMyMoney::Split::State::Cleared) { if (split.shares().isNegative()) { payments[split.accountId()]++; paymentAmount[split.accountId()] += split.shares(); } else { deposits[split.accountId()]++; depositAmount[split.accountId()] += split.shares(); } } if (t->transaction().postDate() > QDate::currentDate()) { tracer.printf("Reducing actual balance by %s because %s/%s(%s) is in the future", qPrintable((split.shares() * factor).formatMoney("", 2)), qPrintable(t->transaction().id()), qPrintable(split.id()), qPrintable(t->transaction().postDate().toString(Qt::ISODate))); actBalance[split.accountId()] -= split.shares() * factor; } } futureBalance[split.accountId()] = balance; } p = p->prevItem(); } clearedBalance[m_account.id()] = MyMoneyFile::instance()->clearedBalance(m_account.id(), reconciliationDate); tracer.printf("total balance of %s = %s", qPrintable(m_account.name()), qPrintable(actBalance[m_account.id()].formatMoney("", 2))); tracer.printf("future balance of %s = %s", qPrintable(m_account.name()), qPrintable(futureBalance[m_account.id()].formatMoney("", 2))); tracer.printf("cleared balance of %s = %s", qPrintable(m_account.name()), qPrintable(clearedBalance[m_account.id()].formatMoney("", 2))); // update statement information if (statement) { const QString aboutDeposits = i18np("%1 deposit (%2)", "%1 deposits (%2)", deposits[m_account.id()], depositAmount[m_account.id()].abs().formatMoney(m_account.fraction())); const QString aboutPayments = i18np("%1 payment (%2)", "%1 payments (%2)", payments[m_account.id()], paymentAmount[m_account.id()].abs().formatMoney(m_account.fraction())); statement->setText(i18nc("%1 is a string, e.g. 7 deposits; %2 is a string, e.g. 4 payments", "%1, %2", aboutDeposits, aboutPayments)); } if (pStatement) { pStatement->setText(i18np("%1 payment (%2)", "%1 payments (%2)", payments[m_account.id()] , paymentAmount[m_account.id()].abs().formatMoney(m_account.fraction()))); } if (dStatement) { dStatement->setText(i18np("%1 deposit (%2)", "%1 deposits (%2)", deposits[m_account.id()] , depositAmount[m_account.id()].abs().formatMoney(m_account.fraction()))); } // add a last empty entry for new transactions // leave some information about the current account MyMoneySplit split; split.setReconcileFlag(eMyMoney::Split::State::NotReconciled); // make sure to use the value specified in the option during reconciliation if (isReconciliationAccount()) split.setReconcileFlag(static_cast(KMyMoneyGlobalSettings::defaultReconciliationState())); KMyMoneyRegister::Register::transactionFactory(m_register, MyMoneyTransaction(), split, 0); m_register->updateRegister(true); if (focusItem) { // in case we have some selected items we just set the focus item // in other cases, we make the focusitem also the selected item if (anchorItem && (anchorItem != focusItem)) { m_register->setFocusItem(focusItem); m_register->setAnchorItem(anchorItem); } else m_register->selectItem(focusItem, true); } else { // just use the empty line at the end if nothing else exists in the ledger p = m_register->lastItem(); m_register->setFocusItem(p); m_register->selectItem(p); focusItem = p; } updateSummaryLine(actBalance, clearedBalance); kmymoney->slotStatusProgressBar(-1, -1); } catch (const MyMoneyException &) { m_account = MyMoneyAccount(); clear(); } d->m_showDetails = KMyMoneyGlobalSettings::showRegisterDetailed(); // and tell everyone what's selected emit accountSelected(m_account); KMyMoneyRegister::SelectedTransactions actualSelection(m_register); emit transactionsSelected(actualSelection); } void KGlobalLedgerView::updateSummaryLine(const QMap& actBalance, const QMap& clearedBalance) { MyMoneyFile* file = MyMoneyFile::instance(); m_leftSummaryLabel->show(); m_centerSummaryLabel->show(); m_rightSummaryLabel->show(); if (isReconciliationAccount()) { if (m_account.accountType() != eMyMoney::Account::Investment) { m_leftSummaryLabel->setText(i18n("Statement: %1", d->m_endingBalance.formatMoney("", d->m_precision))); m_centerSummaryLabel->setText(i18nc("Cleared balance", "Cleared: %1", clearedBalance[m_account.id()].formatMoney("", d->m_precision))); d->m_totalBalance = clearedBalance[m_account.id()] - d->m_endingBalance; } } else { // update summary line in normal mode QDate reconcileDate = m_account.lastReconciliationDate(); if (reconcileDate.isValid()) { m_leftSummaryLabel->setText(i18n("Last reconciled: %1", QLocale().toString(reconcileDate, QLocale::ShortFormat))); } else { m_leftSummaryLabel->setText(i18n("Never reconciled")); } QPalette palette = m_rightSummaryLabel->palette(); palette.setColor(m_rightSummaryLabel->foregroundRole(), m_leftSummaryLabel->palette().color(foregroundRole())); if (m_account.accountType() != eMyMoney::Account::Investment) { m_centerSummaryLabel->setText(i18nc("Cleared balance", "Cleared: %1", clearedBalance[m_account.id()].formatMoney("", d->m_precision))); d->m_totalBalance = actBalance[m_account.id()]; } else { m_centerSummaryLabel->hide(); MyMoneyMoney balance; MyMoneySecurity base = file->baseCurrency(); QMap::const_iterator it_b; // reset the approximated flag d->m_balanceIsApproximated = false; for (it_b = actBalance.begin(); it_b != actBalance.end(); ++it_b) { MyMoneyAccount stock = file->account(it_b.key()); QString currencyId = stock.currencyId(); MyMoneySecurity sec = file->security(currencyId); MyMoneyMoney rate(1, 1); if (stock.isInvest()) { currencyId = sec.tradingCurrency(); const MyMoneyPrice &priceInfo = file->price(sec.id(), currencyId); d->m_balanceIsApproximated |= !priceInfo.isValid(); rate = priceInfo.rate(sec.tradingCurrency()); } if (currencyId != base.id()) { const MyMoneyPrice &priceInfo = file->price(sec.tradingCurrency(), base.id()); d->m_balanceIsApproximated |= !priceInfo.isValid(); rate = (rate * priceInfo.rate(base.id())).convertPrecision(sec.pricePrecision()); } balance += ((*it_b) * rate).convert(base.smallestAccountFraction()); } d->m_totalBalance = balance; } m_rightSummaryLabel->setPalette(palette); } // determine the number of selected transactions KMyMoneyRegister::SelectedTransactions selection; m_register->selectedTransactions(selection); slotUpdateSummaryLine(selection); } void KGlobalLedgerView::slotUpdateSummaryLine(const KMyMoneyRegister::SelectedTransactions& selection) { if (selection.count() > 1) { MyMoneyMoney balance; foreach (const KMyMoneyRegister::SelectedTransaction& t, selection) { if (!t.isScheduled()) { balance += t.split().shares(); } } m_rightSummaryLabel->setText(QString("%1: %2").arg(QChar(0x2211), balance.formatMoney("", d->m_precision))); } else { if (isReconciliationAccount()) { m_rightSummaryLabel->setText(i18n("Difference: %1", d->m_totalBalance.formatMoney("", d->m_precision))); } else { if (m_account.accountType() != eMyMoney::Account::Investment) { m_rightSummaryLabel->setText(i18n("Balance: %1", d->m_totalBalance.formatMoney("", d->m_precision))); bool showNegative = d->m_totalBalance.isNegative(); if (m_account.accountGroup() == eMyMoney::Account::Liability && !d->m_totalBalance.isZero()) showNegative = !showNegative; if (showNegative) { QPalette palette = m_rightSummaryLabel->palette(); palette.setColor(m_rightSummaryLabel->foregroundRole(), KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative)); m_rightSummaryLabel->setPalette(palette); } } else { m_rightSummaryLabel->setText(i18n("Investment value: %1%2", d->m_balanceIsApproximated ? "~" : "", d->m_totalBalance.formatMoney(MyMoneyFile::instance()->baseCurrency().tradingSymbol(), d->m_precision))); } } } } void KGlobalLedgerView::resizeEvent(QResizeEvent* ev) { if (m_needLoad) init(); m_register->resize(KMyMoneyRegister::DetailColumn); m_form->resize(KMyMoneyTransactionForm::ValueColumn1); KMyMoneyViewBase::resizeEvent(ev); } void KGlobalLedgerView::loadAccounts() { MyMoneyFile* file = MyMoneyFile::instance(); auto const model = Models::instance()->accountsModel(); // check if the current account still exists and make it the // current account if (!m_account.id().isEmpty()) { try { m_account = file->account(m_account.id()); } catch (const MyMoneyException &) { m_account = MyMoneyAccount(); return; } } // TODO: check why the invalidate is needed here d->m_filterProxyModel->invalidate(); d->m_filterProxyModel->sort((int)eAccountsModel::Column::Account); d->m_filterProxyModel->setHideClosedAccounts(KMyMoneyGlobalSettings::hideClosedAccounts() && !kmymoney->isActionToggled(Action::ViewShowAll)); d->m_filterProxyModel->setHideEquityAccounts(!KMyMoneyGlobalSettings::expertMode()); d->m_accountComboBox->expandAll(); if (m_account.id().isEmpty()) { // find the first favorite account QModelIndexList list = model->match(model->index(0, 0), (int)eAccountsModel::Role::Favorite, QVariant(true), 1, Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive)); if (list.count() > 0) { QVariant accountId = list.front().data((int)eAccountsModel::Role::ID); if (accountId.isValid()) { m_account = file->account(accountId.toString()); } } if (m_account.id().isEmpty()) { // there are no favorite accounts find any account QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, QVariant(QString("*")), -1, Qt::MatchFlags(Qt::MatchWildcard | Qt::MatchRecursive)); for (QModelIndexList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) { if (!it->parent().isValid()) continue; // skip the top level accounts QVariant accountId = (*it).data((int)eAccountsModel::Role::ID); if (accountId.isValid()) { MyMoneyAccount a = file->account(accountId.toString()); if (!a.isInvest()) { m_account = a; break; } } } } } if (!m_account.id().isEmpty()) { d->m_accountComboBox->setSelected(m_account.id()); try { d->m_precision = MyMoneyMoney::denomToPrec(m_account.fraction()); } catch (const MyMoneyException &) { qDebug("Security %s for account %s not found", qPrintable(m_account.currencyId()), qPrintable(m_account.name())); d->m_precision = 2; } } } void KGlobalLedgerView::selectTransaction(const QString& id) { if (!id.isEmpty()) { KMyMoneyRegister::RegisterItem* p = m_register->lastItem(); while (p) { KMyMoneyRegister::Transaction* t = dynamic_cast(p); if (t) { if (t->transaction().id() == id) { m_register->selectItem(t); m_register->ensureItemVisible(t); break; } } p = p->prevItem(); } } } void KGlobalLedgerView::slotSelectAllTransactions() { if(m_needLoad) init(); m_register->clearSelection(); KMyMoneyRegister::RegisterItem* p = m_register->firstItem(); while (p) { KMyMoneyRegister::Transaction* t = dynamic_cast(p); if (t) { if (t->isVisible() && t->isSelectable() && !t->isScheduled() && !t->id().isEmpty()) { t->setSelected(true); } } p = p->nextItem(); } // this is here only to re-paint the items without selecting anything because the data (including the selection) is not really held in the model right now m_register->selectAll(); // inform everyone else about the selected items KMyMoneyRegister::SelectedTransactions list(m_register); emit transactionsSelected(list); } void KGlobalLedgerView::slotSetReconcileAccount(const MyMoneyAccount& acc, const QDate& reconciliationDate, const MyMoneyMoney& endingBalance) { if(m_needLoad) init(); if (d->m_reconciliationAccount != acc.id()) { // make sure the account is selected if (!acc.id().isEmpty()) slotSelectAccount(acc.id()); d->m_reconciliationAccount = acc.id(); d->m_reconciliationDate = reconciliationDate; d->m_endingBalance = endingBalance; if (acc.accountGroup() == eMyMoney::Account::Liability) d->m_endingBalance = -endingBalance; m_newAccountLoaded = true; if (acc.id().isEmpty()) { m_buttonbar->removeAction(kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::AccountPostponeReconciliation])); m_buttonbar->removeAction(kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::AccountFinishReconciliation])); } else { m_buttonbar->addAction(kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::AccountPostponeReconciliation])); m_buttonbar->addAction(kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::AccountFinishReconciliation])); // when we start reconciliation, we need to reload the view // because no data has been changed. When postponing or finishing // reconciliation, the data change in the engine takes care of updateing // the view. slotLoadView(); } } } -void KGlobalLedgerView::slotSetReconcileAccount(const MyMoneyAccount& acc, const QDate& reconciliationDate) -{ - slotSetReconcileAccount(acc, reconciliationDate, MyMoneyMoney()); -} - -void KGlobalLedgerView::slotSetReconcileAccount(const MyMoneyAccount& acc) -{ - slotSetReconcileAccount(acc, QDate(), MyMoneyMoney()); -} - -void KGlobalLedgerView::slotSetReconcileAccount() -{ - slotSetReconcileAccount(MyMoneyAccount(), QDate(), MyMoneyMoney()); -} - bool KGlobalLedgerView::isReconciliationAccount() const { return m_account.id() == d->m_reconciliationAccount; } bool KGlobalLedgerView::slotSelectAccount(const MyMoneyObject& obj) { if (typeid(obj) != typeid(MyMoneyAccount)) return false; if (d->m_recursion) return false; d->m_recursion = true; const MyMoneyAccount& acc = dynamic_cast(obj); bool rc = slotSelectAccount(acc.id()); d->m_recursion = false; return rc; } bool KGlobalLedgerView::slotSelectAccount(const QString& id, const QString& transactionId) { bool rc = true; if (!id.isEmpty()) { if (m_account.id() != id) { try { m_account = MyMoneyFile::instance()->account(id); // if a stock account is selected, we show the // the corresponding parent (investment) account if (m_account.isInvest()) { m_account = MyMoneyFile::instance()->account(m_account.parentAccountId()); } m_newAccountLoaded = true; slotLoadView(); } catch (const MyMoneyException &) { qDebug("Unable to retrieve account %s", qPrintable(id)); rc = false; } } else { // we need to refresh m_account.m_accountList, a child could have been deleted m_account = MyMoneyFile::instance()->account(id); emit accountSelected(m_account); } selectTransaction(transactionId); } return rc; } void KGlobalLedgerView::slotNewTransaction(KMyMoneyRegister::Action id) { if (!m_inEditMode) { d->m_action = id; emit newTransaction(); } } void KGlobalLedgerView::slotNewTransaction() { slotNewTransaction(KMyMoneyRegister::ActionNone); } void KGlobalLedgerView::setupDefaultAction() { switch (m_account.accountType()) { case eMyMoney::Account::Asset: case eMyMoney::Account::AssetLoan: case eMyMoney::Account::Savings: d->m_action = KMyMoneyRegister::ActionDeposit; break; default: d->m_action = KMyMoneyRegister::ActionWithdrawal; break; } } bool KGlobalLedgerView::selectEmptyTransaction() { bool rc = false; if (!m_inEditMode) { // in case we don't know the type of transaction to be created, // have at least one selected transaction and the id of // this transaction is not empty, we take it as template for the // transaction to be created KMyMoneyRegister::SelectedTransactions list(m_register); if ((d->m_action == KMyMoneyRegister::ActionNone) && (!list.isEmpty()) && (!list[0].transaction().id().isEmpty())) { // the new transaction to be created will have the same type // as the one that currently has the focus KMyMoneyRegister::Transaction* t = dynamic_cast(m_register->focusItem()); if (t) d->m_action = t->actionType(); m_register->clearSelection(); } // if we still don't have an idea which type of transaction // to create, we use the default. if (d->m_action == KMyMoneyRegister::ActionNone) { setupDefaultAction(); } m_register->selectItem(m_register->lastItem()); m_register->updateRegister(); rc = true; } return rc; } TransactionEditor* KGlobalLedgerView::startEdit(const KMyMoneyRegister::SelectedTransactions& list) { // we use the warnlevel to keep track, if we have to warn the // user that some or all splits have been reconciled or if the // user cannot modify the transaction if at least one split // has the status frozen. The following value are used: // // 0 - no sweat, user can modify // 1 - user should be warned that at least one split has been reconciled // already // 2 - user will be informed, that this transaction cannot be changed anymore int warnLevel = list.warnLevel(); Q_ASSERT(warnLevel < 2); // otherwise the edit action should not be enabled switch (warnLevel) { case 0: break; case 1: if (KMessageBox::warningContinueCancel(0, i18n( "At least one split of the selected transactions has been reconciled. " "Do you wish to continue to edit the transactions anyway?" ), i18n("Transaction already reconciled"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "EditReconciledTransaction") == KMessageBox::Cancel) { warnLevel = 2; } break; case 2: KMessageBox::sorry(0, i18n("At least one split of the selected transactions has been frozen. " "Editing the transactions is therefore prohibited."), i18n("Transaction already frozen")); break; case 3: KMessageBox::sorry(0, i18n("At least one split of the selected transaction references an account that has been closed. " "Editing the transactions is therefore prohibited."), i18n("Account closed")); break; } if (warnLevel > 1) return 0; TransactionEditor* editor = 0; KMyMoneyRegister::Transaction* item = dynamic_cast(m_register->focusItem()); if (item) { // in case the current focus item is not selected, we move the focus to the first selected transaction if (!item->isSelected()) { KMyMoneyRegister::RegisterItem* p; for (p = m_register->firstItem(); p; p = p->nextItem()) { KMyMoneyRegister::Transaction* t = dynamic_cast(p); if (t && t->isSelected()) { m_register->setFocusItem(t); item = t; break; } } } // decide, if we edit in the register or in the form TransactionEditorContainer* parent; if (m_formFrame->isVisible()) parent = m_form; else { parent = m_register; } editor = item->createEditor(parent, list, m_lastPostDate); // check that we use the same transaction commodity in all selected transactions // if not, we need to update this in the editor's list. The user can also bail out // of this operation which means that we have to stop editing here. if (editor) { if (!editor->fixTransactionCommodity(m_account)) { // if the user wants to quit, we need to destroy the editor // and bail out delete editor; editor = 0; } } if (editor) { if (parent == m_register) { // make sure, the height of the table is correct m_register->updateRegister(KMyMoneyGlobalSettings::ledgerLens() | !KMyMoneyGlobalSettings::transactionForm()); } m_inEditMode = true; connect(editor, SIGNAL(transactionDataSufficient(bool)), kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::TransactionEnter]), SLOT(setEnabled(bool))); connect(editor, SIGNAL(returnPressed()), kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::TransactionEnter]), SLOT(trigger())); connect(editor, SIGNAL(escapePressed()), kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::TransactionCancel]), SLOT(trigger())); connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), editor, SLOT(slotReloadEditWidgets())); connect(editor, SIGNAL(finishEdit(KMyMoneyRegister::SelectedTransactions)), this, SLOT(slotLeaveEditMode(KMyMoneyRegister::SelectedTransactions))); connect(editor, SIGNAL(objectCreation(bool)), d->m_mousePressFilter, SLOT(setFilterDeactive(bool))); connect(editor, SIGNAL(createPayee(QString,QString&)), kmymoney, SLOT(slotPayeeNew(QString,QString&))); connect(editor, SIGNAL(createTag(QString,QString&)), kmymoney, SLOT(slotTagNew(QString,QString&))); connect(editor, SIGNAL(createCategory(MyMoneyAccount&,MyMoneyAccount)), kmymoney, SLOT(slotCategoryNew(MyMoneyAccount&,MyMoneyAccount))); connect(editor, SIGNAL(createSecurity(MyMoneyAccount&,MyMoneyAccount)), kmymoney, SLOT(slotInvestmentNew(MyMoneyAccount&,MyMoneyAccount))); connect(editor, SIGNAL(assignNumber()), kmymoney, SLOT(slotTransactionAssignNumber())); connect(editor, SIGNAL(lastPostDateUsed(QDate)), this, SLOT(slotKeepPostDate(QDate))); // create the widgets, place them in the parent and load them with data // setup tab order m_tabOrderWidgets.clear(); editor->setup(m_tabOrderWidgets, m_account, d->m_action); Q_ASSERT(!m_tabOrderWidgets.isEmpty()); // install event filter in all taborder widgets QWidgetList::const_iterator it_w = m_tabOrderWidgets.constBegin(); for (; it_w != m_tabOrderWidgets.constEnd(); ++it_w) { (*it_w)->installEventFilter(this); } // Install a filter that checks if a mouse press happened outside // of one of our own widgets. qApp->installEventFilter(d->m_mousePressFilter); // Check if the editor has some preference on where to set the focus // If not, set the focus to the first widget in the tab order QWidget* focusWidget = editor->firstWidget(); if (!focusWidget) focusWidget = m_tabOrderWidgets.first(); // for some reason, this only works reliably if delayed a bit QTimer::singleShot(10, focusWidget, SLOT(setFocus())); // preset to 'I have no idea which type to create' for the next round. d->m_action = KMyMoneyRegister::ActionNone; } } return editor; } void KGlobalLedgerView::slotLeaveEditMode(const KMyMoneyRegister::SelectedTransactions& list) { m_inEditMode = false; qApp->removeEventFilter(d->m_mousePressFilter); // a possible focusOut event may have removed the focus, so we // install it back again. m_register->focusItem()->setFocus(true); // if we come back from editing a new item, we make sure that // we always select the very last known transaction entry no // matter if the transaction has been created or not. if (list.count() && list[0].transaction().id().isEmpty()) { // block signals to prevent some infinite loops that might occur here. m_register->blockSignals(true); m_register->clearSelection(); KMyMoneyRegister::RegisterItem* p = m_register->lastItem(); if (p && p->prevItem()) p = p->prevItem(); m_register->selectItem(p); m_register->updateRegister(true); m_register->blockSignals(false); // we need to update the form manually as sending signals was blocked KMyMoneyRegister::Transaction* t = dynamic_cast(p); if (t) m_form->slotSetTransaction(t); } else { if (!KMyMoneySettings::transactionForm()) { // update the row height of the transactions because it might differ between viewing/editing mode when not using the transaction form m_register->blockSignals(true); m_register->updateRegister(true); m_register->blockSignals(false); } } if (m_needReload) slotLoadView(); m_register->setFocus(); } bool KGlobalLedgerView::focusNextPrevChild(bool next) { bool rc = false; // qDebug("KGlobalLedgerView::focusNextPrevChild(editmode=%s)", m_inEditMode ? "true" : "false"); if (m_inEditMode) { QWidget *w = 0; w = qApp->focusWidget(); // qDebug("w = %p", w); int currentWidgetIndex = m_tabOrderWidgets.indexOf(w); while (w && currentWidgetIndex == -1) { // qDebug("'%s' not in list, use parent", qPrintable(w->objectName())); w = w->parentWidget(); currentWidgetIndex = m_tabOrderWidgets.indexOf(w); } if (currentWidgetIndex != -1) { // if(w) qDebug("tab order is at '%s'", qPrintable(w->objectName())); currentWidgetIndex += next ? 1 : -1; if (currentWidgetIndex < 0) currentWidgetIndex = m_tabOrderWidgets.size() - 1; else if (currentWidgetIndex >= m_tabOrderWidgets.size()) currentWidgetIndex = 0; w = m_tabOrderWidgets[currentWidgetIndex]; // qDebug("currentWidgetIndex = %d, w = %p", currentWidgetIndex, w); if (((w->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) && w->isVisible() && w->isEnabled()) { // qDebug("Selecting '%s' (%p) as focus", qPrintable(w->objectName()), w); w->setFocus(); rc = true; } } } else rc = KMyMoneyViewBase::focusNextPrevChild(next); return rc; } void KGlobalLedgerView::showEvent(QShowEvent* event) { if (m_needLoad) init(); emit aboutToShow(View::Ledgers); if (m_needReload) { if (!m_inEditMode) { setUpdatesEnabled(false); loadView(); setUpdatesEnabled(true); m_needReload = false; m_newAccountLoaded = false; } } else { emit accountSelected(m_account); KMyMoneyRegister::SelectedTransactions list(m_register); emit transactionsSelected(list); } // don't forget base class implementation KMyMoneyViewBase::showEvent(event); } bool KGlobalLedgerView::eventFilter(QObject* o, QEvent* e) { bool rc = false; // Need to capture mouse position here as QEvent::ToolTip is too slow m_tooltipPosn = QCursor::pos(); if (e->type() == QEvent::KeyPress) { if (m_inEditMode) { // qDebug("object = %s, key = %d", o->className(), k->key()); if (o == m_register) { // we hide all key press events from the register // while editing a transaction rc = true; } } } if (!rc) rc = KMyMoneyViewBase::eventFilter(o, e); return rc; } void KGlobalLedgerView::showTooltip(const QString msg) const { QToolTip::showText(m_tooltipPosn, msg); } void KGlobalLedgerView::slotSortOptions() { QPointer dlg = new KSortOptionDlg(this); QString key; QString sortOrder, def; if (isReconciliationAccount()) { key = "kmm-sort-reconcile"; def = KMyMoneyGlobalSettings::sortReconcileView(); } else { key = "kmm-sort-std"; def = KMyMoneyGlobalSettings::sortNormalView(); } // check if we have an account override of the sort order if (!m_account.value(key).isEmpty()) sortOrder = m_account.value(key); QString oldOrder = sortOrder; dlg->setSortOption(sortOrder, def); if (dlg->exec() == QDialog::Accepted) { if (dlg != 0) { sortOrder = dlg->sortOption(); if (sortOrder != oldOrder) { if (sortOrder.isEmpty()) { m_account.deletePair(key); } else { m_account.setValue(key, sortOrder); } MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->modifyAccount(m_account); ft.commit(); } catch (const MyMoneyException &e) { qDebug("Unable to update sort order for account '%s': %s", qPrintable(m_account.name()), qPrintable(e.what())); } } } } delete dlg; } void KGlobalLedgerView::slotToggleTransactionMark(KMyMoneyRegister::Transaction* /* t */) { if (!m_inEditMode) { emit toggleReconciliationFlag(); } } void KGlobalLedgerView::slotKeepPostDate(const QDate& date) { m_lastPostDate = date; } bool KGlobalLedgerView::canCreateTransactions(QString& tooltip) const { bool rc = true; if (m_account.id().isEmpty()) { tooltip = i18n("Cannot create transactions when no account is selected."); rc = false; } if (m_account.accountGroup() == eMyMoney::Account::Income || m_account.accountGroup() == eMyMoney::Account::Expense) { tooltip = i18n("Cannot create transactions in the context of a category."); showTooltip(tooltip); rc = false; } if (m_account.isClosed()) { tooltip = i18n("Cannot create transactions in a closed account."); showTooltip(tooltip); rc = false; } return rc; } bool KGlobalLedgerView::canProcessTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const { MyMoneyAccount acc; QString closedAccount; if (m_register->focusItem() == 0) return false; bool rc = true; if (list.warnLevel() == 3) { //Closed account somewhere KMyMoneyRegister::SelectedTransactions::const_iterator it_t; for (it_t = list.begin(); rc && it_t != list.end(); ++it_t) { QList splitList = (*it_t).transaction().splits(); QString id = splitList.first().accountId(); acc = MyMoneyFile::instance()->account(id); if (!acc.isClosed()) { //wrong split, try other id = splitList.last().accountId(); acc = MyMoneyFile::instance()->account(id); } closedAccount = acc.name(); break; } tooltip = i18n("Cannot process transactions in account %1, which is closed.", closedAccount); showTooltip(tooltip); return false; } if (!m_register->focusItem()->isSelected()) { tooltip = i18n("Cannot process transaction with focus if it is not selected."); showTooltip(tooltip); return false; } tooltip.clear(); return !list.isEmpty(); } bool KGlobalLedgerView::canModifyTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const { return canProcessTransactions(list, tooltip) && list.canModify(); } bool KGlobalLedgerView::canDuplicateTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const { return canProcessTransactions(list, tooltip) && list.canDuplicate(); } bool KGlobalLedgerView::canEditTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const { // check if we can edit the list of transactions. We can edit, if // // a) no mix of standard and investment transactions exist // b) if a split transaction is selected, this is the only selection // c) none of the splits is frozen // d) the transaction having the current focus is selected // check for d) if (!canProcessTransactions(list, tooltip)) return false; // check for c) if (list.warnLevel() == 2) { tooltip = i18n("Cannot edit transactions with frozen splits."); showTooltip(tooltip); return false; } bool rc = true; int investmentTransactions = 0; int normalTransactions = 0; if (m_account.accountGroup() == eMyMoney::Account::Income || m_account.accountGroup() == eMyMoney::Account::Expense) { tooltip = i18n("Cannot edit transactions in the context of a category."); showTooltip(tooltip); rc = false; } if (m_account.isClosed()) { tooltip = i18n("Cannot create or edit any transactions in Account %1 as it is closed", m_account.name()); showTooltip(tooltip); rc = false; } KMyMoneyRegister::SelectedTransactions::const_iterator it_t; QString action; for (it_t = list.begin(); rc && it_t != list.end(); ++it_t) { if ((*it_t).transaction().id().isEmpty()) { tooltip.clear(); rc = false; continue; } if (KMyMoneyUtils::transactionType((*it_t).transaction()) == KMyMoneyUtils::InvestmentTransaction) { if (action.isEmpty()) { action = (*it_t).split().action(); continue; } if (action == (*it_t).split().action()) { continue; } else { tooltip = (i18n("Cannot edit mixed investment action/type transactions together.")); showTooltip(tooltip); rc = false; break; } } if (KMyMoneyUtils::transactionType((*it_t).transaction()) == KMyMoneyUtils::InvestmentTransaction) ++investmentTransactions; else ++normalTransactions; // check for a) if (investmentTransactions != 0 && normalTransactions != 0) { tooltip = i18n("Cannot edit investment transactions and non-investment transactions together."); showTooltip(tooltip); rc = false; break; } // check for b) but only for normalTransactions if ((*it_t).transaction().splitCount() > 2 && normalTransactions != 0) { if (list.count() > 1) { tooltip = i18n("Cannot edit multiple split transactions at once."); showTooltip(tooltip); rc = false; break; } } } // check for multiple transactions being selected in an investment account // we do not allow editing in this case: https://bugs.kde.org/show_bug.cgi?id=240816 // later on, we might allow to edit investment transactions of the same type /// Can now disable the following check. /* if (rc == true && investmentTransactions > 1) { tooltip = i18n("Cannot edit multiple investment transactions at once"); rc = false; }*/ // now check that we have the correct account type for investment transactions if (rc == true && investmentTransactions != 0) { if (m_account.accountType() != eMyMoney::Account::Investment) { tooltip = i18n("Cannot edit investment transactions in the context of this account."); rc = false; } } return rc; } diff --git a/kmymoney/views/kgloballedgerview.h b/kmymoney/views/kgloballedgerview.h index e97cdd9a0..2ebb7e859 100644 --- a/kmymoney/views/kgloballedgerview.h +++ b/kmymoney/views/kgloballedgerview.h @@ -1,414 +1,409 @@ /*************************************************************************** kgloballedgerview.h - description ------------------- begin : Sat Jul 13 2002 copyright : (C) 2000-2002 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KGLOBALLEDGERVIEW_H #define KGLOBALLEDGERVIEW_H // ---------------------------------------------------------------------------- // QT Includes #include #include // ---------------------------------------------------------------------------- // KDE Includes class KToolBar; // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyviewbase.h" #include "mymoneyaccount.h" #include "registeritem.h" -#include "selectedtransactions.h" +#include "selectedtransaction.h" class MyMoneyReport; class MyMoneySplit; class MyMoneyTransaction; class TransactionEditor; class QLabel; -class QFrame; - namespace KMyMoneyRegister { class Register; } namespace KMyMoneyRegister { class Transaction; } namespace KMyMoneyTransactionForm { class TransactionForm; } /** * helper class implementing an event filter to detect mouse button press * events on widgets outside a given set of widgets. This is used internally * to detect when to leave the edit mode. */ class MousePressFilter : public QObject { Q_OBJECT public: explicit MousePressFilter(QWidget* parent = 0); /** * Add widget @p w to the list of possible parent objects. See eventFilter() how * they will be used. */ void addWidget(QWidget* w); public slots: /** * This slot allows to activate/deactivate the filter. By default the * filter is active. * * @param state Allows to activate (@a true) or deactivate (@a false) the filter */ void setFilterActive(bool state = true); /** * This slot allows to activate/deactivate the filter. By default the * filter is active. * * @param state Allows to deactivate (@a true) or activate (@a false) the filter */ void setFilterDeactive(bool state = false) { setFilterActive(!state); } protected: /** * This method checks if the widget @p child is a child of * the widget @p parent and returns either @a true or @a false. * * @param child pointer to child widget * @param parent pointer to parent widget * @retval true @p child points to widget which has @p parent as parent or grand-parent * @retval false @p child points to a widget which is not related to @p parent */ bool isChildOf(QWidget* child, QWidget* parent); /** * Reimplemented from base class. Sends out the mousePressedOnExternalWidget() signal * if object @p o points to an object which is not a child widget of any added previously * using the addWidget() method. The signal is sent out only once for each event @p e. * * @param o pointer to QObject * @param e pointer to QEvent * @return always returns @a false */ bool eventFilter(QObject* o, QEvent* e); signals: void mousePressedOnExternalWidget(bool&); private: QList m_parents; QEvent* m_lastMousePressEvent; bool m_filterActive; }; /** * @author Thomas Baumgart */ class KGlobalLedgerView : public KMyMoneyViewBase { Q_OBJECT public: explicit KGlobalLedgerView(QWidget *parent = nullptr); ~KGlobalLedgerView(); void setDefaultFocus() override; /** * This method returns the id of the currently selected account * or QString() if none is selected. */ const QString accountId() const { return m_account.id(); } /** * Checks if new transactions can be created in the current context * * @param tooltip reference to string receiving the tooltip text * which explains why the modify function is not available (in case * of returning @c false) * * @retval true Yes, view allows to create transactions (tooltip is not changed) * @retval false No, view does not support creation of transactions (tooltip is updated with message) */ bool canCreateTransactions(QString& tooltip) const; /** * Checks if a list of transactions can be modified (edit/delete) in the current context * * @param list list of selected transactions * @param tooltip reference to string receiving the tooltip text * which explains why the modify function is not available (in case * of returning @c false) * * @retval true Yes, view allows to edit/delete transactions (tooltip is not changed) * @retval false No, view cannot edit/delete transactions (tooltip is updated with message) */ bool canModifyTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const; bool canDuplicateTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const; /** * Checks if the list of transactions can be edited in the current context * * @param list list of selected transactions * @param tooltip reference to string receiving the tooltip text * which explains why the edit function is not available (in case * of returning @c false) * * @return @c true if edit operation is possible, @c false if not */ bool canEditTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const; TransactionEditor* startEdit(const KMyMoneyRegister::SelectedTransactions& list); /** * Method to prepare the ledger view to create a new transaction. * Returns if successful or not. * * retval true Emtpy transaction selected. * retval false Not successful (e.g. already editing a transaction) */ bool selectEmptyTransaction(); public slots: void showEvent(QShowEvent* event) override; /** * This method loads the view with data from the MyMoney engine. */ void slotLoadView(); /** * This slot is used to select the correct ledger view type for * the account specified by @p id in a specific mode. * * @param accountId Internal id used for the account to show * @param transactionId Internal id used for the transaction to select. * Default is QString() which will select the last * transaction in the ledger if not the same account * * @retval true selection of account referenced by @p id succeeded * @retval false selection of account failed */ bool slotSelectAccount(const QString& accountId, const QString& transactionId = QString()); /** * This method is provided for convenience and acts as the method above. */ bool slotSelectAccount(const MyMoneyObject& acc); /** * Switch to reconciliation mode for account @a account. * If @a account is MyMoneyAccount() (the default), reconciliation mode * is turned off. * * @param account account for which reconciliation mode is activated. * Default is MyMoneyAccount(). * @param reconciliationDate date of statment * @param endingBalance The calculated ending balance for the statement * Default ist 0. */ - void slotSetReconcileAccount(const MyMoneyAccount& account, const QDate& reconciliationDate, const MyMoneyMoney& endingBalance); - void slotSetReconcileAccount(const MyMoneyAccount& account, const QDate& reconciliationDate); - void slotSetReconcileAccount(const MyMoneyAccount& account); - void slotSetReconcileAccount(); + void slotSetReconcileAccount(const MyMoneyAccount& account = MyMoneyAccount(), const QDate& reconciliationDate = QDate(), const MyMoneyMoney& endingBalance = MyMoneyMoney()); /** * Select all transactions in the ledger that are not hidden. */ void slotSelectAllTransactions(); private: void showTooltip(const QString msg) const; protected: /** * This method reloads the account selection combo box of the * view with all asset and liability accounts from the engine. * If the account id of the current account held in @p m_accountId is * empty or if the referenced account does not exist in the engine, * the first account found in the list will be made the current account. */ void loadAccounts(); /** * This method clears the register, form, transaction list. See @sa m_register, * @sa m_transactionList */ void clear(); void loadView(); void resizeEvent(QResizeEvent*) override; void selectTransaction(const QString& id); /** * This method handles the focus of the keyboard. When in edit mode * (m_inEditMode is true) the keyboard focus is handled * according to the widgets that are referenced in m_tabOrderWidgets. * If not in edit mode, the base class functionality is provided. * * @param next true if forward-tab, false if backward-tab was * pressed by the user */ bool focusNextPrevChild(bool next) override; bool eventFilter(QObject* o, QEvent* e) override; /** * Returns @a true if setReconciliationAccount() has been called for * the current loaded account. * * @retval true current account is in reconciliation mode * @retval false current account is not in reconciliation mode */ bool isReconciliationAccount() const; /** * Updates the values on the summary line beneath the register with * the given values. The contents shown differs between reconciliation * mode and normal mode. * * @param actBalance map of account indexed values to be used as actual balance * @param clearedBalance map of account indexed values to be used as cleared balance */ void updateSummaryLine(const QMap& actBalance, const QMap& clearedBalance); /** * setup the default action according to the current account type */ void setupDefaultAction(); protected slots: void slotLeaveEditMode(const KMyMoneyRegister::SelectedTransactions& list); void slotNewTransaction(); void slotNewTransaction(KMyMoneyRegister::Action); void slotSortOptions(); void slotToggleTransactionMark(KMyMoneyRegister::Transaction* t); void slotKeepPostDate(const QDate&); void slotAboutToSelectItem(KMyMoneyRegister::RegisterItem*, bool&); void slotUpdateSummaryLine(const KMyMoneyRegister::SelectedTransactions&); protected: /** * This member keeps the date that was used as the last posting date. * It will be updated whenever the user modifies the post date * and is used to preset the posting date when new transactions are created. * This member is initialised to the current date when the program is started. */ static QDate m_lastPostDate; private: /// \internal d-pointer class. class Private; /// \internal d-pointer instance. Private* const d; // frames QFrame* m_toolbarFrame; QFrame* m_registerFrame; QFrame* m_buttonFrame; QFrame* m_formFrame; QFrame* m_summaryFrame; // widgets KMyMoneyRegister::Register* m_register; KToolBar* m_buttonbar; /** * This member holds the currently selected account */ MyMoneyAccount m_account; /** * This member holds the transaction list */ QList > m_transactionList; QLabel* m_leftSummaryLabel; QLabel* m_centerSummaryLabel; QLabel* m_rightSummaryLabel; KMyMoneyTransactionForm::TransactionForm* m_form; bool m_needReload; /** * This member holds the load state of page */ bool m_needLoad; bool m_newAccountLoaded; bool m_inEditMode; QWidgetList m_tabOrderWidgets; QPoint m_tooltipPosn; signals: void accountSelected(const MyMoneyObject&); void transactionsSelected(const KMyMoneyRegister::SelectedTransactions&); void newTransaction(); void startEdit(); void endEdit(); void cancelOrEndEdit(bool&); /** * This signal is emitted, when a new report has been generated. A * 'generated' report is halfway between a default report and a custom * report. It's created by the system in response to the user's * request, and it's usually filtered to be a little more specific * than the usual default reports. * * The proper behaviour when getting this signal is to switch to the * reports view and display the report. But it should NOT be added * to the data file, unless the user customizes it further. That's * because the user can always come back to the ledger UI to generate * the report again. * * @param report reference to MyMoneyReport object that contains the report * details */ void reportGenerated(const MyMoneyReport& report); void openContextMenu(); /** * This signal is sent out, when the current selected transaction should * be marked different */ void toggleReconciliationFlag(); private: bool canProcessTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const; /** Initializes page and sets its load status to initialized */ void init(); }; #endif diff --git a/kmymoney/views/khomeview.cpp b/kmymoney/views/khomeview.cpp index 4b48fa58e..3d317a4c3 100644 --- a/kmymoney/views/khomeview.cpp +++ b/kmymoney/views/khomeview.cpp @@ -1,2065 +1,2063 @@ /*************************************************************************** khomeview.cpp - description ------------------- begin : Tue Jan 22 2002 copyright : (C) 2000-2002 by Michael Edwardes Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "khomeview.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_WEBENGINE #include #else #include #endif // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyutils.h" #include "kmymoneyutils.h" #include "kwelcomepage.h" #include "kmymoneyglobalsettings.h" #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "mymoneyprice.h" #include "mymoneyforecast.h" #include "kmymoney.h" #include "kreportchartview.h" #include "pivottable.h" #include "pivotgrid.h" #include "reportaccount.h" -#include "mymoneysplit.h" -#include "mymoneytransaction.h" #include "icons.h" #include "kmymoneywebpage.h" #include "mymoneyschedule.h" #include "mymoneyenums.h" #define VIEW_LEDGER "ledger" #define VIEW_SCHEDULE "schedule" #define VIEW_WELCOME "welcome" #define VIEW_HOME "home" #define VIEW_REPORTS "reports" using namespace Icons; using namespace eMyMoney; bool accountNameLess(const MyMoneyAccount &acc1, const MyMoneyAccount &acc2) { return acc1.name().localeAwareCompare(acc2.name()) < 0; } using namespace reports; class KHomeView::Private { public: Private() : m_showAllSchedules(false), m_needReload(false), m_needLoad(true), m_netWorthGraphLastValidSize(400, 300) { } /** * daily balances of an account */ typedef QMap dailyBalances; #ifdef ENABLE_WEBENGINE QWebEngineView *m_view; #else KWebView *m_view; #endif QString m_html; bool m_showAllSchedules; bool m_needReload; bool m_needLoad; MyMoneyForecast m_forecast; MyMoneyMoney m_total; /** * Hold the last valid size of the net worth graph * for the times when the needed size can't be computed. */ QSize m_netWorthGraphLastValidSize; /** * daily forecast balance of accounts */ QMap m_accountList; }; /** * @brief Converts a QPixmap to an data URI scheme * * According to RFC 2397 * * @param pixmap Source to convert * @return full data URI */ QString QPixmapToDataUri(const QPixmap& pixmap) { QImage image(pixmap.toImage()); QByteArray byteArray; QBuffer buffer(&byteArray); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "PNG"); // writes the image in PNG format inside the buffer return QLatin1String("data:image/png;base64,") + QString(byteArray.toBase64()); } KHomeView::KHomeView(QWidget *parent) : KMyMoneyViewBase(parent), d(new Private) { } KHomeView::~KHomeView() { // if user wants to remember the font size, store it here if (KMyMoneyGlobalSettings::rememberZoomFactor()) { KMyMoneyGlobalSettings::setZoomFactor(d->m_view->zoomFactor()); KMyMoneyGlobalSettings::self()->save(); } delete d; } void KHomeView::init() { d->m_needLoad = false; auto vbox = new QVBoxLayout(this); setLayout(vbox); vbox->setSpacing(6); vbox->setMargin(0); #ifdef ENABLE_WEBENGINE d->m_view = new QWebEngineView(this); #else d->m_view = new KWebView(this); #endif d->m_view->setPage(new MyQWebEnginePage(d->m_view)); vbox->addWidget(d->m_view); d->m_view->setHtml(KWelcomePage::welcomePage(), QUrl("file://")); #ifdef ENABLE_WEBENGINE connect(d->m_view->page(), &QWebEnginePage::urlChanged, this, &KHomeView::slotOpenUrl); #else d->m_view->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); connect(d->m_view->page(), &KWebPage::linkClicked, this, &KHomeView::slotOpenUrl); #endif } void KHomeView::wheelEvent(QWheelEvent* event) { // Zoom text on Ctrl + Scroll if (event->modifiers() & Qt::CTRL) { qreal factor = d->m_view->zoomFactor(); if (event->delta() > 0) factor += 0.1; else if (event->delta() < 0) factor -= 0.1; d->m_view->setZoomFactor(factor); event->accept(); return; } } void KHomeView::slotLoadView() { d->m_needReload = true; if (isVisible()) { loadView(); d->m_needReload = false; } } void KHomeView::showEvent(QShowEvent* event) { if (d->m_needLoad) init(); emit aboutToShow(View::Home); if (d->m_needReload) { loadView(); d->m_needReload = false; } QWidget::showEvent(event); } void KHomeView::slotPrintView() { if (d->m_view) { m_currentPrinter = new QPrinter(); QPrintDialog *dialog = new QPrintDialog(m_currentPrinter, this); dialog->setWindowTitle(QString()); if (dialog->exec() != QDialog::Accepted) { delete m_currentPrinter; m_currentPrinter = nullptr; return; } #ifdef ENABLE_WEBENGINE d->m_view->page()->print(m_currentPrinter, [=] (bool) {delete m_currentPrinter; m_currentPrinter = nullptr;}); #else d->m_view->print(m_currentPrinter); #endif } } void KHomeView::loadView() { d->m_view->setZoomFactor(KMyMoneyGlobalSettings::zoomFactor()); QList list; MyMoneyFile::instance()->accountList(list); if (list.count() == 0) { d->m_view->setHtml(KWelcomePage::welcomePage(), QUrl("file://")); } else { //clear the forecast flag so it will be reloaded d->m_forecast.setForecastDone(false); const QString filename = QStandardPaths::locate(QStandardPaths::AppConfigLocation, "html/kmymoney.css"); QString header = QString("\n\n").arg(QUrl::fromLocalFile(filename).url()); header += KMyMoneyUtils::variableCSS(); header += "\n"; QString footer = "\n"; d->m_html.clear(); d->m_html += header; d->m_html += QString("
%1
").arg(i18n("Your Financial Summary")); QStringList settings = KMyMoneyGlobalSettings::itemList(); QStringList::ConstIterator it; for (it = settings.constBegin(); it != settings.constEnd(); ++it) { int option = (*it).toInt(); if (option > 0) { switch (option) { case 1: // payments showPayments(); break; case 2: // preferred accounts showAccounts(Preferred, i18n("Preferred Accounts")); break; case 3: // payment accounts // Check if preferred accounts are shown separately if (settings.contains("2")) { showAccounts(static_cast(Payment | Preferred), i18n("Payment Accounts")); } else { showAccounts(Payment, i18n("Payment Accounts")); } break; case 4: // favorite reports showFavoriteReports(); break; case 5: // forecast showForecast(); break; case 6: // net worth graph over all accounts showNetWorthGraph(); break; case 8: // assets and liabilities showAssetsLiabilities(); break; case 9: // budget showBudget(); break; case 10: // cash flow summary showCashFlowSummary(); break; } d->m_html += "
 
\n"; } } d->m_html += "
"; d->m_html += link(VIEW_WELCOME, QString()) + i18n("Show KMyMoney welcome page") + linkend(); d->m_html += "
"; d->m_html += "
"; d->m_html += footer; d->m_view->setHtml(d->m_html, QUrl("file://")); } } void KHomeView::showNetWorthGraph() { d->m_html += QString("
%1
\n
 
\n").arg(i18n("Net Worth Forecast")); MyMoneyReport reportCfg = MyMoneyReport( MyMoneyReport::eAssetLiability, MyMoneyReport::eMonths, TransactionFilter::Date::UserDefined, // overridden by the setDateFilter() call below MyMoneyReport::eDetailTotal, i18n("Net Worth Forecast"), i18n("Generated Report")); reportCfg.setChartByDefault(true); reportCfg.setChartCHGridLines(false); reportCfg.setChartSVGridLines(false); reportCfg.setChartDataLabels(false); reportCfg.setChartType(MyMoneyReport::eChartLine); reportCfg.setIncludingSchedules(false); reportCfg.addAccountGroup(Account::Asset); reportCfg.addAccountGroup(Account::Liability); reportCfg.setColumnsAreDays(true); reportCfg.setConvertCurrency(true); reportCfg.setIncludingForecast(true); reportCfg.setDateFilter(QDate::currentDate(), QDate::currentDate().addDays(+ 90)); reports::PivotTable table(reportCfg); reports::KReportChartView* chartWidget = new reports::KReportChartView(0); table.drawChart(*chartWidget); // Adjust the size QSize netWorthGraphSize = KHomeView::size(); netWorthGraphSize -= QSize(80, 30); // consider the computed size valid only if it's smaller on both axes that the applications size if (netWorthGraphSize.width() < kmymoney->width() || netWorthGraphSize.height() < kmymoney->height()) { d->m_netWorthGraphLastValidSize = netWorthGraphSize; } chartWidget->resize(d->m_netWorthGraphLastValidSize); //save the chart to an image QString chart = QPixmapToDataUri(QPixmap::grabWidget(chartWidget->coordinatePlane()->parent())); d->m_html += QString(""); d->m_html += QString(""); d->m_html += QString("").arg(chart); d->m_html += QString(""); d->m_html += QString("
\"Networth\"
"); //delete the widget since we no longer need it delete chartWidget; } void KHomeView::showPayments() { MyMoneyFile* file = MyMoneyFile::instance(); QList overdues; QList schedule; int i = 0; //if forecast has not been executed yet, do it. if (!d->m_forecast.isForecastDone()) doForecast(); schedule = file->scheduleList(QString(), Schedule::Type::Any, Schedule::Occurrence::Any, Schedule::PaymentType::Any, QDate::currentDate(), QDate::currentDate().addMonths(1), false); overdues = file->scheduleList(QString(), Schedule::Type::Any, Schedule::Occurrence::Any, Schedule::PaymentType::Any, QDate(), QDate(), true); if (schedule.empty() && overdues.empty()) return; // HACK // Remove the finished schedules QList::Iterator d_it; //regular schedules d_it = schedule.begin(); while (d_it != schedule.end()) { if ((*d_it).isFinished()) { d_it = schedule.erase(d_it); continue; } ++d_it; } //overdue schedules d_it = overdues.begin(); while (d_it != overdues.end()) { if ((*d_it).isFinished()) { d_it = overdues.erase(d_it); continue; } ++d_it; } d->m_html += "
"; d->m_html += QString("
%1
\n").arg(i18n("Payments")); if (!overdues.isEmpty()) { d->m_html += "
 
\n"; qSort(overdues); QList::Iterator it; QList::Iterator it_f; d->m_html += ""; d->m_html += QString("\n").arg(showColoredAmount(i18n("Overdue payments"), true)); d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; for (it = overdues.begin(); it != overdues.end(); ++it) { // determine number of overdue payments int cnt = (*it).transactionsRemainingUntil(QDate::currentDate().addDays(-1)); d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); showPaymentEntry(*it, cnt); d->m_html += ""; } d->m_html += "
%1
"; d->m_html += i18n("Date"); d->m_html += ""; d->m_html += i18n("Schedule"); d->m_html += ""; d->m_html += i18n("Account"); d->m_html += ""; d->m_html += i18n("Amount"); d->m_html += ""; d->m_html += i18n("Balance after"); d->m_html += "
"; } if (!schedule.isEmpty()) { qSort(schedule); // Extract todays payments if any QList todays; QList::Iterator t_it; for (t_it = schedule.begin(); t_it != schedule.end();) { if ((*t_it).adjustedNextDueDate() == QDate::currentDate()) { todays.append(*t_it); (*t_it).setNextDueDate((*t_it).nextPayment(QDate::currentDate())); // if adjustedNextDueDate is still currentDate then remove it from // scheduled payments if ((*t_it).adjustedNextDueDate() == QDate::currentDate()) { t_it = schedule.erase(t_it); continue; } } ++t_it; } if (todays.count() > 0) { d->m_html += "
 
\n"; d->m_html += ""; d->m_html += QString("\n").arg(i18n("Today's due payments")); d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; for (t_it = todays.begin(); t_it != todays.end(); ++t_it) { d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); showPaymentEntry(*t_it); d->m_html += ""; } d->m_html += "
%1
"; d->m_html += i18n("Date"); d->m_html += ""; d->m_html += i18n("Schedule"); d->m_html += ""; d->m_html += i18n("Account"); d->m_html += ""; d->m_html += i18n("Amount"); d->m_html += ""; d->m_html += i18n("Balance after"); d->m_html += "
"; } if (!schedule.isEmpty()) { d->m_html += "
 
\n"; QList::Iterator it; d->m_html += ""; d->m_html += QString("\n").arg(i18n("Future payments")); d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; // show all or the first 6 entries int cnt; cnt = (d->m_showAllSchedules) ? -1 : 6; bool needMoreLess = d->m_showAllSchedules; QDate lastDate = QDate::currentDate().addMonths(1); qSort(schedule); do { it = schedule.begin(); if (it == schedule.end()) break; // if the next due date is invalid (schedule is finished) // we remove it from the list QDate nextDate = (*it).nextDueDate(); if (!nextDate.isValid()) { schedule.erase(it); continue; } if (nextDate > lastDate) break; if (cnt == 0) { needMoreLess = true; break; } // in case we've shown the current recurrence as overdue, // we don't show it here again, but keep the schedule // as it might show up later in the list again if (!(*it).isOverdue()) { if (cnt > 0) --cnt; d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); showPaymentEntry(*it); d->m_html += ""; // for single occurrence we have reported everything so we // better get out of here. if ((*it).occurrence() == Schedule::Occurrence::Once) { schedule.erase(it); continue; } } // if nextPayment returns an invalid date, setNextDueDate will // just skip it, resulting in a loop // we check the resulting date and erase the schedule if invalid if (!((*it).nextPayment((*it).nextDueDate())).isValid()) { schedule.erase(it); continue; } (*it).setNextDueDate((*it).nextPayment((*it).nextDueDate())); qSort(schedule); } while (1); if (needMoreLess) { d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); d->m_html += ""; d->m_html += ""; } d->m_html += "
%1
"; d->m_html += i18n("Date"); d->m_html += ""; d->m_html += i18n("Schedule"); d->m_html += ""; d->m_html += i18n("Account"); d->m_html += ""; d->m_html += i18n("Amount"); d->m_html += ""; d->m_html += i18n("Balance after"); d->m_html += "
"; if (d->m_showAllSchedules) { d->m_html += link(VIEW_SCHEDULE, QString("?mode=%1").arg("reduced")) + i18nc("Less...", "Show fewer schedules on the list") + linkend(); } else { d->m_html += link(VIEW_SCHEDULE, QString("?mode=%1").arg("full")) + i18nc("More...", "Show more schedules on the list") + linkend(); } d->m_html += "
"; } } d->m_html += "
"; } void KHomeView::showPaymentEntry(const MyMoneySchedule& sched, int cnt) { QString tmp; MyMoneyFile* file = MyMoneyFile::instance(); try { MyMoneyAccount acc = sched.account(); if (!acc.id().isEmpty()) { MyMoneyTransaction t = sched.transaction(); // only show the entry, if it is still active if (!sched.isFinished()) { MyMoneySplit sp = t.splitByAccount(acc.id(), true); QString pathEnter = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::KeyEnter]).pixmap(QSize(16,16))); QString pathSkip = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::MediaSkipForward]).pixmap(QSize(16,16))); //show payment date tmp = QString("") + QLocale().toString(sched.adjustedNextDueDate(), QLocale::ShortFormat) + ""; if (!pathEnter.isEmpty()) tmp += link(VIEW_SCHEDULE, QString("?id=%1&mode=enter").arg(sched.id()), i18n("Enter schedule")) + QString("").arg(pathEnter) + linkend(); if (!pathSkip.isEmpty()) tmp += " " + link(VIEW_SCHEDULE, QString("?id=%1&mode=skip").arg(sched.id()), i18n("Skip schedule")) + QString("").arg(pathSkip) + linkend(); tmp += QString(" "); tmp += link(VIEW_SCHEDULE, QString("?id=%1&mode=edit").arg(sched.id()), i18n("Edit schedule")) + sched.name() + linkend(); //show quantity of payments overdue if any if (cnt > 1) tmp += i18np(" (%1 payment)", " (%1 payments)", cnt); //show account of the main split tmp += ""; tmp += QString(file->account(acc.id()).name()); //show amount of the schedule tmp += ""; const MyMoneySecurity& currency = MyMoneyFile::instance()->currency(acc.currencyId()); MyMoneyMoney payment = MyMoneyMoney(sp.value(t.commodity(), acc.currencyId()) * cnt); QString amount = MyMoneyUtils::formatMoney(payment, acc, currency); amount.replace(QChar(' '), " "); tmp += showColoredAmount(amount, payment.isNegative()); tmp += ""; //show balance after payments tmp += ""; QDate paymentDate = QDate(sched.adjustedNextDueDate()); MyMoneyMoney balanceAfter = forecastPaymentBalance(acc, payment, paymentDate); QString balance = MyMoneyUtils::formatMoney(balanceAfter, acc, currency); balance.replace(QChar(' '), " "); tmp += showColoredAmount(balance, balanceAfter.isNegative()); tmp += ""; // qDebug("paymentEntry = '%s'", tmp.toLatin1()); d->m_html += tmp; } } } catch (const MyMoneyException &e) { qDebug("Unable to display schedule entry: %s", qPrintable(e.what())); } } void KHomeView::showAccounts(KHomeView::paymentTypeE type, const QString& header) { MyMoneyFile* file = MyMoneyFile::instance(); int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); QList accounts; bool showClosedAccounts = kmymoney->isActionToggled(Action::ViewShowAll); // get list of all accounts file->accountList(accounts); for (QList::Iterator it = accounts.begin(); it != accounts.end();) { bool removeAccount = false; if (!(*it).isClosed() || showClosedAccounts) { switch ((*it).accountType()) { case Account::Expense: case Account::Income: // never show a category account // Note: This might be different in a future version when // the homepage also shows category based information removeAccount = true; break; // Asset and Liability accounts are only shown if they // have the preferred flag set case Account::Asset: case Account::Liability: case Account::Investment: // if preferred accounts are requested, then keep in list if ((*it).value("PreferredAccount") != "Yes" || (type & Preferred) == 0) { removeAccount = true; } break; // Check payment accounts. If payment and preferred is selected, // then always show them. If only payment is selected, then // show only if preferred flag is not set. case Account::Checkings: case Account::Savings: case Account::Cash: case Account::CreditCard: switch (type & (Payment | Preferred)) { case Payment: if ((*it).value("PreferredAccount") == "Yes") removeAccount = true; break; case Preferred: if ((*it).value("PreferredAccount") != "Yes") removeAccount = true; break; case Payment | Preferred: break; default: removeAccount = true; break; } break; // filter all accounts that are not used on homepage views default: removeAccount = true; break; } } else if ((*it).isClosed() || (*it).isInvest()) { // don't show if closed or a stock account removeAccount = true; } if (removeAccount) it = accounts.erase(it); else ++it; } if (!accounts.isEmpty()) { // sort the accounts by name qStableSort(accounts.begin(), accounts.end(), accountNameLess); QString tmp; int i = 0; tmp = "
" + header + "
\n
 
\n"; d->m_html += tmp; d->m_html += ""; d->m_html += ""; if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) { QString pathStatusHeader = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::Download]).pixmap(QSize(16,16))); d->m_html += QString("").arg(pathStatusHeader); } d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions()) d->m_html += QString(""); if (KMyMoneyGlobalSettings::showCountOfClearedTransactions()) d->m_html += QString(""); if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) d->m_html += QString(""); d->m_html += ""; //only show limit info if user chose to do so if (KMyMoneyGlobalSettings::showLimitInfo()) { d->m_html += ""; } d->m_html += ""; d->m_total = 0; QList::const_iterator it_m; for (it_m = accounts.constBegin(); it_m != accounts.constEnd(); ++it_m) { d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); showAccountEntry(*it_m); d->m_html += ""; } d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); QString amount = d->m_total.formatMoney(file->baseCurrency().tradingSymbol(), prec); if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) d->m_html += ""; d->m_html += QString("").arg(i18n("Total")); if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions()) d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfClearedTransactions()) d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) d->m_html += ""; d->m_html += QString("").arg(showColoredAmount(amount, d->m_total.isNegative())); d->m_html += "
"; d->m_html += i18n("Account"); d->m_html += "!MC!R"; d->m_html += i18n("Current Balance"); d->m_html += ""; d->m_html += i18n("To Minimum Balance / Maximum Credit"); d->m_html += "
%1%1
"; } } void KHomeView::showAccountEntry(const MyMoneyAccount& acc) { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneySecurity currency = file->currency(acc.currencyId()); MyMoneyMoney value; bool showLimit = KMyMoneyGlobalSettings::showLimitInfo(); if (acc.accountType() == Account::Investment) { //investment accounts show the balances of all its subaccounts value = investmentBalance(acc); //investment accounts have no minimum balance showAccountEntry(acc, value, MyMoneyMoney(), showLimit); } else { //get balance for normal accounts value = file->balance(acc.id(), QDate::currentDate()); if (acc.currencyId() != file->baseCurrency().id()) { ReportAccount repAcc = ReportAccount(acc.id()); MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); MyMoneyMoney baseValue = value * curPrice; baseValue = baseValue.convert(file->baseCurrency().smallestAccountFraction()); d->m_total += baseValue; } else { d->m_total += value; } //if credit card or checkings account, show maximum credit if (acc.accountType() == Account::CreditCard || acc.accountType() == Account::Checkings) { QString maximumCredit = acc.value("maxCreditAbsolute"); if (maximumCredit.isEmpty()) { maximumCredit = acc.value("minBalanceAbsolute"); } MyMoneyMoney maxCredit = MyMoneyMoney(maximumCredit); showAccountEntry(acc, value, value - maxCredit, showLimit); } else { //otherwise use minimum balance QString minimumBalance = acc.value("minBalanceAbsolute"); MyMoneyMoney minBalance = MyMoneyMoney(minimumBalance); showAccountEntry(acc, value, value - minBalance, showLimit); } } } void KHomeView::showAccountEntry(const MyMoneyAccount& acc, const MyMoneyMoney& value, const MyMoneyMoney& valueToMinBal, const bool showMinBal) { MyMoneyFile* file = MyMoneyFile::instance(); QString tmp; MyMoneySecurity currency = file->currency(acc.currencyId()); QString amount; QString amountToMinBal; //format amounts amount = MyMoneyUtils::formatMoney(value, acc, currency); amount.replace(QChar(' '), " "); if (showMinBal) { amountToMinBal = MyMoneyUtils::formatMoney(valueToMinBal, acc, currency); amountToMinBal.replace(QChar(' '), " "); } QString cellStatus, cellCounts, pathOK, pathTODO, pathNotOK; if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) { //show account's online-status pathOK = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::DialogOKApply]).pixmap(QSize(16,16))); pathTODO = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::MailReceive]).pixmap(QSize(16,16))); pathNotOK = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::DialogCancel]).pixmap(QSize(16,16))); if (acc.value("lastImportedTransactionDate").isEmpty() || acc.value("lastStatementBalance").isEmpty()) cellStatus = '-'; else if (file->hasMatchingOnlineBalance(acc)) { if (file->hasNewerTransaction(acc.id(), QDate::fromString(acc.value("lastImportedTransactionDate"), Qt::ISODate))) cellStatus = QString("").arg(pathTODO); else cellStatus = QString("").arg(pathOK); } else cellStatus = QString("").arg(pathNotOK); tmp = QString("%1").arg(cellStatus); } tmp += QString("") + link(VIEW_LEDGER, QString("?id=%1").arg(acc.id())) + acc.name() + linkend() + ""; int countNotMarked = 0, countCleared = 0, countNotReconciled = 0; QString countStr; if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions() || KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) countNotMarked = file->countTransactionsWithSpecificReconciliationState(acc.id(), TransactionFilter::State::NotReconciled); if (KMyMoneyGlobalSettings::showCountOfClearedTransactions() || KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) countCleared = file->countTransactionsWithSpecificReconciliationState(acc.id(), TransactionFilter::State::Cleared); if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) countNotReconciled = countNotMarked + countCleared; if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions()) { if (countNotMarked) countStr = QString("%1").arg(countNotMarked); else countStr = '-'; tmp += QString("%1").arg(countStr); } if (KMyMoneyGlobalSettings::showCountOfClearedTransactions()) { if (countCleared) countStr = QString("%1").arg(countCleared); else countStr = '-'; tmp += QString("%1").arg(countStr); } if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) { if (countNotReconciled) countStr = QString("%1").arg(countNotReconciled); else countStr = '-'; tmp += QString("%1").arg(countStr); } //show account balance tmp += QString("%1").arg(showColoredAmount(amount, value.isNegative())); //show minimum balance column if requested if (showMinBal) { //if it is an investment, show minimum balance empty if (acc.accountType() == Account::Investment) { tmp += QString(" "); } else { //show minimum balance entry tmp += QString("%1").arg(showColoredAmount(amountToMinBal, valueToMinBal.isNegative())); } } // qDebug("accountEntry = '%s'", tmp.toLatin1()); d->m_html += tmp; } MyMoneyMoney KHomeView::investmentBalance(const MyMoneyAccount& acc) { auto file = MyMoneyFile::instance(); auto value = file->balance(acc.id(), QDate::currentDate()); foreach (const auto accountID, acc.accountList()) { auto stock = file->account(accountID); if (!stock.isClosed()) { try { MyMoneyMoney val; MyMoneyMoney balance = file->balance(stock.id(), QDate::currentDate()); MyMoneySecurity security = file->security(stock.currencyId()); const MyMoneyPrice &price = file->price(stock.currencyId(), security.tradingCurrency()); val = (balance * price.rate(security.tradingCurrency())).convertPrecision(security.pricePrecision()); // adjust value of security to the currency of the account MyMoneySecurity accountCurrency = file->currency(acc.currencyId()); val = val * file->price(security.tradingCurrency(), accountCurrency.id()).rate(accountCurrency.id()); val = val.convert(acc.fraction()); value += val; } catch (const MyMoneyException &e) { qWarning("%s", qPrintable(QString("cannot convert stock balance of %1 to base currency: %2").arg(stock.name(), e.what()))); } } } return value; } void KHomeView::showFavoriteReports() { QList reports = MyMoneyFile::instance()->reportList(); if (!reports.isEmpty()) { bool firstTime = 1; int row = 0; QList::const_iterator it_report = reports.constBegin(); while (it_report != reports.constEnd()) { if ((*it_report).isFavorite()) { if (firstTime) { d->m_html += QString("
%1
\n
 
\n").arg(i18n("Favorite Reports")); d->m_html += ""; d->m_html += ""; firstTime = false; } d->m_html += QString("") .arg(row++ & 0x01 ? "even" : "odd") .arg(link(VIEW_REPORTS, QString("?id=%1").arg((*it_report).id()))) .arg((*it_report).name()) .arg(linkend()) .arg((*it_report).comment()); } ++it_report; } if (!firstTime) d->m_html += "
"; d->m_html += i18n("Report"); d->m_html += ""; d->m_html += i18n("Comment"); d->m_html += "
%2%3%4%5
"; } } void KHomeView::showForecast() { MyMoneyFile* file = MyMoneyFile::instance(); QList accList; //if forecast has not been executed yet, do it. if (!d->m_forecast.isForecastDone()) doForecast(); accList = d->m_forecast.accountList(); if (accList.count() > 0) { // sort the accounts by name qStableSort(accList.begin(), accList.end(), accountNameLess); int i = 0; int colspan = 1; //get begin day int beginDay = QDate::currentDate().daysTo(d->m_forecast.beginForecastDate()); //if begin day is today skip to next cycle if (beginDay == 0) beginDay = d->m_forecast.accountsCycle(); // Now output header d->m_html += QString("
%1
\n
 
\n").arg(i18n("%1 Day Forecast", d->m_forecast.forecastDays())); d->m_html += ""; d->m_html += ""; int colWidth = 55 / (d->m_forecast.forecastDays() / d->m_forecast.accountsCycle()); for (i = 0; (i*d->m_forecast.accountsCycle() + beginDay) <= d->m_forecast.forecastDays(); ++i) { d->m_html += QString(""; colspan++; } d->m_html += ""; // Now output entries i = 0; QList::ConstIterator it_account; for (it_account = accList.constBegin(); it_account != accList.constEnd(); ++it_account) { //MyMoneyAccount acc = (*it_n); d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); d->m_html += QString(""; int dropZero = -1; //account dropped below zero int dropMinimum = -1; //account dropped below minimum balance QString minimumBalance = (*it_account).value("minimumBalance"); MyMoneyMoney minBalance = MyMoneyMoney(minimumBalance); MyMoneySecurity currency; MyMoneyMoney forecastBalance; //change account to deep currency if account is an investment if ((*it_account).isInvest()) { MyMoneySecurity underSecurity = file->security((*it_account).currencyId()); currency = file->security(underSecurity.tradingCurrency()); } else { currency = file->security((*it_account).currencyId()); } for (int f = beginDay; f <= d->m_forecast.forecastDays(); f += d->m_forecast.accountsCycle()) { forecastBalance = d->m_forecast.forecastBalance(*it_account, QDate::currentDate().addDays(f)); QString amount; amount = MyMoneyUtils::formatMoney(forecastBalance, *it_account, currency); amount.replace(QChar(' '), " "); d->m_html += QString("").arg(showColoredAmount(amount, forecastBalance.isNegative())); } d->m_html += ""; //Check if the account is going to be below zero or below the minimal balance in the forecast period //Check if the account is going to be below minimal balance dropMinimum = d->m_forecast.daysToMinimumBalance(*it_account); //Check if the account is going to be below zero in the future dropZero = d->m_forecast.daysToZeroBalance(*it_account); // spit out possible warnings QString msg; // if a minimum balance has been specified, an appropriate warning will // only be shown, if the drop below 0 is on a different day or not present if (dropMinimum != -1 && !minBalance.isZero() && (dropMinimum < dropZero || dropZero == -1)) { switch (dropMinimum) { case -1: break; case 0: msg = i18n("The balance of %1 is below the minimum balance %2 today.", (*it_account).name(), MyMoneyUtils::formatMoney(minBalance, *it_account, currency)); msg = showColoredAmount(msg, true); break; default: msg = i18np("The balance of %2 will drop below the minimum balance %3 in %1 day.", "The balance of %2 will drop below the minimum balance %3 in %1 days.", dropMinimum - 1, (*it_account).name(), MyMoneyUtils::formatMoney(minBalance, *it_account, currency)); msg = showColoredAmount(msg, true); break; } if (!msg.isEmpty()) { d->m_html += QString("").arg(msg).arg(colspan); } } // a drop below zero is always shown msg.clear(); switch (dropZero) { case -1: break; case 0: if ((*it_account).accountGroup() == Account::Asset) { msg = i18n("The balance of %1 is below %2 today.", (*it_account).name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), *it_account, currency)); msg = showColoredAmount(msg, true); break; } if ((*it_account).accountGroup() == Account::Liability) { msg = i18n("The balance of %1 is above %2 today.", (*it_account).name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), *it_account, currency)); break; } break; default: if ((*it_account).accountGroup() == Account::Asset) { msg = i18np("The balance of %2 will drop below %3 in %1 day.", "The balance of %2 will drop below %3 in %1 days.", dropZero, (*it_account).name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), *it_account, currency)); msg = showColoredAmount(msg, true); break; } if ((*it_account).accountGroup() == Account::Liability) { msg = i18np("The balance of %2 will raise above %3 in %1 day.", "The balance of %2 will raise above %3 in %1 days.", dropZero, (*it_account).name(), MyMoneyUtils::formatMoney(MyMoneyMoney(), *it_account, currency)); break; } } if (!msg.isEmpty()) { d->m_html += QString("").arg(msg).arg(colspan); } } d->m_html += "
"; d->m_html += i18n("Account"); d->m_html += "").arg(colWidth); d->m_html += i18ncp("Forecast days", "%1 day", "%1 days", i * d->m_forecast.accountsCycle() + beginDay); d->m_html += "
") + link(VIEW_LEDGER, QString("?id=%1").arg((*it_account).id())) + (*it_account).name() + linkend() + "").arg(colWidth); d->m_html += QString("%1
%1
%1
"; } } QString KHomeView::link(const QString& view, const QString& query, const QString& _title) const { QString titlePart; QString title(_title); if (!title.isEmpty()) titlePart = QString(" title=\"%1\"").arg(title.replace(QLatin1Char(' '), " ")); return QString("").arg(view, query, titlePart); } QString KHomeView::linkend() const { return QStringLiteral(""); } void KHomeView::slotOpenUrl(const QUrl &url) { QString protocol = url.scheme(); QString view = url.fileName(); if (view.isEmpty()) return; QUrlQuery query(url); QString id = query.queryItemValue("id"); QString mode = query.queryItemValue("mode"); if (protocol == QLatin1String("http")) { QDesktopServices::openUrl(url); } else if (protocol == QLatin1String("mailto")) { QDesktopServices::openUrl(url); } else { KXmlGuiWindow* mw = KMyMoneyUtils::mainWindow(); Q_CHECK_PTR(mw); if (view == VIEW_LEDGER) { emit ledgerSelected(id, QString()); } else if (view == VIEW_SCHEDULE) { if (mode == QLatin1String("enter")) { emit scheduleSelected(id); QTimer::singleShot(0, mw->actionCollection()->action(kmymoney->s_Actions[Action::ScheduleEnter]), SLOT(trigger())); } else if (mode == QLatin1String("edit")) { emit scheduleSelected(id); QTimer::singleShot(0, mw->actionCollection()->action(kmymoney->s_Actions[Action::ScheduleEdit]), SLOT(trigger())); } else if (mode == QLatin1String("skip")) { emit scheduleSelected(id); QTimer::singleShot(0, mw->actionCollection()->action(kmymoney->s_Actions[Action::ScheduleSkip]), SLOT(trigger())); } else if (mode == QLatin1String("full")) { d->m_showAllSchedules = true; loadView(); } else if (mode == QLatin1String("reduced")) { d->m_showAllSchedules = false; loadView(); } } else if (view == VIEW_REPORTS) { emit reportSelected(id); } else if (view == VIEW_WELCOME) { if (mode == QLatin1String("whatsnew")) d->m_view->setHtml(KWelcomePage::whatsNewPage(), QUrl("file://")); else d->m_view->setHtml(KWelcomePage::welcomePage(), QUrl("file://")); } else if (view == QLatin1String("action")) { QTimer::singleShot(0, mw->actionCollection()->action(id), SLOT(trigger())); } else if (view == VIEW_HOME) { QList list; MyMoneyFile::instance()->accountList(list); if (list.count() == 0) { KMessageBox::information(this, i18n("Before KMyMoney can give you detailed information about your financial status, you need to create at least one account. Until then, KMyMoney shows the welcome page instead.")); } loadView(); } else { qDebug("Unknown view '%s' in KHomeView::slotOpenURL()", qPrintable(view)); } } } void KHomeView::showAssetsLiabilities() { QList accounts; QList::ConstIterator it; QList assets; QList liabilities; MyMoneyMoney netAssets; MyMoneyMoney netLiabilities; QString fontStart, fontEnd; MyMoneyFile* file = MyMoneyFile::instance(); int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); int i = 0; // get list of all accounts file->accountList(accounts); for (it = accounts.constBegin(); it != accounts.constEnd();) { if (!(*it).isClosed()) { switch ((*it).accountType()) { // group all assets into one list but make sure that investment accounts always show up case Account::Investment: assets << *it; break; case Account::Checkings: case Account::Savings: case Account::Cash: case Account::Asset: case Account::AssetLoan: // list account if it's the last in the hierarchy or has transactions in it if ((*it).accountList().isEmpty() || (file->transactionCount((*it).id()) > 0)) { assets << *it; } break; // group the liabilities into the other case Account::CreditCard: case Account::Liability: case Account::Loan: // list account if it's the last in the hierarchy or has transactions in it if ((*it).accountList().isEmpty() || (file->transactionCount((*it).id()) > 0)) { liabilities << *it; } break; default: break; } } ++it; } //only do it if we have assets or liabilities account if (assets.count() > 0 || liabilities.count() > 0) { // sort the accounts by name qStableSort(assets.begin(), assets.end(), accountNameLess); qStableSort(liabilities.begin(), liabilities.end(), accountNameLess); QString statusHeader; if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) { QString pathStatusHeader; pathStatusHeader = QPixmapToDataUri(QIcon::fromTheme(g_Icons[Icon::ViewOutbox]).pixmap(QSize(16,16))); statusHeader = QString("").arg(pathStatusHeader); } //print header d->m_html += "
" + i18n("Assets and Liabilities Summary") + "
\n
 
\n"; d->m_html += ""; //column titles d->m_html += ""; if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) { d->m_html += ""; } d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions()) d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfClearedTransactions()) d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) d->m_html += ""; d->m_html += ""; //intermediate row to separate both columns d->m_html += ""; if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) { d->m_html += ""; } d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions()) d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfClearedTransactions()) d->m_html += ""; if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) d->m_html += ""; d->m_html += ""; QString placeHolder_Status, placeHolder_Counts; if (KMyMoneyGlobalSettings::showBalanceStatusOfOnlineAccounts()) placeHolder_Status = ""; if (KMyMoneyGlobalSettings::showCountOfUnmarkedTransactions()) placeHolder_Counts = ""; if (KMyMoneyGlobalSettings::showCountOfClearedTransactions()) placeHolder_Counts += ""; if (KMyMoneyGlobalSettings::showCountOfNotReconciledTransactions()) placeHolder_Counts += ""; //get asset and liability accounts QList::const_iterator asset_it = assets.constBegin(); QList::const_iterator liabilities_it = liabilities.constBegin(); for (; asset_it != assets.constEnd() || liabilities_it != liabilities.constEnd();) { d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); //write an asset account if we still have any if (asset_it != assets.constEnd()) { MyMoneyMoney value; //investment accounts consolidate the balance of its subaccounts if ((*asset_it).accountType() == Account::Investment) { value = investmentBalance(*asset_it); } else { value = MyMoneyFile::instance()->balance((*asset_it).id(), QDate::currentDate()); } //calculate balance for foreign currency accounts if ((*asset_it).currencyId() != file->baseCurrency().id()) { ReportAccount repAcc = ReportAccount((*asset_it).id()); MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); MyMoneyMoney baseValue = value * curPrice; baseValue = baseValue.convert(10000); netAssets += baseValue; } else { netAssets += value; } //show the account without minimum balance showAccountEntry(*asset_it, value, MyMoneyMoney(), false); ++asset_it; } else { //write a white space if we don't d->m_html += QString("%1%2").arg(placeHolder_Status).arg(placeHolder_Counts); } //leave the intermediate column empty d->m_html += ""; //write a liability account if (liabilities_it != liabilities.constEnd()) { MyMoneyMoney value; value = MyMoneyFile::instance()->balance((*liabilities_it).id(), QDate::currentDate()); //calculate balance if foreign currency if ((*liabilities_it).currencyId() != file->baseCurrency().id()) { ReportAccount repAcc = ReportAccount((*liabilities_it).id()); MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); MyMoneyMoney baseValue = value * curPrice; baseValue = baseValue.convert(10000); netLiabilities += baseValue; } else { netLiabilities += value; } //show the account without minimum balance showAccountEntry(*liabilities_it, value, MyMoneyMoney(), false); ++liabilities_it; } else { //leave the space empty if we run out of liabilities d->m_html += QString("%1%2").arg(placeHolder_Status).arg(placeHolder_Counts); } d->m_html += ""; } //calculate net worth MyMoneyMoney netWorth = netAssets + netLiabilities; //format assets, liabilities and net worth QString amountAssets = netAssets.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountLiabilities = netLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountNetWorth = netWorth.formatMoney(file->baseCurrency().tradingSymbol(), prec); amountAssets.replace(QChar(' '), " "); amountLiabilities.replace(QChar(' '), " "); amountNetWorth.replace(QChar(' '), " "); d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); //print total for assets d->m_html += QString("%1%3").arg(placeHolder_Status).arg(i18n("Total Assets")).arg(placeHolder_Counts).arg(showColoredAmount(amountAssets, netAssets.isNegative())); //leave the intermediate column empty d->m_html += ""; //print total liabilities d->m_html += QString("%1%3").arg(placeHolder_Status).arg(i18n("Total Liabilities")).arg(placeHolder_Counts).arg(showColoredAmount(amountLiabilities, netLiabilities.isNegative())); d->m_html += ""; //print net worth d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); d->m_html += QString("%1%2").arg(placeHolder_Status).arg(placeHolder_Counts); d->m_html += QString("%1%3").arg(placeHolder_Status).arg(i18n("Net Worth")).arg(placeHolder_Counts).arg(showColoredAmount(amountNetWorth, netWorth.isNegative())); d->m_html += ""; d->m_html += "
"; d->m_html += statusHeader; d->m_html += ""; d->m_html += i18n("Asset Accounts"); d->m_html += "!MC!R"; d->m_html += i18n("Current Balance"); d->m_html += ""; d->m_html += statusHeader; d->m_html += ""; d->m_html += i18n("Liability Accounts"); d->m_html += "!MC!R"; d->m_html += i18n("Current Balance"); d->m_html += "
%2%4%2%4
%2%4
"; d->m_html += "
"; } } void KHomeView::showBudget() { MyMoneyFile* file = MyMoneyFile::instance(); if (file->countBudgets()) { int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); bool isOverrun = false; int i = 0; //config report just like "Monthly Budgeted vs Actual MyMoneyReport reportCfg = MyMoneyReport( MyMoneyReport::eBudgetActual, MyMoneyReport::eMonths, TransactionFilter::Date::CurrentMonth, MyMoneyReport::eDetailAll, i18n("Monthly Budgeted vs. Actual"), i18n("Generated Report")); reportCfg.setBudget("Any", true); reports::PivotTable table(reportCfg); PivotGrid grid = table.grid(); //div header d->m_html += "
" + i18n("Budget") + "
\n
 
\n"; //display budget summary d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += QString(""); MyMoneyMoney totalBudgetValue = grid.m_total[eBudget].m_total; MyMoneyMoney totalActualValue = grid.m_total[eActual].m_total; MyMoneyMoney totalBudgetDiffValue = grid.m_total[eBudgetDiff].m_total; QString totalBudgetAmount = totalBudgetValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString totalActualAmount = totalActualValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString totalBudgetDiffAmount = totalBudgetDiffValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); d->m_html += QString("").arg(showColoredAmount(totalBudgetAmount, totalBudgetValue.isNegative())); d->m_html += QString("").arg(showColoredAmount(totalActualAmount, totalActualValue.isNegative())); d->m_html += QString("").arg(showColoredAmount(totalBudgetDiffAmount, totalBudgetDiffValue.isNegative())); d->m_html += ""; d->m_html += "
"; d->m_html += i18n("Current Month Summary"); d->m_html += "
"; d->m_html += i18n("Budgeted"); d->m_html += ""; d->m_html += i18n("Actual"); d->m_html += ""; d->m_html += i18n("Difference"); d->m_html += "
%1%1%1
"; //budget overrun d->m_html += "
 
\n"; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; PivotGrid::iterator it_outergroup = grid.begin(); while (it_outergroup != grid.end()) { i = 0; PivotOuterGroup::iterator it_innergroup = (*it_outergroup).begin(); while (it_innergroup != (*it_outergroup).end()) { PivotInnerGroup::iterator it_row = (*it_innergroup).begin(); while (it_row != (*it_innergroup).end()) { //column number is 1 because the report includes only current month if (it_row.value()[eBudgetDiff].value(1).isNegative()) { //get report account to get the name later ReportAccount rowname = it_row.key(); //write the outergroup if it is the first row of outergroup being shown if (i == 0) { d->m_html += ""; d->m_html += QString("").arg(MyMoneyAccount::accountTypeToString(rowname.accountType())); d->m_html += ""; } d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); //get values from grid MyMoneyMoney actualValue = it_row.value()[eActual][1]; MyMoneyMoney budgetValue = it_row.value()[eBudget][1]; MyMoneyMoney budgetDiffValue = it_row.value()[eBudgetDiff][1]; //format amounts QString actualAmount = actualValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString budgetAmount = budgetValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString budgetDiffAmount = budgetDiffValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); //account name d->m_html += QString(""; //show amounts d->m_html += QString("").arg(showColoredAmount(budgetAmount, budgetValue.isNegative())); d->m_html += QString("").arg(showColoredAmount(actualAmount, actualValue.isNegative())); d->m_html += QString("").arg(showColoredAmount(budgetDiffAmount, budgetDiffValue.isNegative())); d->m_html += ""; //set the flag that there are overruns isOverrun = true; } ++it_row; } ++it_innergroup; } ++it_outergroup; } //if no negative differences are found, then inform that if (!isOverrun) { d->m_html += QString("").arg(i++ & 0x01 ? "even" : "odd"); d->m_html += QString("").arg(i18n("No Budget Categories have been overrun")); d->m_html += ""; } d->m_html += "
"; d->m_html += i18n("Budget Overruns"); d->m_html += "
"; d->m_html += i18n("Account"); d->m_html += ""; d->m_html += i18n("Budgeted"); d->m_html += ""; d->m_html += i18n("Actual"); d->m_html += ""; d->m_html += i18n("Difference"); d->m_html += "
%1
") + link(VIEW_LEDGER, QString("?id=%1").arg(rowname.id())) + rowname.name() + linkend() + "%1%1%1
%1
"; } } QString KHomeView::showColoredAmount(const QString& amount, bool isNegative) { if (isNegative) { //if negative, get the settings for negative numbers return QString("%2").arg(KMyMoneyGlobalSettings::schemeColor(SchemeColor::Negative).name(), amount); } //if positive, return the same string return amount; } void KHomeView::doForecast() { //clear m_accountList because forecast is about to changed d->m_accountList.clear(); //reinitialize the object d->m_forecast = KMyMoneyGlobalSettings::forecast(); //If forecastDays lower than accountsCycle, adjust to the first cycle if (d->m_forecast.accountsCycle() > d->m_forecast.forecastDays()) d->m_forecast.setForecastDays(d->m_forecast.accountsCycle()); //Get all accounts of the right type to calculate forecast d->m_forecast.doForecast(); } MyMoneyMoney KHomeView::forecastPaymentBalance(const MyMoneyAccount& acc, const MyMoneyMoney& payment, QDate& paymentDate) { //if paymentDate before or equal to currentDate set it to current date plus 1 //so we get to accumulate forecast balance correctly if (paymentDate <= QDate::currentDate()) paymentDate = QDate::currentDate().addDays(1); //check if the account is already there if (d->m_accountList.find(acc.id()) == d->m_accountList.end() || d->m_accountList[acc.id()].find(paymentDate) == d->m_accountList[acc.id()].end()) { if (paymentDate == QDate::currentDate()) { d->m_accountList[acc.id()][paymentDate] = d->m_forecast.forecastBalance(acc, paymentDate); } else { d->m_accountList[acc.id()][paymentDate] = d->m_forecast.forecastBalance(acc, paymentDate.addDays(-1)); } } d->m_accountList[acc.id()][paymentDate] = d->m_accountList[acc.id()][paymentDate] + payment; return d->m_accountList[acc.id()][paymentDate]; } void KHomeView::showCashFlowSummary() { MyMoneyTransactionFilter filter; MyMoneyMoney incomeValue; MyMoneyMoney expenseValue; MyMoneyFile* file = MyMoneyFile::instance(); int prec = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); //set start and end of month dates QDate startOfMonth = QDate(QDate::currentDate().year(), QDate::currentDate().month(), 1); QDate endOfMonth = QDate(QDate::currentDate().year(), QDate::currentDate().month(), QDate::currentDate().daysInMonth()); //Add total income and expenses for this month //get transactions for current month filter.setDateFilter(startOfMonth, endOfMonth); filter.setReportAllSplits(false); QList transactions = file->transactionList(filter); //if no transaction then skip and print total in zero if (transactions.size() > 0) { QList::const_iterator it_transaction; //get all transactions for this month for (it_transaction = transactions.constBegin(); it_transaction != transactions.constEnd(); ++it_transaction) { //get the splits for each transaction const QList& splits = (*it_transaction).splits(); QList::const_iterator it_split; for (it_split = splits.begin(); it_split != splits.end(); ++it_split) { if (!(*it_split).shares().isZero()) { ReportAccount repSplitAcc = ReportAccount((*it_split).accountId()); //only add if it is an income or expense if (repSplitAcc.isIncomeExpense()) { MyMoneyMoney value; //convert to base currency if necessary if (repSplitAcc.currencyId() != file->baseCurrency().id()) { MyMoneyMoney curPrice = repSplitAcc.baseCurrencyPrice((*it_transaction).postDate()); value = ((*it_split).shares() * MyMoneyMoney::MINUS_ONE) * curPrice; value = value.convert(10000); } else { value = ((*it_split).shares() * MyMoneyMoney::MINUS_ONE); } //store depending on account type if (repSplitAcc.accountType() == Account::Income) { incomeValue += value; } else { expenseValue += value; } } } } } } //format income and expenses QString amountIncome = incomeValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountExpense = expenseValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); amountIncome.replace(QChar(' '), " "); amountExpense.replace(QChar(' '), " "); //calculate schedules //Add all schedules for this month MyMoneyMoney scheduledIncome; MyMoneyMoney scheduledExpense; MyMoneyMoney scheduledLiquidTransfer; MyMoneyMoney scheduledOtherTransfer; //get overdues and schedules until the end of this month QList schedule = file->scheduleList(QString(), Schedule::Type::Any, Schedule::Occurrence::Any, Schedule::PaymentType::Any, QDate(), endOfMonth, false); //Remove the finished schedules QList::Iterator finished_it; for (finished_it = schedule.begin(); finished_it != schedule.end();) { if ((*finished_it).isFinished()) { finished_it = schedule.erase(finished_it); continue; } ++finished_it; } //add income and expenses QList::Iterator sched_it; for (sched_it = schedule.begin(); sched_it != schedule.end();) { QDate nextDate = (*sched_it).nextDueDate(); int cnt = 0; while (nextDate.isValid() && nextDate <= endOfMonth) { ++cnt; nextDate = (*sched_it).nextPayment(nextDate); // for single occurrence nextDate will not change, so we // better get out of here. if ((*sched_it).occurrence() == Schedule::Occurrence::Once) break; } MyMoneyAccount acc = (*sched_it).account(); if (!acc.id().isEmpty()) { MyMoneyTransaction transaction = (*sched_it).transaction(); // only show the entry, if it is still active MyMoneySplit sp = transaction.splitByAccount(acc.id(), true); // take care of the autoCalc stuff if ((*sched_it).type() == Schedule::Type::LoanPayment) { QDate nextDate = (*sched_it).nextPayment((*sched_it).lastPayment()); //make sure we have all 'starting balances' so that the autocalc works QList::const_iterator it_s; QMap balanceMap; for (it_s = transaction.splits().constBegin(); it_s != transaction.splits().constEnd(); ++it_s) { MyMoneyAccount acc = file->account((*it_s).accountId()); // collect all overdues on the first day QDate schedDate = nextDate; if (QDate::currentDate() >= nextDate) schedDate = QDate::currentDate().addDays(1); balanceMap[acc.id()] += file->balance(acc.id(), QDate::currentDate()); } KMyMoneyUtils::calculateAutoLoan(*sched_it, transaction, balanceMap); } //go through the splits and assign to liquid or other transfers const QList splits = transaction.splits(); QList::const_iterator split_it; for (split_it = splits.constBegin(); split_it != splits.constEnd(); ++split_it) { if ((*split_it).accountId() != acc.id()) { ReportAccount repSplitAcc = ReportAccount((*split_it).accountId()); //get the shares and multiply by the quantity of occurrences in the period MyMoneyMoney value = (*split_it).shares() * cnt; //convert to foreign currency if needed if (repSplitAcc.currencyId() != file->baseCurrency().id()) { MyMoneyMoney curPrice = repSplitAcc.baseCurrencyPrice(QDate::currentDate()); value = value * curPrice; value = value.convert(10000); } if ((repSplitAcc.isLiquidLiability() || repSplitAcc.isLiquidAsset()) && acc.accountGroup() != repSplitAcc.accountGroup()) { scheduledLiquidTransfer += value; } else if (repSplitAcc.isAssetLiability() && !repSplitAcc.isLiquidLiability() && !repSplitAcc.isLiquidAsset()) { scheduledOtherTransfer += value; } else if (repSplitAcc.isIncomeExpense()) { //income and expenses are stored as negative values if (repSplitAcc.accountType() == Account::Income) scheduledIncome -= value; if (repSplitAcc.accountType() == Account::Expense) scheduledExpense -= value; } } } } ++sched_it; } //format the currency strings QString amountScheduledIncome = scheduledIncome.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountScheduledExpense = scheduledExpense.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountScheduledLiquidTransfer = scheduledLiquidTransfer.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountScheduledOtherTransfer = scheduledOtherTransfer.formatMoney(file->baseCurrency().tradingSymbol(), prec); amountScheduledIncome.replace(QChar(' '), " "); amountScheduledExpense.replace(QChar(' '), " "); amountScheduledLiquidTransfer.replace(QChar(' '), " "); amountScheduledOtherTransfer.replace(QChar(' '), " "); //get liquid assets and liabilities QList accounts; QList::const_iterator account_it; MyMoneyMoney liquidAssets; MyMoneyMoney liquidLiabilities; // get list of all accounts file->accountList(accounts); for (account_it = accounts.constBegin(); account_it != accounts.constEnd();) { if (!(*account_it).isClosed()) { switch ((*account_it).accountType()) { //group all assets into one list case Account::Checkings: case Account::Savings: case Account::Cash: { MyMoneyMoney value = MyMoneyFile::instance()->balance((*account_it).id(), QDate::currentDate()); //calculate balance for foreign currency accounts if ((*account_it).currencyId() != file->baseCurrency().id()) { ReportAccount repAcc = ReportAccount((*account_it).id()); MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); MyMoneyMoney baseValue = value * curPrice; liquidAssets += baseValue; liquidAssets = liquidAssets.convert(10000); } else { liquidAssets += value; } break; } //group the liabilities into the other case Account::CreditCard: { MyMoneyMoney value; value = MyMoneyFile::instance()->balance((*account_it).id(), QDate::currentDate()); //calculate balance if foreign currency if ((*account_it).currencyId() != file->baseCurrency().id()) { ReportAccount repAcc = ReportAccount((*account_it).id()); MyMoneyMoney curPrice = repAcc.baseCurrencyPrice(QDate::currentDate()); MyMoneyMoney baseValue = value * curPrice; liquidLiabilities += baseValue; liquidLiabilities = liquidLiabilities.convert(10000); } else { liquidLiabilities += value; } break; } default: break; } } ++account_it; } //calculate net worth MyMoneyMoney liquidWorth = liquidAssets + liquidLiabilities; //format assets, liabilities and net worth QString amountLiquidAssets = liquidAssets.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountLiquidLiabilities = liquidLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountLiquidWorth = liquidWorth.formatMoney(file->baseCurrency().tradingSymbol(), prec); amountLiquidAssets.replace(QChar(' '), " "); amountLiquidLiabilities.replace(QChar(' '), " "); amountLiquidWorth.replace(QChar(' '), " "); //show the summary d->m_html += "
" + i18n("Cash Flow Summary") + "
\n
 
\n"; //print header d->m_html += ""; //income and expense title d->m_html += ""; d->m_html += ""; //column titles d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; //add row with banding d->m_html += QString(""); //print current income d->m_html += QString("").arg(showColoredAmount(amountIncome, incomeValue.isNegative())); //print the scheduled income d->m_html += QString("").arg(showColoredAmount(amountScheduledIncome, scheduledIncome.isNegative())); //print current expenses d->m_html += QString("").arg(showColoredAmount(amountExpense, expenseValue.isNegative())); //print the scheduled expenses d->m_html += QString("").arg(showColoredAmount(amountScheduledExpense, scheduledExpense.isNegative())); d->m_html += ""; d->m_html += "
"; d->m_html += i18n("Income and Expenses of Current Month"); d->m_html += "
"; d->m_html += i18n("Income"); d->m_html += ""; d->m_html += i18n("Scheduled Income"); d->m_html += ""; d->m_html += i18n("Expenses"); d->m_html += ""; d->m_html += i18n("Scheduled Expenses"); d->m_html += "
%2%2%2%2
"; //print header of assets and liabilities d->m_html += "
 
\n"; d->m_html += ""; //assets and liabilities title d->m_html += ""; d->m_html += ""; //column titles d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; //add row with banding d->m_html += QString(""); //print current liquid assets d->m_html += QString("").arg(showColoredAmount(amountLiquidAssets, liquidAssets.isNegative())); //print the scheduled transfers d->m_html += QString("").arg(showColoredAmount(amountScheduledLiquidTransfer, scheduledLiquidTransfer.isNegative())); //print current liabilities d->m_html += QString("").arg(showColoredAmount(amountLiquidLiabilities, liquidLiabilities.isNegative())); //print the scheduled transfers d->m_html += QString("").arg(showColoredAmount(amountScheduledOtherTransfer, scheduledOtherTransfer.isNegative())); d->m_html += ""; d->m_html += "
"; d->m_html += i18n("Liquid Assets and Liabilities"); d->m_html += "
"; d->m_html += i18n("Liquid Assets"); d->m_html += ""; d->m_html += i18n("Transfers to Liquid Liabilities"); d->m_html += ""; d->m_html += i18n("Liquid Liabilities"); d->m_html += ""; d->m_html += i18n("Other Transfers"); d->m_html += "
%2%2%2%2
"; //final conclusion MyMoneyMoney profitValue = incomeValue + expenseValue + scheduledIncome + scheduledExpense; MyMoneyMoney expectedAsset = liquidAssets + scheduledIncome + scheduledExpense + scheduledLiquidTransfer + scheduledOtherTransfer; MyMoneyMoney expectedLiabilities = liquidLiabilities + scheduledLiquidTransfer; QString amountExpectedAsset = expectedAsset.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountExpectedLiabilities = expectedLiabilities.formatMoney(file->baseCurrency().tradingSymbol(), prec); QString amountProfit = profitValue.formatMoney(file->baseCurrency().tradingSymbol(), prec); amountProfit.replace(QChar(' '), " "); amountExpectedAsset.replace(QChar(' '), " "); amountExpectedLiabilities.replace(QChar(' '), " "); //print header of cash flow status d->m_html += "
 
\n"; d->m_html += ""; //income and expense title d->m_html += ""; d->m_html += ""; //column titles d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; d->m_html += ""; //add row with banding d->m_html += QString(""); d->m_html += ""; //print expected assets d->m_html += QString("").arg(showColoredAmount(amountExpectedAsset, expectedAsset.isNegative())); //print expected liabilities d->m_html += QString("").arg(showColoredAmount(amountExpectedLiabilities, expectedLiabilities.isNegative())); //print expected profit d->m_html += QString("").arg(showColoredAmount(amountProfit, profitValue.isNegative())); d->m_html += ""; d->m_html += "
"; d->m_html += i18n("Cash Flow Status"); d->m_html += "
 "; d->m_html += i18n("Expected Liquid Assets"); d->m_html += ""; d->m_html += i18n("Expected Liquid Liabilities"); d->m_html += ""; d->m_html += i18n("Expected Profit/Loss"); d->m_html += "
 %2%2%2
"; d->m_html += "
"; } // Make sure, that these definitions are only used within this file // this does not seem to be necessary, but when building RPMs the // build option 'final' is used and all CPP files are concatenated. // So it could well be, that in another CPP file these definitions // are also used. #undef VIEW_LEDGER #undef VIEW_SCHEDULE #undef VIEW_WELCOME #undef VIEW_HOME #undef VIEW_REPORTS diff --git a/kmymoney/views/kmymoneyview.h b/kmymoney/views/kmymoneyview.h index 6e40e0651..c0a65f3ed 100644 --- a/kmymoney/views/kmymoneyview.h +++ b/kmymoney/views/kmymoneyview.h @@ -1,623 +1,622 @@ /*************************************************************************** kmymoneyview.h ------------------- copyright : (C) 2000-2001 by Michael Edwardes 2004 by Thomas Baumgart 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYVIEW_H #define KMYMONEYVIEW_H #include "config-kmymoney.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes -#include "selectedtransactions.h" +#include "selectedtransaction.h" #ifdef KF5Activities_FOUND namespace KActivities { class ResourceInstance; } #endif namespace eAccountsModel { enum class Column; } class KMyMoneyApp; class KHomeView; class KAccountsView; class KCategoriesView; class KInstitutionsView; class KPayeesView; class KTagsView; class KBudgetView; class KScheduledView; class KGlobalLedgerView; class IMyMoneyStorageFormat; class MyMoneyTransaction; class KInvestmentView; class KReportsView; class MyMoneySchedule; class MyMoneySecurity; class MyMoneyReport; class TransactionEditor; class KForecastView; class KOnlineJobOutbox; class KMyMoneyTitleLabel; class MyMoneyAccount; -class MyMoneyMoney; class QLabel; /** * This class represents the view of the MyMoneyFile which contains * Banks/Accounts/Transactions, Recurring transactions (or Bills & Deposits) * and scripts (yet to be implemented). Each different aspect of the file * is represented by a tab within the view. * * @author Michael Edwardes 2001 Copyright 2000-2001 * * @short Handles the view of the MyMoneyFile. */ enum class View; class KMyMoneyView : public KPageWidget { Q_OBJECT public: // file actions for plugin enum fileActions { preOpen, postOpen, preSave, postSave, preClose, postClose }; KOnlineJobOutbox* getOnlineJobOutbox() const { return m_onlineJobOutboxView; } private: enum menuID { AccountNew = 1, AccountOpen, AccountReconcile, AccountEdit, AccountDelete, AccountOnlineMap, AccountOnlineUpdate, AccountOfxConnect, CategoryNew }; enum storageTypeE { Memory = 0, Database } _storageType; KPageWidgetModel* m_model; KHomeView *m_homeView; KAccountsView *m_accountsView; KInstitutionsView *m_institutionsView; KCategoriesView *m_categoriesView; KPayeesView *m_payeesView; KTagsView *m_tagsView; KBudgetView *m_budgetView; KScheduledView *m_scheduledView; KGlobalLedgerView *m_ledgerView; KInvestmentView *m_investmentView; KReportsView* m_reportsView; KForecastView* m_forecastView; KOnlineJobOutbox* m_onlineJobOutboxView; QHash viewFrames; KMyMoneyTitleLabel* m_header; bool m_inConstructor; bool m_fileOpen; QFileDevice::Permissions m_fmode; int m_lastViewSelected; // Keep a note of the file type typedef enum _fileTypeE { KmmBinary = 0, // native, binary KmmXML, // native, XML KmmDb, // SQL database /* insert new native file types above this line */ MaxNativeFileType, /* and non-native types below */ GncXML // Gnucash XML } fileTypeE; fileTypeE m_fileType; #ifdef KF5Activities_FOUND private: KActivities::ResourceInstance * m_activityResourceInstance; #endif private: void ungetString(QIODevice *qfile, char * buf, int len); /** * if no base currency is defined, start the dialog and force it to be set */ void selectBaseCurrency(); /** * This method attaches an empty storage object to the MyMoneyFile * object. It calls removeStorage() to remove a possibly attached * storage object. */ void newStorage(storageTypeE = Memory); /** * This method removes an attached storage from the MyMoneyFile * object. */ void removeStorage(); void viewAccountList(const QString& selectAccount); // Show the accounts view static void progressCallback(int current, int total, const QString&); /** */ void fixFile_0(); void fixFile_1(); void fixFile_2(); void fixFile_3(); /** */ void fixLoanAccount_0(MyMoneyAccount acc); /** */ void fixTransactions_0(); void fixSchedule_0(MyMoneySchedule sched); void fixDuplicateAccounts_0(MyMoneyTransaction& t); void createSchedule(MyMoneySchedule s, MyMoneyAccount& a); void checkAccountName(const MyMoneyAccount& acc, const QString& name) const; public: /** * The constructor for KMyMoneyView. Just creates all the tabs for the * different aspects of the MyMoneyFile. */ explicit KMyMoneyView(KMyMoneyApp *kmymoney); /** * Destructor */ ~KMyMoneyView(); /** * Makes sure that a MyMoneyFile is open and has been created successfully. * * @return Whether the file is open and initialised */ bool fileOpen(); /** * Closes the open MyMoneyFile and frees all the allocated memory, I hope ! */ void closeFile(); /** * Calls MyMoneyFile::readAllData which reads a MyMoneyFile into appropriate * data structures in memory. The return result is examined to make sure no * errors occurred whilst parsing. * * @param url The URL to read from. * If no protocol is specified, file:// is assumed. * * @return Whether the read was successful. */ bool readFile(const QUrl &url); /** * Saves the data into permanent storage using the XML format. * * @param url The URL to save into. * If no protocol is specified, file:// is assumed. * @param keyList QString containing a comma separated list of keys * to be used for encryption. If @p keyList is empty, * the file will be saved unencrypted (the default) * * @retval false save operation failed * @retval true save operation was successful */ bool saveFile(const QUrl &url, const QString& keyList = QString()); /** * Saves the data into permanent storage on a new or empty SQL database. * * @param url The pseudo of tyhe database * * @retval false save operation failed * @retval true save operation was successful */ //const bool saveDatabase(const QUrl &url); This no longer relevant /** * Saves the data into permanent storage on a new or empty SQL database. * * @param url The pseudo URL of the database * * @retval false save operation failed * @retval true save operation was successful */ bool saveAsDatabase(const QUrl &url); /** * Call this to find out if the currently open file is native KMM * * @retval true file is native * @retval false file is foreign */ bool isNativeFile() { return (m_fileOpen && (m_fileType < MaxNativeFileType)); } /** * Call this to find out if the currently open file is a sql database * * @retval true file is database * @retval false file is serial */ bool isDatabase() { return (m_fileOpen && ((m_fileType == KmmDb))); } /** * Call this to see if the MyMoneyFile contains any unsaved data. * * @retval true if any data has been modified but not saved * @retval false otherwise */ bool dirty(); /** * Close the currently opened file and create an empty new file. * * @see MyMoneyFile */ void newFile(); /** * This method enables the state of all views (except home view) according * to an open file. */ void enableViewsIfFileOpen(); void addWidget(QWidget* w); void showPage(KPageWidgetItem* pageItem); /** * check if the current view allows to create a transaction * * @param list list of selected transactions * @param tooltip reference to string receiving the tooltip text * which explains why the modify function is not available (in case * of returning @c false) * * @retval true Yes, view allows to create a transaction (tooltip is not changed) * @retval false No, view cannot to create a transaction (tooltip is updated with message) */ bool canCreateTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const; /** * check if the current view allows to modify (edit/delete) the selected transactions * * @param list list of selected transactions * @param tooltip reference to string receiving the tooltip text * which explains why the modify function is not available (in case * of returning @c false) * * @retval true Yes, view allows to edit/delete transactions (tooltip is not changed) * @retval false No, view cannot edit/delete transactions (tooltip is updated with message) */ bool canModifyTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const; bool canDuplicateTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const; /** * check if the current view allows to edit the selected transactions * * @param list list of selected transactions * @param tooltip reference to string receiving the tooltip text * which explains why the edit function is not available (in case * of returning @c false) * * @retval true Yes, view allows to enter/edit transactions * @retval false No, view cannot enter/edit transactions */ bool canEditTransactions(const KMyMoneyRegister::SelectedTransactions& list, QString& tooltip) const; /** * check if the current view allows to print something * * @retval true Yes, view allows to print * @retval false No, view cannot print */ bool canPrint(); TransactionEditor* startEdit(const KMyMoneyRegister::SelectedTransactions&); bool createNewTransaction(); /** * Used to start reconciliation of account @a account. It switches the * ledger view into reconciliation mode and updates the view. * * @param account account which should be reconciled * @param reconciliationDate the statement date * @param endingBalance the ending balance entered for this account * * @retval true Reconciliation started * @retval false Account cannot be reconciled */ bool startReconciliation(const MyMoneyAccount& account, const QDate& reconciliationDate, const MyMoneyMoney& endingBalance); /** * Used to finish reconciliation of account @a account. It switches the * ledger view to normal mode and updates the view. * * @param account account which should be reconciled */ void finishReconciliation(const MyMoneyAccount& account); /** * This method updates names of currencies from file to localized names */ void updateCurrencyNames(); /** * This method loads all known currencies and saves them to the storage */ void loadAllCurrencies(); void showTitleBar(bool show); /** * This method changes the view type according to the settings. */ void updateViewType(); void slotAccountTreeViewChanged(const eAccountsModel::Column column, const bool show); void slotNetBalProChanged(const MyMoneyMoney &val, QLabel *label, const View view); protected: /** * Overwritten because KMyMoney has it's custom header. */ virtual bool showPageHeader() const; public slots: /** * This slot writes information about the page passed as argument @a current * in the kmymoney.rc file so that it can be selected automatically when * the application is started again. * * @param current QModelIndex of the current page item * @param previous QModelIndex of the previous page item */ void slotCurrentPageChanged(const QModelIndex current, const QModelIndex previous); /** * Brings up a dialog to change the list(s) settings and saves them into the * class KMyMoneySettings (a singleton). * * @see KListSettingsDlg * Refreshes all views. Used e.g. after settings have been changed or * data has been loaded from external sources (QIF import). **/ void slotRefreshViews(); /** * Called, whenever the ledger view should pop up and a specific * transaction in an account should be shown. If @p transaction * is empty, the last transaction should be selected * * @param acc The ID of the account to be shown * @param transaction The ID of the transaction to be selected */ void slotLedgerSelected(const QString& acc, const QString& transaction = QString()); /** * Called, whenever the payees view should pop up and a specific * transaction in an account should be shown. * * @param payeeId The ID of the payee to be shown * @param accountId The ID of the account to be shown * @param transactionId The ID of the transaction to be selected */ void slotPayeeSelected(const QString& payeeId, const QString& accountId, const QString& transactionId); /** * Called, whenever the tags view should pop up and a specific * transaction in an account should be shown. * * @param tagId The ID of the tag to be shown * @param accountId The ID of the account to be shown * @param transactionId The ID of the transaction to be selected */ void slotTagSelected(const QString& tagId, const QString& accountId, const QString& transactionId); /** * Called, whenever the schedule view should pop up and a specific * schedule should be shown. * * @param schedule The ID of the schedule to be shown */ void slotScheduleSelected(const QString& schedule); /** * Called, whenever the report view should pop up and a specific * report should be shown. * * @param reportid The ID of the report to be shown */ void slotShowReport(const QString& reportid); /** * Same as the above, but the caller passes in an actual report * definition to be shown. * * @param report The report to be shown */ void slotShowReport(const MyMoneyReport& report); /** * This slot prints the current view. */ void slotPrintView(); /** * Called when the user changes the detail * setting of the transaction register * * @param detailed if true, the register is shown with all details */ void slotShowTransactionDetail(bool detailed); private slots: /** * This slots switches the view to the specific page */ void slotShowHomePage(); void slotShowInstitutionsPage(); void slotShowAccountsPage(); void slotShowSchedulesPage(); void slotShowCategoriesPage(); void slotShowTagsPage(); void slotShowPayeesPage(); void slotShowLedgersPage(); void slotShowInvestmentsPage(); void slotShowReportsPage(); void slotShowBudgetPage(); void slotShowForecastPage(); void slotShowOutboxPage(); protected slots: /** * eventually replace this with KMyMoneyApp::slotCurrencySetBase(). * it contains the same code * * @deprecated */ void slotSetBaseCurrency(const MyMoneySecurity& baseCurrency); private: /** * This method is called from readFile to open a database file which * is to be processed in 'proper' database mode, i.e. in-place updates * * @param dbaseURL pseudo-QUrl representation of database * * @retval true Database opened successfully * @retval false Could not open or read database */ bool openDatabase(const QUrl &dbaseURL); /** * This method is used after a file or database has been * read into storage, and performs various initialization tasks * * @retval true all went okay * @retval false an exception occurred during this process */ bool initializeStorage(); /** * This method is used by saveFile() to store the data * either directly in the destination file if it is on * the local file system or in a temporary file when * the final destination is reached over a network * protocol (e.g. FTP) * * @param localFile the name of the local file * @param writer pointer to the formatter * @param plaintext whether to override any compression & encryption settings * @param keyList QString containing a comma separated list of keys to be used for encryption * If @p keyList is empty, the file will be saved unencrypted * * @note This method will close the file when it is written. */ void saveToLocalFile(const QString& localFile, IMyMoneyStorageFormat* writer, bool plaintext = false, const QString& keyList = QString()); /** * Internal method used by slotAccountNew() and slotAccountCategory(). */ void accountNew(const bool createCategory); void resetViewSelection(const View); void connectView(const View); signals: /** * This signal is emitted whenever a view is selected. * The parameter @p view is identified as one of KMyMoneyView::viewID. */ void viewActivated(int view); /** * This signal is emitted whenever a new view is about to be selected. */ void aboutToChangeView(); void accountSelectedForContextMenu(const MyMoneyAccount& acc); void viewStateChanged(bool enabled); /** * This signal is emitted to inform the kmmFile plugin when various file actions * occur. The Action parameter distinguishes between them. */ void kmmFilePlugin(unsigned int action); /** * Signal is emitted when reconciliation starts or ends. In case of end, * @a account is MyMoneyAccount() * * @param account account for which reconciliation starts or MyMoneyAccount() * if reconciliation ends. * @param reconciliationDate the statement date * @param endingBalance collected ending balance when reconciliation starts * 0 otherwise */ void reconciliationStarts(const MyMoneyAccount& account, const QDate& reconciliationDate, const MyMoneyMoney& endingBalance); /** * This signal is emitted after a data source has been closed */ void fileClosed(); /** * This signal is emitted after a data source has been opened */ void fileOpened(); }; #endif diff --git a/kmymoney/views/kpayeesview.cpp b/kmymoney/views/kpayeesview.cpp index bddf250f1..dacb02b78 100644 --- a/kmymoney/views/kpayeesview.cpp +++ b/kmymoney/views/kpayeesview.cpp @@ -1,991 +1,989 @@ /*************************************************************************** kpayeesview.cpp --------------- begin : Thu Jan 24 2002 copyright : (C) 2000-2002 by Michael Edwardes Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio Andreas Nicolai ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kpayeesview.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "config-kmymoney.h" #include "mymoneyfile.h" #include "mymoneyprice.h" #include "mymoneytransactionfilter.h" #include "kmymoneyglobalsettings.h" #include "kmymoney.h" #include "models.h" #include "accountsmodel.h" #include "mymoneysecurity.h" #include "mymoneycontact.h" -#include "mymoneysplit.h" -#include "mymoneytransaction.h" #include "icons/icons.h" using namespace Icons; // *** KPayeeListItem Implementation *** KPayeeListItem::KPayeeListItem(QListWidget *parent, const MyMoneyPayee& payee) : QListWidgetItem(parent, QListWidgetItem::UserType), m_payee(payee) { setText(payee.name()); // allow in column rename setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); } KPayeeListItem::~KPayeeListItem() { } // *** KPayeesView Implementation *** KPayeesView::KPayeesView(QWidget *parent) : QWidget(parent), m_contact(new MyMoneyContact(this)), m_needReload(false), m_needLoad(true), m_inSelection(false), m_allowEditing(true), m_payeeFilterType(0) { } KPayeesView::~KPayeesView() { if(!m_needLoad) { // remember the splitter settings for startup KConfigGroup grp = KSharedConfig::openConfig()->group("Last Use Settings"); grp.writeEntry("KPayeesViewSplitterSize", m_splitter->saveState()); grp.sync(); } } void KPayeesView::setDefaultFocus() { QTimer::singleShot(0, m_searchWidget, SLOT(setFocus())); } void KPayeesView::init() { m_needLoad = false; setupUi(this); m_filterProxyModel = new AccountNamesFilterProxyModel(this); m_filterProxyModel->setHideEquityAccounts(!KMyMoneyGlobalSettings::expertMode()); m_filterProxyModel->addAccountGroup(QVector {eMyMoney::Account::Asset, eMyMoney::Account::Liability, eMyMoney::Account::Income, eMyMoney::Account::Expense, eMyMoney::Account::Equity}); auto const model = Models::instance()->accountsModel(); m_filterProxyModel->setSourceModel(model); m_filterProxyModel->setSourceColumns(model->getColumns()); m_filterProxyModel->sort((int)eAccountsModel::Column::Account); comboDefaultCategory->setModel(m_filterProxyModel); matchTypeCombo->addItem(i18nc("@item No matching", "No matching"), MyMoneyPayee::matchDisabled); matchTypeCombo->addItem(i18nc("@item Match Payees name partially", "Match Payees name (partial)"), MyMoneyPayee::matchName); matchTypeCombo->addItem(i18nc("@item Match Payees name exactly", "Match Payees name (exact)"), MyMoneyPayee::matchNameExact); matchTypeCombo->addItem(i18nc("@item Search match in list", "Match on a name listed below"), MyMoneyPayee::matchKey); // create the searchline widget // and insert it into the existing layout m_searchWidget = new KListWidgetSearchLine(this, m_payeesList); m_searchWidget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); m_payeesList->setContextMenuPolicy(Qt::CustomContextMenu); m_listTopHLayout->insertWidget(0, m_searchWidget); //load the filter type m_filterBox->addItem(i18nc("@item Show all payees", "All")); m_filterBox->addItem(i18nc("@item Show only used payees", "Used")); m_filterBox->addItem(i18nc("@item Show only unused payees", "Unused")); m_filterBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); KGuiItem newButtonItem(QString(), QIcon::fromTheme(g_Icons[Icon::ListAddUser]), i18n("Creates a new payee"), i18n("Use this to create a new payee.")); KGuiItem::assign(m_newButton, newButtonItem); m_newButton->setToolTip(newButtonItem.toolTip()); KGuiItem renameButtonItem(QString(), QIcon::fromTheme(g_Icons[Icon::UserProperties]), i18n("Rename the current selected payee"), i18n("Use this to start renaming the selected payee.")); KGuiItem::assign(m_renameButton, renameButtonItem); m_renameButton->setToolTip(renameButtonItem.toolTip()); KGuiItem deleteButtonItem(QString(), QIcon::fromTheme(g_Icons[Icon::ListRemoveUser]), i18n("Delete selected payee(s)"), i18n("Use this to delete the selected payee. You can also select " "multiple payees to be deleted.")); KGuiItem::assign(m_deleteButton, deleteButtonItem); m_deleteButton->setToolTip(deleteButtonItem.toolTip()); KGuiItem mergeButtonItem(QString(), QIcon::fromTheme(g_Icons[Icon::Merge]), i18n("Merge multiple selected payees"), i18n("Use this to merge multiple selected payees.")); KGuiItem::assign(m_mergeButton, mergeButtonItem); m_mergeButton->setToolTip(mergeButtonItem.toolTip()); KGuiItem updateButtonItem(i18nc("Update payee", "Update"), QIcon::fromTheme(g_Icons[Icon::DialogOK]), i18n("Accepts the entered data and stores it"), i18n("Use this to accept the modified data.")); KGuiItem::assign(m_updateButton, updateButtonItem); KGuiItem syncButtonItem(i18nc("Sync payee", "Sync"), QIcon::fromTheme(g_Icons[Icon::Refresh]), i18n("Fetches the payee's data from your addressbook."), i18n("Use this to fetch payee's data.")); KGuiItem::assign(m_syncAddressbook, syncButtonItem); KGuiItem sendMailButtonItem(i18nc("Send mail", "Send"), QIcon::fromTheme(g_Icons[Icon::MailMessage]), i18n("Creates new e-mail to your payee."), i18n("Use this to create new e-mail to your payee.")); KGuiItem::assign(m_sendMail, sendMailButtonItem); m_updateButton->setEnabled(false); m_syncAddressbook->setEnabled(false); #ifndef KMM_ADDRESSBOOK_FOUND m_syncAddressbook->hide(); #endif matchTypeCombo->setCurrentIndex(0); checkMatchIgnoreCase->setEnabled(false); checkEnableDefaultCategory->setChecked(false); labelDefaultCategory->setEnabled(false); comboDefaultCategory->setEnabled(false); QList cols; cols << KMyMoneyRegister::DateColumn; cols << KMyMoneyRegister::AccountColumn; cols << KMyMoneyRegister::DetailColumn; cols << KMyMoneyRegister::ReconcileFlagColumn; cols << KMyMoneyRegister::PaymentColumn; cols << KMyMoneyRegister::DepositColumn; m_register->setupRegister(MyMoneyAccount(), cols); m_register->setSelectionMode(QTableWidget::SingleSelection); m_register->setDetailsColumnType(KMyMoneyRegister::AccountFirst); m_balanceLabel->hide(); connect(m_contact, SIGNAL(contactFetched(ContactData)), this, SLOT(slotContactFetched(ContactData))); connect(m_payeesList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(slotSelectPayee(QListWidgetItem*,QListWidgetItem*))); connect(m_payeesList, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectPayee())); connect(m_payeesList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(slotStartRename(QListWidgetItem*))); connect(m_payeesList, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(slotRenamePayee(QListWidgetItem*))); connect(m_payeesList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotOpenContextMenu(QPoint))); connect(m_renameButton, SIGNAL(clicked()), this, SLOT(slotRenameButtonCliked())); connect(m_deleteButton, SIGNAL(clicked()), kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::PayeeDelete]), SLOT(trigger())); connect(m_mergeButton, SIGNAL(clicked()), kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::PayeeMerge]), SLOT(trigger())); connect(m_newButton, SIGNAL(clicked()), this, SLOT(slotPayeeNew())); connect(addressEdit, SIGNAL(textChanged()), this, SLOT(slotPayeeDataChanged())); connect(postcodeEdit, SIGNAL(textChanged(QString)), this, SLOT(slotPayeeDataChanged())); connect(telephoneEdit, SIGNAL(textChanged(QString)), this, SLOT(slotPayeeDataChanged())); connect(emailEdit, SIGNAL(textChanged(QString)), this, SLOT(slotPayeeDataChanged())); connect(notesEdit, SIGNAL(textChanged()), this, SLOT(slotPayeeDataChanged())); connect(matchKeyEditList, SIGNAL(changed()), this, SLOT(slotKeyListChanged())); connect(matchTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPayeeDataChanged())); connect(checkMatchIgnoreCase, SIGNAL(toggled(bool)), this, SLOT(slotPayeeDataChanged())); connect(checkEnableDefaultCategory, SIGNAL(toggled(bool)), this, SLOT(slotPayeeDataChanged())); connect(comboDefaultCategory, SIGNAL(accountSelected(QString)), this, SLOT(slotPayeeDataChanged())); connect(buttonSuggestACategory, SIGNAL(clicked()), this, SLOT(slotChooseDefaultAccount())); connect(m_updateButton, SIGNAL(clicked()), this, SLOT(slotUpdatePayee())); connect(m_syncAddressbook, SIGNAL(clicked()), this, SLOT(slotSyncAddressBook())); connect(m_helpButton, SIGNAL(clicked()), this, SLOT(slotHelp())); connect(m_sendMail, SIGNAL(clicked()), this, SLOT(slotSendMail())); connect(m_register, SIGNAL(editTransaction()), this, SLOT(slotSelectTransaction())); connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotLoadPayees())); connect(m_filterBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChangeFilter(int))); connect(payeeIdentifiers, SIGNAL(dataChanged()), this, SLOT(slotPayeeDataChanged())); // use the size settings of the last run (if any) KConfigGroup grp = KSharedConfig::openConfig()->group("Last Use Settings"); m_splitter->restoreState(grp.readEntry("KPayeesViewSplitterSize", QByteArray())); m_splitter->setChildrenCollapsible(false); //At start we haven't any payee selected m_tabWidget->setEnabled(false); // disable tab widget m_deleteButton->setEnabled(false); //disable delete, rename and merge buttons m_renameButton->setEnabled(false); m_mergeButton->setEnabled(false); m_payee = MyMoneyPayee(); // make sure we don't access an undefined payee clearItemData(); } void KPayeesView::slotChooseDefaultAccount() { MyMoneyFile* file = MyMoneyFile::instance(); QMap account_count; KMyMoneyRegister::RegisterItem* item = m_register->firstItem(); while (item) { //only walk through selectable items. eg. transactions and not group markers if (item->isSelectable()) { KMyMoneyRegister::Transaction* t = dynamic_cast(item); MyMoneySplit s = t->transaction().splitByPayee(m_payee.id()); const MyMoneyAccount& acc = file->account(s.accountId()); QString txt; if (s.action() != MyMoneySplit::ActionAmortization && acc.accountType() != eMyMoney::Account::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::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()) { checkEnableDefaultCategory->setChecked(true); comboDefaultCategory->setSelected(most_frequent.key()); setDirty(); } } void KPayeesView::slotStartRename(QListWidgetItem* item) { m_allowEditing = true; m_payeesList->editItem(item); } void KPayeesView::slotRenameButtonCliked() { if (m_payeesList->currentItem() && m_payeesList->selectedItems().count() == 1) { slotStartRename(m_payeesList->currentItem()); } } // This variant is only called when a single payee is selected and renamed. void KPayeesView::slotRenamePayee(QListWidgetItem* p) { //if there is no current item selected, exit if (m_allowEditing == false || !m_payeesList->currentItem() || p != m_payeesList->currentItem()) return; //qDebug() << "[KPayeesView::slotRenamePayee]"; // create a copy of the new name without appended whitespaces QString new_name = p->text(); if (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(m_payee.name()); return; } } catch (const MyMoneyException &) { // all ok, the name is unique } m_payee.setName(new_name); m_newName = new_name; MyMoneyFile::instance()->modifyPayee(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 ensurePayeeVisible(m_payee.id()); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to modify payee"), i18n("%1 thrown in %2:%3", e.what(), e.file(), e.line())); } } else { p->setText(new_name); } } void KPayeesView::ensurePayeeVisible(const QString& id) { for (int i = 0; i < m_payeesList->count(); ++i) { KPayeeListItem* p = dynamic_cast(m_payeesList->item(0)); if (p && p->payee().id() == id) { m_payeesList->scrollToItem(p, QAbstractItemView::PositionAtCenter); m_payeesList->setCurrentItem(p); // active item and deselect all others m_payeesList->setCurrentRow(i, QItemSelectionModel::ClearAndSelect); // and select it break; } } } void KPayeesView::selectedPayees(QList& payeesList) const { QList selectedItems = m_payeesList->selectedItems(); QList::ConstIterator itemsIt = selectedItems.constBegin(); while (itemsIt != selectedItems.constEnd()) { KPayeeListItem* item = dynamic_cast(*itemsIt); if (item) payeesList << item->payee(); ++itemsIt; } } void KPayeesView::slotSelectPayee(QListWidgetItem* cur, QListWidgetItem* prev) { Q_UNUSED(cur); Q_UNUSED(prev); m_allowEditing = false; } void KPayeesView::slotSelectPayee() { // check if the content of a currently selected payee was modified // and ask to store the data if (isDirty()) { QString question = QString("%1").arg(i18n("Do you want to save the changes for %1?", m_newName)); if (KMessageBox::questionYesNo(this, question, i18n("Save changes")) == KMessageBox::Yes) { m_inSelection = true; slotUpdatePayee(); m_inSelection = false; } } // make sure we always clear the selected list when listing again m_selectedPayeesList.clear(); // loop over all payees and count the number of payees, also // obtain last selected payee selectedPayees(m_selectedPayeesList); emit selectObjects(m_selectedPayeesList); if (m_selectedPayeesList.isEmpty()) { m_tabWidget->setEnabled(false); // disable tab widget m_balanceLabel->hide(); m_deleteButton->setEnabled(false); //disable delete, rename and merge buttons m_renameButton->setEnabled(false); m_mergeButton->setEnabled(false); clearItemData(); m_payee = MyMoneyPayee(); m_syncAddressbook->setEnabled(false); return; // make sure we don't access an undefined payee } m_deleteButton->setEnabled(true); //re-enable delete button m_syncAddressbook->setEnabled(true); // if we have multiple payees selected, clear and disable the payee information if (m_selectedPayeesList.count() > 1) { m_tabWidget->setEnabled(false); // disable tab widget m_renameButton->setEnabled(false); // disable also the rename button m_mergeButton->setEnabled(true); m_balanceLabel->hide(); clearItemData(); } else { m_mergeButton->setEnabled(false); m_renameButton->setEnabled(true); } // otherwise we have just one selected, enable payee information widget m_tabWidget->setEnabled(true); 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 { m_payee = m_selectedPayeesList[0]; m_newName = m_payee.name(); addressEdit->setEnabled(true); addressEdit->setText(m_payee.address()); postcodeEdit->setEnabled(true); postcodeEdit->setText(m_payee.postcode()); telephoneEdit->setEnabled(true); telephoneEdit->setText(m_payee.telephone()); emailEdit->setEnabled(true); emailEdit->setText(m_payee.email()); notesEdit->setText(m_payee.notes()); QStringList keys; bool ignorecase = false; MyMoneyPayee::payeeMatchType type = m_payee.matchData(ignorecase, keys); matchTypeCombo->setCurrentIndex(matchTypeCombo->findData(type)); matchKeyEditList->clear(); matchKeyEditList->insertStringList(keys); checkMatchIgnoreCase->setChecked(ignorecase); checkEnableDefaultCategory->setChecked(m_payee.defaultAccountEnabled()); comboDefaultCategory->setSelected(m_payee.defaultAccountId()); payeeIdentifiers->setSource(m_payee); slotPayeeDataChanged(); showTransactions(); } catch (const MyMoneyException &e) { qDebug("exception during display of payee: %s at %s:%ld", qPrintable(e.what()), qPrintable(e.file()), e.line()); m_register->clear(); m_selectedPayeesList.clear(); m_payee = MyMoneyPayee(); } m_allowEditing = true; } void KPayeesView::clearItemData() { addressEdit->setText(QString()); postcodeEdit->setText(QString()); telephoneEdit->setText(QString()); emailEdit->setText(QString()); notesEdit->setText(QString()); showTransactions(); } void KPayeesView::showTransactions() { MyMoneyMoney balance; MyMoneyFile *file = MyMoneyFile::instance(); MyMoneySecurity base = file->baseCurrency(); // setup sort order m_register->setSortOrder(KMyMoneyGlobalSettings::sortSearchView()); // clear the register m_register->clear(); if (m_selectedPayeesList.isEmpty() || !m_tabWidget->isEnabled()) { m_balanceLabel->setText(i18n("Balance: %1", balance.formatMoney(file->baseCurrency().smallestAccountFraction()))); return; } // setup the list and the pointer vector MyMoneyTransactionFilter filter; for (QList::const_iterator it = m_selectedPayeesList.constBegin(); it != m_selectedPayeesList.constEnd(); ++it) filter.addPayee((*it).id()); filter.setDateFilter(KMyMoneyGlobalSettings::startDate().date(), QDate()); // retrieve the list from the engine file->transactionList(m_transactionList, filter); // create the elements for the register QList >::const_iterator it; QMap uniqueMap; MyMoneyMoney deposit, payment; int splitCount = 0; bool balanceAccurate = true; for (it = m_transactionList.constBegin(); it != m_transactionList.constEnd(); ++it) { const MyMoneySplit& split = (*it).second; MyMoneyAccount acc = file->account(split.accountId()); ++splitCount; uniqueMap[(*it).first.id()]++; KMyMoneyRegister::Register::transactionFactory(m_register, (*it).first, (*it).second, uniqueMap[(*it).first.id()]); // take care of foreign currencies MyMoneyMoney val = split.shares().abs(); if (acc.currencyId() != base.id()) { const MyMoneyPrice &price = file->price(acc.currencyId(), base.id()); // in case the price is valid, we use it. Otherwise, we keep // a flag that tells us that the balance is somewhat inaccurate if (price.isValid()) { val *= price.rate(base.id()); } else { balanceAccurate = false; } } if (split.shares().isNegative()) { payment += val; } else { deposit += val; } } balance = deposit - payment; // add the group markers m_register->addGroupMarkers(); // sort the transactions according to the sort setting m_register->sortItems(); // remove trailing and adjacent markers m_register->removeUnwantedGroupMarkers(); m_register->updateRegister(true); // we might end up here with updates disabled on the register so // make sure that we enable updates here m_register->setUpdatesEnabled(true); m_balanceLabel->setText(i18n("Balance: %1%2", balanceAccurate ? "" : "~", balance.formatMoney(file->baseCurrency().smallestAccountFraction()))); } void KPayeesView::slotKeyListChanged() { bool rc = false; bool ignorecase = false; QStringList keys; m_payee.matchData(ignorecase, keys); if (matchTypeCombo->currentData().toUInt() == MyMoneyPayee::matchKey) { rc |= (keys != matchKeyEditList->items()); } setDirty(rc); } void KPayeesView::slotPayeeDataChanged() { bool rc = false; if (m_tabWidget->isEnabled()) { rc |= ((m_payee.email().isEmpty() != emailEdit->text().isEmpty()) || (!emailEdit->text().isEmpty() && m_payee.email() != emailEdit->text())); rc |= ((m_payee.address().isEmpty() != addressEdit->toPlainText().isEmpty()) || (!addressEdit->toPlainText().isEmpty() && m_payee.address() != addressEdit->toPlainText())); rc |= ((m_payee.postcode().isEmpty() != postcodeEdit->text().isEmpty()) || (!postcodeEdit->text().isEmpty() && m_payee.postcode() != postcodeEdit->text())); rc |= ((m_payee.telephone().isEmpty() != telephoneEdit->text().isEmpty()) || (!telephoneEdit->text().isEmpty() && m_payee.telephone() != telephoneEdit->text())); rc |= ((m_payee.name().isEmpty() != m_newName.isEmpty()) || (!m_newName.isEmpty() && m_payee.name() != m_newName)); rc |= ((m_payee.notes().isEmpty() != notesEdit->toPlainText().isEmpty()) || (!notesEdit->toPlainText().isEmpty() && m_payee.notes() != notesEdit->toPlainText())); bool ignorecase = false; QStringList keys; MyMoneyPayee::payeeMatchType type = m_payee.matchData(ignorecase, keys); rc |= (static_cast(type) != matchTypeCombo->currentData().toUInt()); checkMatchIgnoreCase->setEnabled(false); matchKeyEditList->setEnabled(false); if (matchTypeCombo->currentData().toUInt() != MyMoneyPayee::matchDisabled) { checkMatchIgnoreCase->setEnabled(true); // if we turn matching on, we default to 'ignore case' // TODO maybe make the default a user option if (type == MyMoneyPayee::matchDisabled && matchTypeCombo->currentData().toUInt() != MyMoneyPayee::matchDisabled) checkMatchIgnoreCase->setChecked(true); rc |= (ignorecase != checkMatchIgnoreCase->isChecked()); if (matchTypeCombo->currentData().toUInt() == MyMoneyPayee::matchKey) { matchKeyEditList->setEnabled(true); rc |= (keys != matchKeyEditList->items()); } } rc |= (checkEnableDefaultCategory->isChecked() != m_payee.defaultAccountEnabled()); if (checkEnableDefaultCategory->isChecked()) { comboDefaultCategory->setEnabled(true); labelDefaultCategory->setEnabled(true); // this is only going to understand the first in the list of selected accounts if (comboDefaultCategory->getSelected().isEmpty()) { rc |= !m_payee.defaultAccountId().isEmpty(); } else { QString temp = comboDefaultCategory->getSelected(); rc |= (temp.isEmpty() != m_payee.defaultAccountId().isEmpty()) || (!m_payee.defaultAccountId().isEmpty() && temp != m_payee.defaultAccountId()); } } else { comboDefaultCategory->setEnabled(false); labelDefaultCategory->setEnabled(false); } rc |= (m_payee.payeeIdentifiers() != payeeIdentifiers->identifiers()); } setDirty(rc); } void KPayeesView::slotUpdatePayee() { if (isDirty()) { MyMoneyFileTransaction ft; setDirty(false); try { m_payee.setName(m_newName); m_payee.setAddress(addressEdit->toPlainText()); m_payee.setPostcode(postcodeEdit->text()); m_payee.setTelephone(telephoneEdit->text()); m_payee.setEmail(emailEdit->text()); m_payee.setNotes(notesEdit->toPlainText()); m_payee.setMatchData(static_cast(matchTypeCombo->currentData().toUInt()), checkMatchIgnoreCase->isChecked(), matchKeyEditList->items()); m_payee.setDefaultAccountId(); m_payee.resetPayeeIdentifiers(payeeIdentifiers->identifiers()); if (checkEnableDefaultCategory->isChecked()) { QString temp; if (!comboDefaultCategory->getSelected().isEmpty()) { temp = comboDefaultCategory->getSelected(); m_payee.setDefaultAccountId(temp); } } MyMoneyFile::instance()->modifyPayee(m_payee); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to modify payee"), i18n("%1 thrown in %2:%3", e.what(), e.file(), e.line())); } } } void KPayeesView::slotSyncAddressBook() { if (m_payeeRows.isEmpty()) { // empty list means no syncing is pending... foreach (auto item, m_payeesList->selectedItems()) { m_payeeRows.append(m_payeesList->row(item)); // ...so initialize one } m_payeesList->clearSelection(); // otherwise slotSelectPayee will be run after every payee update // m_syncAddressbook->setEnabled(false); // disallow concurent syncs } if (m_payeeRows.count() <= m_payeeRow) { KPayeeListItem* item = dynamic_cast(m_payeesList->currentItem()); if (item) { // update ui if something is selected m_payee = item->payee(); addressEdit->setText(m_payee.address()); postcodeEdit->setText(m_payee.postcode()); telephoneEdit->setText(m_payee.telephone()); } m_payeeRows.clear(); // that means end of sync m_payeeRow = 0; return; } KPayeeListItem* item = dynamic_cast(m_payeesList->item(m_payeeRows.at(m_payeeRow))); if (item) m_payee = item->payee(); ++m_payeeRow; m_contact->fetchContact(m_payee.email()); // search for payee's data in addressbook and receive it in slotContactFetched } void KPayeesView::slotContactFetched(const ContactData &identity) { 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() && m_payee.address().compare(txt) != 0) m_payee.setAddress(txt); if (!identity.postalCode.isEmpty() && m_payee.postcode().compare(identity.postalCode) != 0) m_payee.setPostcode(identity.postalCode); if (!identity.phoneNumber.isEmpty() && m_payee.telephone().compare(identity.phoneNumber) != 0) m_payee.setTelephone(identity.phoneNumber); MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->modifyPayee(m_payee); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to modify payee"), i18n("%1 thrown in %2:%3", e.what(), e.file(), e.line())); } } slotSyncAddressBook(); // process next payee } void KPayeesView::slotSendMail() { QRegularExpression re(".+@.+"); if (re.match(m_payee.email()).hasMatch()) QDesktopServices::openUrl(QUrl(QStringLiteral("mailto:?to=") + m_payee.email(), QUrl::TolerantMode)); } void KPayeesView::showEvent(QShowEvent* event) { if (m_needLoad) init(); emit aboutToShow(); if (m_needReload) { loadPayees(); m_needReload = false; } // don't forget base class implementation QWidget::showEvent(event); QList list; selectedPayees(list); emit selectObjects(list); } void KPayeesView::slotLoadPayees() { if (isVisible()) { if (m_inSelection) QTimer::singleShot(0, this, SLOT(slotLoadPayees())); else loadPayees(); } else { m_needReload = true; } } void KPayeesView::loadPayees() { if (m_inSelection) return; QMap isSelected; QString id; MyMoneyFile* file = MyMoneyFile::instance(); // remember which items are selected in the list QList selectedItems = m_payeesList->selectedItems(); QList::const_iterator payeesIt = selectedItems.constBegin(); while (payeesIt != selectedItems.constEnd()) { KPayeeListItem* item = dynamic_cast(*payeesIt); if (item) isSelected[item->payee().id()] = true; ++payeesIt; } // keep current selected item KPayeeListItem *currentItem = static_cast(m_payeesList->currentItem()); if (currentItem) id = currentItem->payee().id(); m_allowEditing = false; // clear the list m_searchWidget->clear(); m_searchWidget->updateSearch(); m_payeesList->clear(); m_register->clear(); currentItem = 0; QListlist = file->payeeList(); QList::ConstIterator it; for (it = list.constBegin(); it != list.constEnd(); ++it) { if (m_payeeFilterType == eAllPayees || (m_payeeFilterType == eReferencedPayees && file->isReferenced(*it)) || (m_payeeFilterType == eUnusedPayees && !file->isReferenced(*it))) { KPayeeListItem* item = new KPayeeListItem(m_payeesList, *it); if (item->payee().id() == id) currentItem = item; if (isSelected[item->payee().id()]) item->setSelected(true); } } m_payeesList->sortItems(); if (currentItem) { m_payeesList->setCurrentItem(currentItem); m_payeesList->scrollToItem(currentItem); } m_filterProxyModel->invalidate(); comboDefaultCategory->expandAll(); slotSelectPayee(0, 0); m_allowEditing = true; } void KPayeesView::slotSelectTransaction() { QList list = m_register->selectedItems(); if (!list.isEmpty()) { KMyMoneyRegister::Transaction* t = dynamic_cast(list[0]); if (t) emit transactionSelected(t->split().accountId(), t->transaction().id()); } } void KPayeesView::slotSelectPayeeAndTransaction(const QString& payeeId, const QString& accountId, const QString& transactionId) { if (!isVisible()) return; try { // clear filter m_searchWidget->clear(); m_searchWidget->updateSearch(); // deselect all other selected items QList selectedItems = m_payeesList->selectedItems(); QList::const_iterator payeesIt = selectedItems.constBegin(); while (payeesIt != selectedItems.constEnd()) { KPayeeListItem* item = dynamic_cast(*payeesIt); if (item) item->setSelected(false); ++payeesIt; } // find the payee in the list QListWidgetItem* it; for (int i = 0; i < m_payeesList->count(); ++i) { it = m_payeesList->item(i); KPayeeListItem* item = dynamic_cast(it); if (item && item->payee().id() == payeeId) { m_payeesList->scrollToItem(it, QAbstractItemView::PositionAtCenter); m_payeesList->setCurrentItem(it); // active item and deselect all others m_payeesList->setCurrentRow(i, QItemSelectionModel::ClearAndSelect); // and select it //make sure the payee selection is updated and transactions are updated accordingly slotSelectPayee(); KMyMoneyRegister::RegisterItem *item = 0; for (int i = 0; i < m_register->rowCount(); ++i) { item = m_register->itemAtRow(i); KMyMoneyRegister::Transaction* t = dynamic_cast(item); if (t) { if (t->transaction().id() == transactionId && t->transaction().accountReferenced(accountId)) { m_register->selectItem(item); m_register->ensureItemVisible(item); break; } } } // quit out of for() loop break; } } } catch (const MyMoneyException &e) { qWarning("Unexpected exception in KPayeesView::slotSelectPayeeAndTransaction %s", qPrintable(e.what())); } } void KPayeesView::slotOpenContextMenu(const QPoint& /*p*/) { KPayeeListItem* item = dynamic_cast(m_payeesList->currentItem()); if (item) { slotSelectPayee(); emit openContextMenu(item->payee()); } } void KPayeesView::slotPayeeNew() { kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::PayeeNew])->trigger(); } void KPayeesView::slotHelp() { KHelpClient::invokeHelp("details.payees"); } void KPayeesView::slotChangeFilter(int index) { //update the filter type then reload the payees list m_payeeFilterType = index; loadPayees(); } bool KPayeesView::isDirty() const { return m_updateButton->isEnabled(); } void KPayeesView::setDirty(bool dirty) { m_updateButton->setEnabled(dirty); } diff --git a/kmymoney/views/kscheduledview.cpp b/kmymoney/views/kscheduledview.cpp index 753115e1d..a8effa180 100644 --- a/kmymoney/views/kscheduledview.cpp +++ b/kmymoney/views/kscheduledview.cpp @@ -1,614 +1,612 @@ /*************************************************************************** kscheduledview.cpp - description ------------------- begin : Sun Jan 27 2002 copyright : (C) 2000-2002 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kscheduledview.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyutils.h" #include "kmymoneyglobalsettings.h" #include "kscheduletreeitem.h" #include "ktreewidgetfilterlinewidget.h" #include "kmymoney.h" #include "icons/icons.h" #include "mymoneyutils.h" #include "mymoneyaccount.h" #include "mymoneyschedule.h" #include "mymoneyfile.h" #include "mymoneypayee.h" -#include "mymoneysplit.h" -#include "mymoneytransaction.h" using namespace Icons; KScheduledView::KScheduledView(QWidget *parent) : QWidget(parent), m_openBills(true), m_openDeposits(true), m_openTransfers(true), m_openLoans(true), m_needLoad(true) { } KScheduledView::~KScheduledView() { if(!m_needLoad) writeConfig(); } void KScheduledView::setDefaultFocus() { QTimer::singleShot(0, m_searchWidget->searchLine(), SLOT(setFocus())); } void KScheduledView::init() { m_needLoad = false; setupUi(this); // create the searchline widget // and insert it into the existing layout m_searchWidget = new KTreeWidgetFilterLineWidget(this, m_scheduleTree); vboxLayout->insertWidget(1, m_searchWidget); //enable custom context menu m_scheduleTree->setContextMenuPolicy(Qt::CustomContextMenu); m_scheduleTree->setSelectionMode(QAbstractItemView::SingleSelection); readConfig(); connect(m_qbuttonNew, SIGNAL(clicked()), kmymoney->actionCollection()->action(kmymoney->s_Actions[Action::ScheduleNew]), SLOT(trigger())); // attach popup to 'Filter...' button m_kaccPopup = new QMenu(this); m_accountsCombo->setMenu(m_kaccPopup); connect(m_kaccPopup, SIGNAL(triggered(QAction*)), this, SLOT(slotAccountActivated())); KGuiItem::assign(m_qbuttonNew, KMyMoneyUtils::scheduleNewGuiItem()); KGuiItem::assign(m_accountsCombo, KMyMoneyUtils::accountsFilterGuiItem()); connect(m_scheduleTree, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotListViewContextMenu(QPoint))); connect(m_scheduleTree, SIGNAL(itemSelectionChanged()), this, SLOT(slotSetSelectedItem())); connect(m_scheduleTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotListItemExecuted(QTreeWidgetItem*,int))); connect(m_scheduleTree, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(slotListViewExpanded(QTreeWidgetItem*))); connect(m_scheduleTree, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(slotListViewCollapsed(QTreeWidgetItem*))); connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotReloadView())); } static bool accountNameLessThan(const MyMoneyAccount& acc1, const MyMoneyAccount& acc2) { return acc1.name().toLower() < acc2.name().toLower(); } void KScheduledView::refresh(bool full, const QString& schedId) { m_scheduleTree->header()->setFont(KMyMoneyGlobalSettings::listHeaderFont()); m_scheduleTree->clear(); try { if (full) { try { m_kaccPopup->clear(); MyMoneyFile* file = MyMoneyFile::instance(); // extract a list of all accounts under the asset group // and sort them by name QList list; QStringList accountList = file->asset().accountList(); accountList.append(file->liability().accountList()); file->accountList(list, accountList, true); qStableSort(list.begin(), list.end(), accountNameLessThan); QList::ConstIterator it_a; for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) { if (!(*it_a).isClosed()) { QAction* act; act = m_kaccPopup->addAction((*it_a).name()); act->setCheckable(true); act->setChecked(true); } } } catch (const MyMoneyException &e) { KMessageBox::detailedError(this, i18n("Unable to load accounts: "), e.what()); } } MyMoneyFile *file = MyMoneyFile::instance(); QList scheduledItems = file->scheduleList(); if (scheduledItems.count() == 0) return; //disable sorting for performance m_scheduleTree->setSortingEnabled(false); KScheduleTreeItem *itemBills = new KScheduleTreeItem(m_scheduleTree); itemBills->setIcon(0, QIcon::fromTheme(g_Icons[Icon::ViewExpense])); itemBills->setText(0, i18n("Bills")); itemBills->setData(0, KScheduleTreeItem::OrderRole, QVariant("0")); itemBills->setFirstColumnSpanned(true); itemBills->setFlags(Qt::ItemIsEnabled); QFont bold = itemBills->font(0); bold.setBold(true); itemBills->setFont(0, bold); KScheduleTreeItem *itemDeposits = new KScheduleTreeItem(m_scheduleTree); itemDeposits->setIcon(0, QIcon::fromTheme(g_Icons[Icon::ViewIncome])); itemDeposits->setText(0, i18n("Deposits")); itemDeposits->setData(0, KScheduleTreeItem::OrderRole, QVariant("1")); itemDeposits->setFirstColumnSpanned(true); itemDeposits->setFlags(Qt::ItemIsEnabled); itemDeposits->setFont(0, bold); KScheduleTreeItem *itemLoans = new KScheduleTreeItem(m_scheduleTree); itemLoans->setIcon(0, QIcon::fromTheme(g_Icons[Icon::ViewLoan])); itemLoans->setText(0, i18n("Loans")); itemLoans->setData(0, KScheduleTreeItem::OrderRole, QVariant("2")); itemLoans->setFirstColumnSpanned(true); itemLoans->setFlags(Qt::ItemIsEnabled); itemLoans->setFont(0, bold); KScheduleTreeItem *itemTransfers = new KScheduleTreeItem(m_scheduleTree); itemTransfers->setIcon(0, QIcon::fromTheme(g_Icons[Icon::ViewFinancialTransfer])); itemTransfers->setText(0, i18n("Transfers")); itemTransfers->setData(0, KScheduleTreeItem::OrderRole, QVariant("3")); itemTransfers->setFirstColumnSpanned(true); itemTransfers->setFlags(Qt::ItemIsEnabled); itemTransfers->setFont(0, bold); QList::Iterator it; QTreeWidgetItem *openItem = 0; for (it = scheduledItems.begin(); it != scheduledItems.end(); ++it) { MyMoneySchedule schedData = (*it); QTreeWidgetItem* item = 0; bool bContinue = true; QStringList::iterator accIt; for (accIt = m_filterAccounts.begin(); accIt != m_filterAccounts.end(); ++accIt) { if (*accIt == schedData.account().id()) { bContinue = false; // Filter it out break; } } if (!bContinue) continue; QTreeWidgetItem* parent = 0; switch (schedData.type()) { case eMyMoney::Schedule::Type::Any: // Should we display an error ? // We just sort it as bill and fall through here case eMyMoney::Schedule::Type::Bill: parent = itemBills; break; case eMyMoney::Schedule::Type::Deposit: parent = itemDeposits; break; case eMyMoney::Schedule::Type::Transfer: parent = itemTransfers; break; case eMyMoney::Schedule::Type::LoanPayment: parent = itemLoans; break; } if (parent) { if (!KMyMoneyGlobalSettings::hideFinishedSchedules() || !schedData.isFinished()) { item = addScheduleItem(parent, schedData); if (schedData.id() == schedId) openItem = item; } } } if (openItem) { m_scheduleTree->setCurrentItem(openItem); } // using a timeout is the only way, I got the 'ensureTransactionVisible' // working when coming from hidden form to visible form. I assume, this // has something to do with the delayed update of the display somehow. resize(width(), height() - 1); QTimer::singleShot(10, this, SLOT(slotTimerDone())); m_scheduleTree->update(); // force repaint in case the filter is set m_searchWidget->searchLine()->updateSearch(QString()); if (m_openBills) itemBills->setExpanded(true); if (m_openDeposits) itemDeposits->setExpanded(true); if (m_openTransfers) itemTransfers->setExpanded(true); if (m_openLoans) itemLoans->setExpanded(true); } catch (const MyMoneyException &e) { KMessageBox::error(this, e.what()); } for (int i = 0; i < m_scheduleTree->columnCount(); ++i) { m_scheduleTree->resizeColumnToContents(i); } //reenable sorting after loading items m_scheduleTree->setSortingEnabled(true); } QTreeWidgetItem* KScheduledView::addScheduleItem(QTreeWidgetItem* parent, MyMoneySchedule& schedule) { KScheduleTreeItem* item = new KScheduleTreeItem(parent); item->setData(0, Qt::UserRole, QVariant::fromValue(schedule)); item->setData(0, KScheduleTreeItem::OrderRole, schedule.name()); if (!schedule.isFinished()) { if (schedule.isOverdue()) { item->setIcon(0, QIcon::fromTheme(g_Icons[Icon::ViewUpcominEvents])); QBrush brush = item->foreground(0); brush.setColor(Qt::red); for (int i = 0; i < m_scheduleTree->columnCount(); ++i) { item->setForeground(i, brush); } } else { item->setIcon(0, QIcon::fromTheme(g_Icons[Icon::ViewCalendarDay])); } } else { item->setIcon(0, QIcon::fromTheme(g_Icons[Icon::DialogClose])); QBrush brush = item->foreground(0); brush.setColor(Qt::darkGreen); for (int i = 0; i < m_scheduleTree->columnCount(); ++i) { item->setForeground(i, brush); } } try { MyMoneyTransaction transaction = schedule.transaction(); MyMoneySplit s1 = (transaction.splits().size() < 1) ? MyMoneySplit() : transaction.splits()[0]; MyMoneySplit s2 = (transaction.splits().size() < 2) ? MyMoneySplit() : transaction.splits()[1]; QList::ConstIterator it_s; MyMoneySplit split; MyMoneyAccount acc; switch (schedule.type()) { case eMyMoney::Schedule::Type::Deposit: if (s1.value().isNegative()) split = s2; else split = s1; break; case eMyMoney::Schedule::Type::LoanPayment: for (it_s = transaction.splits().constBegin(); it_s != transaction.splits().constEnd(); ++it_s) { acc = MyMoneyFile::instance()->account((*it_s).accountId()); if (acc.accountGroup() == eMyMoney::Account::Asset || acc.accountGroup() == eMyMoney::Account::Liability) { if (acc.accountType() != eMyMoney::Account::Loan && acc.accountType() != eMyMoney::Account::AssetLoan) { split = *it_s; break; } } } if (it_s == transaction.splits().constEnd()) { qWarning("Split for payment account not found in %s:%d.", __FILE__, __LINE__); } break; default: if (!s1.value().isPositive()) split = s1; else split = s2; break; } acc = MyMoneyFile::instance()->account(split.accountId()); item->setText(0, schedule.name()); MyMoneySecurity currency = MyMoneyFile::instance()->currency(acc.currencyId()); QString accName = acc.name(); if (!accName.isEmpty()) { item->setText(1, accName); } else { item->setText(1, "---"); } item->setData(1, KScheduleTreeItem::OrderRole, QVariant(accName)); QString payeeName; if (!s1.payeeId().isEmpty()) { payeeName = MyMoneyFile::instance()->payee(s1.payeeId()).name(); item->setText(2, payeeName); } else { item->setText(2, "---"); } item->setData(2, KScheduleTreeItem::OrderRole, QVariant(payeeName)); MyMoneyMoney amount = split.shares().abs(); item->setData(3, Qt::UserRole, QVariant::fromValue(amount)); if (!accName.isEmpty()) { item->setText(3, QString("%1 ").arg(MyMoneyUtils::formatMoney(amount, acc, currency))); } else { //there are some cases where the schedule does not have an account //in those cases the account will not have a fraction //use base currency instead item->setText(3, QString("%1 ").arg(MyMoneyUtils::formatMoney(amount, MyMoneyFile::instance()->baseCurrency()))); } item->setTextAlignment(3, Qt::AlignRight | Qt::AlignVCenter); item->setData(3, KScheduleTreeItem::OrderRole, QVariant::fromValue(amount)); // Do the real next payment like ms-money etc QDate nextDueDate; if (schedule.isFinished()) { item->setText(4, i18nc("Finished schedule", "Finished")); } else { nextDueDate = schedule.adjustedNextDueDate(); item->setText(4, QLocale().toString(schedule.adjustedNextDueDate(), QLocale::ShortFormat)); } item->setData(4, KScheduleTreeItem::OrderRole, QVariant(nextDueDate)); item->setText(5, i18nc("Frequency of schedule", schedule.occurrenceToString().toLatin1())); item->setText(6, KMyMoneyUtils::paymentMethodToString(schedule.paymentType())); } catch (const MyMoneyException &e) { item->setText(0, "Error:"); item->setText(1, e.what()); } return item; } void KScheduledView::slotTimerDone() { QTreeWidgetItem* item; item = m_scheduleTree->currentItem(); if (item) { m_scheduleTree->scrollToItem(item); } // force a repaint of all items to update the branches /*for (item = m_scheduleTree->item(0); item != 0; item = m_scheduleTree->item(m_scheduleTree->row(item) + 1)) { m_scheduleTree->repaintItem(item); } resize(width(), height() + 1);*/ } void KScheduledView::slotReloadView() { m_needReload = true; if (isVisible()) { m_qbuttonNew->setEnabled(true); refresh(true, m_selectedSchedule); m_needReload = false; QTimer::singleShot(50, this, SLOT(slotRearrange())); } } void KScheduledView::showEvent(QShowEvent* event) { if (m_needLoad) init(); emit aboutToShow(); if (m_needReload) slotReloadView(); QWidget::showEvent(event); } void KScheduledView::slotRearrange() { resizeEvent(0); } void KScheduledView::readConfig() { KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup grp = config->group("Last Use Settings"); m_openBills = grp.readEntry("KScheduleView_openBills", true); m_openDeposits = grp.readEntry("KScheduleView_openDeposits", true); m_openTransfers = grp.readEntry("KScheduleView_openTransfers", true); m_openLoans = grp.readEntry("KScheduleView_openLoans", true); QByteArray columns; columns = grp.readEntry("KScheduleView_treeState", columns); m_scheduleTree->header()->restoreState(columns); m_scheduleTree->header()->setFont(KMyMoneyGlobalSettings::listHeaderFont()); } void KScheduledView::writeConfig() { KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup grp = config->group("Last Use Settings"); grp.writeEntry("KScheduleView_openBills", m_openBills); grp.writeEntry("KScheduleView_openDeposits", m_openDeposits); grp.writeEntry("KScheduleView_openTransfers", m_openTransfers); grp.writeEntry("KScheduleView_openLoans", m_openLoans); QByteArray columns = m_scheduleTree->header()->saveState(); grp.writeEntry("KScheduleView_treeState", columns); config->sync(); } void KScheduledView::slotListViewContextMenu(const QPoint& pos) { QTreeWidgetItem* item = m_scheduleTree->itemAt(pos); if (item) { try { MyMoneySchedule schedule = item->data(0, Qt::UserRole).value(); emit scheduleSelected(schedule); m_selectedSchedule = schedule.id(); emit openContextMenu(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Error activating context menu"), e.what()); } } else { emit openContextMenu(); } } void KScheduledView::slotListItemExecuted(QTreeWidgetItem* item, int) { if (!item) return; try { MyMoneySchedule schedule = item->data(0, Qt::UserRole).value(); m_selectedSchedule = schedule.id(); emit editSchedule(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(this, i18n("Error executing item"), e.what()); } } void KScheduledView::slotAccountActivated() { m_filterAccounts.clear(); try { int accountCount = 0; MyMoneyFile* file = MyMoneyFile::instance(); // extract a list of all accounts under the asset and liability groups // and sort them by name QList list; QStringList accountList = file->asset().accountList(); accountList.append(file->liability().accountList()); file->accountList(list, accountList, true); qStableSort(list.begin(), list.end(), accountNameLessThan); QList::ConstIterator it_a; for (it_a = list.constBegin(); it_a != list.constEnd(); ++it_a) { if (!(*it_a).isClosed()) { if (!m_kaccPopup->actions().value(accountCount)->isChecked()) { m_filterAccounts.append((*it_a).id()); } ++accountCount; } } refresh(false, m_selectedSchedule); } catch (const MyMoneyException &e) { KMessageBox::detailedError(this, i18n("Unable to filter account"), e.what()); } } void KScheduledView::slotListViewExpanded(QTreeWidgetItem* item) { if (item) { if (item->text(0) == i18n("Bills")) m_openBills = true; else if (item->text(0) == i18n("Deposits")) m_openDeposits = true; else if (item->text(0) == i18n("Transfers")) m_openTransfers = true; else if (item->text(0) == i18n("Loans")) m_openLoans = true; } } void KScheduledView::slotListViewCollapsed(QTreeWidgetItem* item) { if (item) { if (item->text(0) == i18n("Bills")) m_openBills = false; else if (item->text(0) == i18n("Deposits")) m_openDeposits = false; else if (item->text(0) == i18n("Transfers")) m_openTransfers = false; else if (item->text(0) == i18n("Loans")) m_openLoans = false; } } void KScheduledView::slotSelectSchedule(const QString& schedule) { refresh(true, schedule); } void KScheduledView::slotBriefEnterClicked(const MyMoneySchedule& schedule, const QDate& date) { Q_UNUSED(date); emit scheduleSelected(schedule); emit enterSchedule(); } void KScheduledView::slotBriefSkipClicked(const MyMoneySchedule& schedule, const QDate& date) { Q_UNUSED(date); emit scheduleSelected(schedule); emit skipSchedule(); } void KScheduledView::slotSetSelectedItem() { emit scheduleSelected(MyMoneySchedule()); QTreeWidgetItem* item = m_scheduleTree->currentItem(); if (item) { try { MyMoneySchedule schedule = item->data(0, Qt::UserRole).value(); emit scheduleSelected(schedule); m_selectedSchedule = schedule.id(); } catch (const MyMoneyException &e) { qDebug("KScheduledView::slotSetSelectedItem: %s", qPrintable(e.what())); } } } diff --git a/kmymoney/views/ktagsview.cpp b/kmymoney/views/ktagsview.cpp index c8a15ebe6..a59a61409 100644 --- a/kmymoney/views/ktagsview.cpp +++ b/kmymoney/views/ktagsview.cpp @@ -1,610 +1,608 @@ /*************************************************************************** ktagsview.cpp ------------- begin : Sat Oct 13 2012 copyright : (C) 2012 by Alessandro Russo (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "ktagsview.h" #include "ktagsview_p.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyprice.h" #include "kmymoneyglobalsettings.h" #include "mymoneysecurity.h" -#include "mymoneysplit.h" -#include "mymoneytransaction.h" using namespace Icons; /* -------------------------------------------------------------------------*/ /* KTransactionPtrVector */ /* -------------------------------------------------------------------------*/ // *** KTagListItem Implementation *** /** * This class represents an item in the tags list view. */ class KTagListItem : public QListWidgetItem { public: /** * Constructor to be used to construct a tag entry object. * * @param parent pointer to the QListWidget object this entry should be * added to. * @param tag const reference to MyMoneyTag for which * the QListWidget entry is constructed */ explicit KTagListItem(QListWidget *parent, const MyMoneyTag& tag); ~KTagListItem(); MyMoneyTag tag() const; private: MyMoneyTag m_tag; }; KTagListItem::KTagListItem(QListWidget *parent, const MyMoneyTag& tag) : QListWidgetItem(parent, QListWidgetItem::UserType), m_tag(tag) { setText(tag.name()); // allow in column rename setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); } KTagListItem::~KTagListItem() { } MyMoneyTag KTagListItem::tag() const { return m_tag; } // *** KTagsView Implementation *** KTagsView::KTagsView(QWidget *parent) : KMyMoneyViewBase(*new KTagsViewPrivate(this), parent) { Q_D(KTagsView); d->m_needLoad = true; d->m_inSelection = false; d->m_allowEditing = true; d->m_tagFilterType = 0; } KTagsView::~KTagsView() { Q_D(KTagsView); if (!d->m_needLoad) { // remember the splitter settings for startup KConfigGroup grp = KSharedConfig::openConfig()->group("Last Use Settings"); grp.writeEntry("KTagsViewSplitterSize", d->ui->m_splitter->saveState()); grp.sync(); } } void KTagsView::setDefaultFocus() { Q_D(KTagsView); QTimer::singleShot(0, d->m_searchWidget, SLOT(setFocus())); } void KTagsView::refresh() { Q_D(KTagsView); if (isVisible()) { if (d->m_inSelection) QTimer::singleShot(0, this, SLOT(refresh())); else loadTags(); d->m_needsRefresh = false; } else { d->m_needsRefresh = true; } } void KTagsView::slotStartRename(QListWidgetItem* item) { Q_D(KTagsView); d->m_allowEditing = true; d->ui->m_tagsList->editItem(item); } void KTagsView::slotRenameButtonCliked() { Q_D(KTagsView); if (d->ui->m_tagsList->currentItem() && d->ui->m_tagsList->selectedItems().count() == 1) { slotStartRename(d->ui->m_tagsList->currentItem()); } } // This variant is only called when a single tag is selected and renamed. void KTagsView::slotRenameTag(QListWidgetItem* ta) { Q_D(KTagsView); //if there is no current item selected, exit if (d->m_allowEditing == false || !d->ui->m_tagsList->currentItem() || ta != d->ui->m_tagsList->currentItem()) return; //qDebug() << "[KTagsView::slotRenameTag]"; // create a copy of the new name without appended whitespaces QString new_name = ta->text(); if (d->m_tag.name() != new_name) { MyMoneyFileTransaction ft; try { // check if we already have a tag with the new name try { // this function call will throw an exception, if the tag // hasn't been found. MyMoneyFile::instance()->tagByName(new_name); // the name already exists, ask the user whether he's sure to keep the name if (KMessageBox::questionYesNo(this, i18n("A tag with the name '%1' already exists. It is not advisable to have " "multiple tags with the same identification name. Are you sure you would like " "to rename the tag?", new_name)) != KMessageBox::Yes) { ta->setText(d->m_tag.name()); return; } } catch (const MyMoneyException &) { // all ok, the name is unique } d->m_tag.setName(new_name); d->m_newName = new_name; MyMoneyFile::instance()->modifyTag(d->m_tag); // the above call to modifyTag 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 ensureTagVisible(d->m_tag.id()); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to modify tag"), i18n("%1 thrown in %2:%3", e.what(), e.file(), e.line())); } } else { ta->setText(new_name); } } void KTagsView::ensureTagVisible(const QString& id) { Q_D(KTagsView); for (int i = 0; i < d->ui->m_tagsList->count(); ++i) { KTagListItem* ta = dynamic_cast(d->ui->m_tagsList->item(0)); if (ta && ta->tag().id() == id) { d->ui->m_tagsList->scrollToItem(ta, QAbstractItemView::PositionAtCenter); d->ui->m_tagsList->setCurrentItem(ta); // active item and deselect all others d->ui->m_tagsList->setCurrentRow(i, QItemSelectionModel::ClearAndSelect); // and select it break; } } } void KTagsView::selectedTags(QList& tagsList) const { Q_D(const KTagsView); QList selectedItems = d->ui->m_tagsList->selectedItems(); QList::ConstIterator itemsIt = selectedItems.constBegin(); while (itemsIt != selectedItems.constEnd()) { KTagListItem* item = dynamic_cast(*itemsIt); if (item) tagsList << item->tag(); ++itemsIt; } } void KTagsView::slotSelectTag(QListWidgetItem* cur, QListWidgetItem* prev) { Q_D(KTagsView); Q_UNUSED(cur); Q_UNUSED(prev); d->m_allowEditing = false; } void KTagsView::slotSelectTag() { Q_D(KTagsView); // check if the content of a currently selected tag was modified // and ask to store the data if (d->ui->m_updateButton->isEnabled()) { if (KMessageBox::questionYesNo(this, QString("%1").arg( i18n("Do you want to save the changes for %1?", d->m_newName)), i18n("Save changes")) == KMessageBox::Yes) { d->m_inSelection = true; slotUpdateTag(); d->m_inSelection = false; } } // loop over all tags and count the number of tags, also // obtain last selected tag QList tagsList; selectedTags(tagsList); emit selectObjects(tagsList); if (tagsList.isEmpty()) { d->ui->m_tabWidget->setEnabled(false); // disable tab widget d->ui->m_balanceLabel->hide(); d->ui->m_deleteButton->setEnabled(false); //disable delete and rename button d->ui->m_renameButton->setEnabled(false); clearItemData(); d->m_tag = MyMoneyTag(); return; // make sure we don't access an undefined tag } d->ui->m_deleteButton->setEnabled(true); //re-enable delete button // if we have multiple tags selected, clear and disable the tag information if (tagsList.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_balanceLabel->hide(); clearItemData(); } else d->ui->m_renameButton->setEnabled(true); // otherwise we have just one selected, enable tag information widget and renameButton d->ui->m_tabWidget->setEnabled(true); d->ui->m_balanceLabel->show(); // as of now we are updating only the last selected tag, 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_tag = tagsList[0]; d->m_newName = d->m_tag.name(); d->ui->m_colorbutton->setEnabled(true); d->ui->m_colorbutton->setColor(d->m_tag.tagColor()); d->ui->m_closed->setEnabled(true); d->ui->m_closed->setChecked(d->m_tag.isClosed()); d->ui->m_notes->setEnabled(true); d->ui->m_notes->setText(d->m_tag.notes()); slotTagDataChanged(); showTransactions(); } catch (const MyMoneyException &e) { qDebug("exception during display of tag: %s at %s:%ld", qPrintable(e.what()), qPrintable(e.file()), e.line()); d->ui->m_register->clear(); d->m_tag = MyMoneyTag(); } d->m_allowEditing = true; } void KTagsView::clearItemData() { Q_D(KTagsView); d->ui->m_colorbutton->setColor(QColor()); d->ui->m_closed->setChecked(false); d->ui->m_notes->setText(QString()); showTransactions(); } void KTagsView::showTransactions() { Q_D(KTagsView); MyMoneyMoney balance; auto file = MyMoneyFile::instance(); MyMoneySecurity base = file->baseCurrency(); // setup sort order d->ui->m_register->setSortOrder(KMyMoneyGlobalSettings::sortSearchView()); // clear the register d->ui->m_register->clear(); if (d->m_tag.id().isEmpty() || !d->ui->m_tabWidget->isEnabled()) { d->ui->m_balanceLabel->setText(i18n("Balance: %1", balance.formatMoney(file->baseCurrency().smallestAccountFraction()))); return; } // setup the list and the pointer vector MyMoneyTransactionFilter filter; filter.addTag(d->m_tag.id()); filter.setDateFilter(KMyMoneyGlobalSettings::startDate().date(), QDate()); // retrieve the list from the engine file->transactionList(d->m_transactionList, filter); // create the elements for the register QList >::const_iterator it; QMap uniqueMap; MyMoneyMoney deposit, payment; int splitCount = 0; bool balanceAccurate = true; for (it = d->m_transactionList.constBegin(); it != d->m_transactionList.constEnd(); ++it) { const MyMoneySplit& split = (*it).second; MyMoneyAccount acc = file->account(split.accountId()); ++splitCount; uniqueMap[(*it).first.id()]++; KMyMoneyRegister::Register::transactionFactory(d->ui->m_register, (*it).first, (*it).second, uniqueMap[(*it).first.id()]); // take care of foreign currencies MyMoneyMoney val = split.shares().abs(); if (acc.currencyId() != base.id()) { const MyMoneyPrice &price = file->price(acc.currencyId(), base.id()); // in case the price is valid, we use it. Otherwise, we keep // a flag that tells us that the balance is somewhat inaccurate if (price.isValid()) { val *= price.rate(base.id()); } else { balanceAccurate = false; } } if (split.shares().isNegative()) { payment += val; } else { deposit += val; } } balance = deposit - payment; // add the group markers d->ui->m_register->addGroupMarkers(); // sort the transactions according to the sort setting d->ui->m_register->sortItems(); // remove trailing and adjacent markers d->ui->m_register->removeUnwantedGroupMarkers(); d->ui->m_register->updateRegister(true); // we might end up here with updates disabled on the register so // make sure that we enable updates here d->ui->m_register->setUpdatesEnabled(true); d->ui->m_balanceLabel->setText(i18n("Balance: %1%2", balanceAccurate ? "" : "~", balance.formatMoney(file->baseCurrency().smallestAccountFraction()))); } void KTagsView::slotTagDataChanged() { Q_D(KTagsView); auto rc = false; if (d->ui->m_tabWidget->isEnabled()) { rc |= ((d->m_tag.tagColor().isValid() != d->ui->m_colorbutton->color().isValid()) || (d->ui->m_colorbutton->color().isValid() && d->m_tag.tagColor() != d->ui->m_colorbutton->color())); rc |= (d->ui->m_closed->isChecked() != d->m_tag.isClosed()); rc |= ((d->m_tag.notes().isEmpty() != d->ui->m_notes->toPlainText().isEmpty()) || (!d->ui->m_notes->toPlainText().isEmpty() && d->m_tag.notes() != d->ui->m_notes->toPlainText())); } d->ui->m_updateButton->setEnabled(rc); } void KTagsView::slotUpdateTag() { Q_D(KTagsView); if (d->ui->m_updateButton->isEnabled()) { MyMoneyFileTransaction ft; d->ui->m_updateButton->setEnabled(false); try { d->m_tag.setName(d->m_newName); d->m_tag.setTagColor(d->ui->m_colorbutton->color()); d->m_tag.setClosed(d->ui->m_closed->isChecked()); d->m_tag.setNotes(d->ui->m_notes->toPlainText()); MyMoneyFile::instance()->modifyTag(d->m_tag); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(0, i18n("Unable to modify tag"), i18n("%1 thrown in %2:%3", e.what(), e.file(), e.line())); } } } void KTagsView::showEvent(QShowEvent* event) { Q_D(KTagsView); if (d->m_needLoad) d->init(); emit aboutToShow(View::Tags); if (d->m_needsRefresh) refresh(); // don't forget base class implementation QWidget::showEvent(event); QList list; selectedTags(list); emit selectObjects(list); } void KTagsView::loadTags() { Q_D(KTagsView); if (d->m_inSelection) return; QMap isSelected; QString id; MyMoneyFile* file = MyMoneyFile::instance(); // remember which items are selected in the list QList selectedItems = d->ui->m_tagsList->selectedItems(); QList::const_iterator tagsIt = selectedItems.constBegin(); while (tagsIt != selectedItems.constEnd()) { KTagListItem* item = dynamic_cast(*tagsIt); if (item) isSelected[item->tag().id()] = true; ++tagsIt; } // keep current selected item KTagListItem *currentItem = static_cast(d->ui->m_tagsList->currentItem()); if (currentItem) id = currentItem->tag().id(); d->m_allowEditing = false; // clear the list d->m_searchWidget->clear(); d->m_searchWidget->updateSearch(); d->ui->m_tagsList->clear(); d->ui->m_register->clear(); currentItem = 0; QListlist = file->tagList(); QList::ConstIterator it; for (it = list.constBegin(); it != list.constEnd(); ++it) { if (d->m_tagFilterType == (int)eView::Tag::All || (d->m_tagFilterType == (int)eView::Tag::Referenced && file->isReferenced(*it)) || (d->m_tagFilterType == (int)eView::Tag::Unused && !file->isReferenced(*it)) || (d->m_tagFilterType == (int)eView::Tag::Opened && !(*it).isClosed()) || (d->m_tagFilterType == (int)eView::Tag::Closed && (*it).isClosed())) { KTagListItem* item = new KTagListItem(d->ui->m_tagsList, *it); if (item->tag().id() == id) currentItem = item; if (isSelected[item->tag().id()]) item->setSelected(true); } } d->ui->m_tagsList->sortItems(); if (currentItem) { d->ui->m_tagsList->setCurrentItem(currentItem); d->ui->m_tagsList->scrollToItem(currentItem); } slotSelectTag(0, 0); d->m_allowEditing = true; } void KTagsView::slotSelectTransaction() { Q_D(KTagsView); QList list = d->ui->m_register->selectedItems(); if (!list.isEmpty()) { KMyMoneyRegister::Transaction* t = dynamic_cast(list[0]); if (t) emit transactionSelected(t->split().accountId(), t->transaction().id()); } } void KTagsView::slotSelectTagAndTransaction(const QString& tagId, const QString& accountId, const QString& transactionId) { if (!isVisible()) return; Q_D(KTagsView); try { // clear filter d->m_searchWidget->clear(); d->m_searchWidget->updateSearch(); // deselect all other selected items QList selectedItems = d->ui->m_tagsList->selectedItems(); QList::const_iterator tagsIt = selectedItems.constBegin(); while (tagsIt != selectedItems.constEnd()) { KTagListItem* item = dynamic_cast(*tagsIt); if (item) item->setSelected(false); ++tagsIt; } // find the tag in the list QListWidgetItem* it; for (int i = 0; i < d->ui->m_tagsList->count(); ++i) { it = d->ui->m_tagsList->item(i); KTagListItem* item = dynamic_cast(it); if (item && item->tag().id() == tagId) { d->ui->m_tagsList->scrollToItem(it, QAbstractItemView::PositionAtCenter); d->ui->m_tagsList->setCurrentItem(it); // active item and deselect all others d->ui->m_tagsList->setCurrentRow(i, QItemSelectionModel::ClearAndSelect); // and select it //make sure the tag selection is updated and transactions are updated accordingly slotSelectTag(); KMyMoneyRegister::RegisterItem *item = 0; for (int i = 0; i < d->ui->m_register->rowCount(); ++i) { item = d->ui->m_register->itemAtRow(i); KMyMoneyRegister::Transaction* t = dynamic_cast(item); if (t) { if (t->transaction().id() == transactionId && t->transaction().accountReferenced(accountId)) { d->ui->m_register->selectItem(item); d->ui->m_register->ensureItemVisible(item); break; } } } // quit out of for() loop break; } } } catch (const MyMoneyException &e) { qWarning("Unexpected exception in KTagsView::slotSelectTagAndTransaction %s", qPrintable(e.what())); } } void KTagsView::slotSelectTagAndTransaction(const QString& tagId) { slotSelectTagAndTransaction(tagId, QString(), QString()); } void KTagsView::slotOpenContextMenu(const QPoint& /*ta*/) { Q_D(KTagsView); auto item = dynamic_cast(d->ui->m_tagsList->currentItem()); if (item) { slotSelectTag(); emit openContextMenu(item->tag()); } } void KTagsView::slotHelp() { KHelpClient::invokeHelp("details.tags.attributes"); //FIXME-ALEX update help file } void KTagsView::slotChangeFilter(int index) { Q_D(KTagsView); //update the filter type then reload the tags list d->m_tagFilterType = index; loadTags(); } diff --git a/kmymoney/views/ktagsview_p.h b/kmymoney/views/ktagsview_p.h index 149a6378a..6595a2d0d 100644 --- a/kmymoney/views/ktagsview_p.h +++ b/kmymoney/views/ktagsview_p.h @@ -1,182 +1,181 @@ /*************************************************************************** ktagsview_p.h ------------- begin : Sat Oct 13 2012 copyright : (C) 2012 by Alessandro Russo (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KTAGSVIEW_P_H #define KTAGSVIEW_P_H #include "ktagsview.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_ktagsview.h" #include "kmymoneyviewbase_p.h" -#include "mymoneyaccount.h" #include "mymoneyfile.h" #include "mymoneytag.h" #include "mymoneytransactionfilter.h" #include "icons.h" #include "viewenums.h" using namespace Icons; namespace Ui { class KTagsView; } class KTagsViewPrivate : public KMyMoneyViewBasePrivate { Q_DECLARE_PUBLIC(KTagsView) public: KTagsViewPrivate(KTagsView *qq) : q_ptr(qq), ui(new Ui::KTagsView) { } ~KTagsViewPrivate() { delete ui; } void init() { Q_Q(KTagsView); m_needLoad = false; ui->setupUi(q); // create the searchline widget // and insert it into the existing layout m_searchWidget = new KListWidgetSearchLine(q, ui->m_tagsList); m_searchWidget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); ui->m_tagsList->setContextMenuPolicy(Qt::CustomContextMenu); ui->m_listTopHLayout->insertWidget(0, m_searchWidget); //load the filter type ui->m_filterBox->addItem(i18nc("@item Show all tags", "All")); ui->m_filterBox->addItem(i18nc("@item Show only used tags", "Used")); ui->m_filterBox->addItem(i18nc("@item Show only unused tags", "Unused")); ui->m_filterBox->addItem(i18nc("@item Show only opened tags", "Opened")); ui->m_filterBox->addItem(i18nc("@item Show only closed tags", "Closed")); ui->m_filterBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); ui->m_newButton->setIcon(QIcon::fromTheme(g_Icons[Icon::ListAddTag])); ui->m_renameButton->setIcon(QIcon::fromTheme(g_Icons[Icon::EditRename])); ui->m_deleteButton->setIcon(QIcon::fromTheme(g_Icons[Icon::ListRemoveTag])); ui->m_updateButton->setIcon(QIcon::fromTheme(g_Icons[Icon::DialogOK])); ui->m_updateButton->setEnabled(false); ui->m_register->setupRegister(MyMoneyAccount(), QList { KMyMoneyRegister::DateColumn, KMyMoneyRegister::AccountColumn, KMyMoneyRegister::DetailColumn, KMyMoneyRegister::ReconcileFlagColumn, KMyMoneyRegister::PaymentColumn, KMyMoneyRegister::DepositColumn }); ui->m_register->setSelectionMode(QTableWidget::SingleSelection); ui->m_register->setDetailsColumnType(KMyMoneyRegister::AccountFirst); ui->m_balanceLabel->hide(); q->connect(ui->m_tagsList, &QListWidget::currentItemChanged, q, static_cast(&KTagsView::slotSelectTag)); q->connect(ui->m_tagsList, &QListWidget::itemSelectionChanged, q, static_cast(&KTagsView::slotSelectTag)); q->connect(ui->m_tagsList, &QListWidget::itemDoubleClicked, q, &KTagsView::slotStartRename); q->connect(ui->m_tagsList, &QListWidget::itemChanged, q, &KTagsView::slotRenameTag); q->connect(ui->m_tagsList, &QWidget::customContextMenuRequested, q, &KTagsView::slotOpenContextMenu); q->connect(ui->m_newButton, &QAbstractButton::clicked, q, &KTagsView::tagNewClicked); q->connect(ui->m_renameButton, &QAbstractButton::clicked, q, &KTagsView::slotRenameButtonCliked); q->connect(ui->m_deleteButton, &QAbstractButton::clicked, q, &KTagsView::tagDeleteClicked); q->connect(ui->m_colorbutton, &KColorButton::changed, q, &KTagsView::slotTagDataChanged); q->connect(ui->m_closed, &QCheckBox::stateChanged, q, &KTagsView::slotTagDataChanged); q->connect(ui->m_notes, &QTextEdit::textChanged, q, &KTagsView::slotTagDataChanged); q->connect(ui->m_updateButton, &QAbstractButton::clicked, q, &KTagsView::slotUpdateTag); q->connect(ui->m_helpButton, &QAbstractButton::clicked, q, &KTagsView::slotHelp); q->connect(ui->m_register, &KMyMoneyRegister::Register::editTransaction, q, &KTagsView::slotSelectTransaction); q->connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KTagsView::refresh); q->connect(ui->m_filterBox, static_cast(&QComboBox::currentIndexChanged), q, &KTagsView::slotChangeFilter); // use the size settings of the last run (if any) auto grp = KSharedConfig::openConfig()->group("Last Use Settings"); ui->m_splitter->restoreState(grp.readEntry("KTagsViewSplitterSize", QByteArray())); ui->m_splitter->setChildrenCollapsible(false); // At start we haven't any tag selected ui->m_tabWidget->setEnabled(false); // disable tab widget ui->m_deleteButton->setEnabled(false); // disable delete and rename button ui->m_renameButton->setEnabled(false); m_tag = MyMoneyTag(); // make sure we don't access an undefined tag q->clearItemData(); } KTagsView *q_ptr; Ui::KTagsView *ui; MyMoneyTag m_tag; QString m_newName; /** * This member holds a list of all transactions */ QList > m_transactionList; /** * This member holds the load state of page */ bool m_needLoad; /** * Search widget for the list */ KListWidgetSearchLine* m_searchWidget; /** * Semaphore to suppress loading during selection */ bool m_inSelection; /** * This signals whether a tag can be edited **/ bool m_allowEditing; /** * This holds the filter type */ int m_tagFilterType; }; #endif diff --git a/kmymoney/widgets/CMakeLists.txt b/kmymoney/widgets/CMakeLists.txt index f4f63aa9f..5e6b5d072 100644 --- a/kmymoney/widgets/CMakeLists.txt +++ b/kmymoney/widgets/CMakeLists.txt @@ -1,218 +1,201 @@ ########### create links ############### set(kmymoney_STAT_HEADERS kaccounttemplateselector.h kbudgetvalues.h kguiutils.h kmymoneyaccountcombo.h kmymoneyaccountcompletion.h kmymoneyaccountselector.h kmymoneycategory.h kmymoneycombo.h kmymoneymvccombo.h kmymoneycompletion.h kmymoneycurrencyselector.h kmymoneydateinput.h kmymoneyedit.h kmymoneylineedit.h kmymoneyselector.h - kmymoneytitlelabel.h kmymoneywizard.h register.h registeritem.h groupmarker.h fancydategroupmarker.h - scheduledtransaction.h selectedtransaction.h selectedtransactions.h stdtransactiondownloaded.h + kmymoneytitlelabel.h kmymoneywizard.h register.h registeritem.h + scheduledtransaction.h selectedtransaction.h stdtransactiondownloaded.h stdtransactionmatched.h transactioneditorcontainer.h - transactionform.h transaction.h investtransaction.h stdtransaction.h - transactionsortoption.h reporttabimpl.h reportcontrolimpl.h + transactionform.h transaction.h transactionsortoptionimpl.h + reporttabimpl.h reportcontrolimpl.h kmymoneyvalidationfeedback.h onlinejobmessagesview.h kmymoneydateedit.h amountedit.h ) ########### Shared widget library ########### set(kmm_widgets_sources kmymoneydateinput.cpp kmymoneyvalidationfeedback.cpp styleditemdelegateforwarder.cpp kmymoneyedit.cpp kmymoneylineedit.cpp kmymoneytextedit.cpp kmymoneymvccombo.cpp - kmymoneygeneralcombo.cpp - kmymoneyactivitycombo.cpp - kmymoneycashflowcombo.cpp - kmymoneyfrequencycombo.cpp - kmymoneyoccurrencecombo.cpp - kmymoneyoccurrenceperiodcombo.cpp - kmymoneypayeecombo.cpp - kmymoneyperiodcombo.cpp - kmymoneyreconcilecombo.cpp - kmymoneytagcombo.cpp - ktagcontainer.cpp - ktaglabel.cpp kmymoneyselector.cpp kmymoneycalculator.cpp ktreewidgetfilterlinewidget.cpp kguiutils.cpp onlinejobmessagesview.cpp kmymoneydateedit.cpp amountedit.cpp ) ki18n_wrap_ui(kmm_widgets_sources kmymoneyvalidationfeedback.ui onlinejobmessagesview.ui ) add_library(kmm_widgets SHARED ${kmm_widgets_sources}) target_link_libraries(kmm_widgets PUBLIC KF5::TextWidgets KF5::KIOWidgets KF5::Completion KF5::Notifications KF5::ItemViews KF5::I18n Qt5::Gui Qt5::Sql Qt5::Core Alkimia::alkimia kmm_config kmm_mymoney ) set_target_properties(kmm_widgets PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} COMPILE_FLAGS "-DKMM_BUILD_WIDGETS_LIB" ) generate_export_header(kmm_widgets) install(TARGETS kmm_widgets ${INSTALL_TARGETS_DEFAULT_ARGS} ) ########### Basic Widget Library (kmymoney_base) STATIC ################# # Common sources for libkmymoney.so and libwidgets.a that do not # contain the KMM_DESIGNER flag set(_uncritial_common_sources fixedcolumntreeview.cpp kbudgetvalues.cpp kmymoneyaccountcombo.cpp kmymoneyaccountselector.cpp kmymoneyaccounttreeview.cpp accountsviewproxymodel.cpp budgetviewproxymodel.cpp kmymoneycombo.cpp kmymoneycompletion.cpp kmymoneytitlelabel.cpp kmymoneydateedit.cpp kmymoneywizard.cpp kpricetreeitem.cpp registeritem.cpp scheduledtransaction.cpp selectedtransaction.cpp - selectedtransactions.cpp stdtransactiondownloaded.cpp stdtransactionmatched.cpp transactionform.cpp - transactionsortoption.cpp + transactionsortoptionimpl.cpp ) # sources that contain the KMM_DESIGNER flag set (_critial_common_sources kaccounttemplateselector.cpp kmymoneycurrencyselector.cpp kmymoneyaccountcompletion.cpp kmymoneycategory.cpp - groupmarker.cpp - fancydategroupmarker.cpp register.cpp transaction.cpp - stdtransaction.cpp - investtransaction.cpp transactioneditorcontainer.cpp ) set (kmymoney_base_UI - kbudgetvalues.ui transactionsortoption.ui kaccounttemplateselector.ui + kbudgetvaluesdecl.ui transactionsortoptiondecl.ui kaccounttemplateselectordecl.ui ) ki18n_wrap_ui(kmymoney_base_ui_srcs ${kmymoney_base_UI}) set(_uncritial_common_sources ${_uncritial_common_sources} ${kmymoney_base_ui_srcs}) # in order to use add_dependencies, we need to add this custom target # for all generated header files. # (see http://www.vtk.org/Wiki/CMake_FAQ#How_can_I_add_a_dependency_to_a_source_file_which_is_generated_in_a_subdirectory.3F ) add_custom_target(generate_base_ui_srcs DEPENDS ${kmymoney_base_ui_srcs}) # We can compile the uncritical sources without KMM_DESIGNER flags add_library(kmymoney_base STATIC ${_uncritial_common_sources}) # TODO: fix dependencies target_link_libraries(kmymoney_base KF5::XmlGui KF5::TextWidgets KF5::IconThemes KF5::I18n KF5::ConfigWidgets KF5::ConfigCore KF5::Completion KF5::Service Qt5::Gui Qt5::Widgets Qt5::Sql Qt5::Xml Alkimia::alkimia) add_dependencies(kmymoney_base kmm_config) ########### QtDesigner Widget Library (kmymoneywidgets) ################# # we never link against this library, # but it is needed for uic and QtDesigner if( USE_QT_DESIGNER ) set(kmymoneywidgets_PART_SRCS ${CMAKE_CURRENT_BINARY_DIR}/kmymoneywidgets.cpp) kde4_add_widget_files(kmymoneywidgets_PART_SRCS kmymoney.widgets) set(kmymoneywidgets_PART_SRCS ${_critial_common_sources} ${kmymoneywidgets_PART_SRCS}) add_library(kmymoneywidgets MODULE ${kmymoneywidgets_PART_SRCS}) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) # The option -DKMM_DESIGNER will leave away any code that breaks uic. set_target_properties(kmymoneywidgets PROPERTIES COMPILE_FLAGS "-DKMM_DESIGNER") # The qt-designer widget library shouldn't need to link against the # dialogs and converter libraries. If a widget references something # from one of these libraries it is most likely due to code that needs # to be excluded with a KMM_DESIGNER ifndef. target_link_libraries(kmymoneywidgets kmymoney_base kmm_mymoney kmymoney_common kmm_config models) install(TARGETS kmymoneywidgets DESTINATION ${QT_PLUGINS_DIR}/designer ) endif( USE_QT_DESIGNER ) ########### Widget Library (widgets) STATIC ################# set(libwidgets_a_SOURCES ${_critial_common_sources} kmymoneybriefschedule.cpp registersearchline.cpp transactioneditorcontainer.cpp reporttabimpl.cpp reportcontrolimpl.cpp daterangedlg.cpp ) set(libwidgets_a_UI kschedulebriefwidget.ui reportcontrol.ui reporttabgeneral.ui reporttabrowcolquery.ui reporttabrowcolpivot.ui reporttabrange.ui reporttabchart.ui reporttabcapitalgain.ui reporttabperformance.ui - daterangedlg.ui + daterangedlgdecl.ui ) # using uic on the above UI files DEPENDS on libkmymoney.so. If uic # does not find libkmymoney.so, gcc will fail compiling # kmymoneyreportconfigtab2decl.cpp and throw errors like "invalid use # of undefined type `struct KMyMoneyGeneralCombo'" ki18n_wrap_ui(widgets_ui_srcs ${libwidgets_a_UI}) add_custom_target(generate_widgets_ui_srcs DEPENDS ${widgets_ui_srcs}) add_library(widgets STATIC ${libwidgets_a_SOURCES} ${widgets_ui_srcs} ) target_link_libraries(widgets KF5::XmlGui kmymoney_base) add_dependencies(widgets kmm_config) ########### install files ############### install(FILES ${kmymoney_STAT_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kmm_widgets_export.h DESTINATION ${INCLUDE_INSTALL_DIR}/kmymoney COMPONENT Devel) diff --git a/kmymoney/widgets/amountedit.cpp b/kmymoney/widgets/amountedit.cpp index dcd4efae7..1f7cfd123 100644 --- a/kmymoney/widgets/amountedit.cpp +++ b/kmymoney/widgets/amountedit.cpp @@ -1,595 +1,591 @@ /*************************************************************************** amountedit.cpp ------------------- copyright : (C) 2016 by Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "amountedit.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneycalculator.h" #include "mymoneymoney.h" #include "mymoneysecurity.h" #include "icons/icons.h" using namespace Icons; AmountValidator::AmountValidator(QObject * parent) : QDoubleValidator(parent) { } AmountValidator::AmountValidator(double bottom, double top, int decimals, QObject * parent) : QDoubleValidator(bottom, top, decimals, parent) { } /* * The code of the following function is taken from kdeui/knumvalidator.cpp * and adjusted to always use the monetary symbols defined in the KDE System Settings */ QValidator::State AmountValidator::validate(QString & input, int & _p) const { QString s = input; QLocale locale; // ok, we have to re-format the number to have: // 1. decimalSymbol == '.' // 2. negativeSign == '-' // 3. positiveSign == // 4. thousandsSeparator() == (we don't check that there // are exactly three decimals between each separator): QString dec = locale.decimalPoint(), n = locale.negativeSign(), p = locale.positiveSign(), t = locale.groupSeparator(); // first, delete p's and t's: if (!p.isEmpty()) for (int idx = s.indexOf(p) ; idx >= 0 ; idx = s.indexOf(p, idx)) s.remove(idx, p.length()); if (!t.isEmpty()) for (int idx = s.indexOf(t) ; idx >= 0 ; idx = s.indexOf(t, idx)) s.remove(idx, t.length()); // then, replace the d's and n's if ((!n.isEmpty() && n.indexOf('.') != -1) || (!dec.isEmpty() && dec.indexOf('-') != -1)) { // make sure we don't replace something twice: qWarning() << "KDoubleValidator: decimal symbol contains '-' or " "negative sign contains '.' -> improve algorithm" << endl; return Invalid; } if (!dec.isEmpty() && dec != ".") for (int idx = s.indexOf(dec) ; idx >= 0 ; idx = s.indexOf(dec, idx + 1)) s.replace(idx, dec.length(), "."); if (!n.isEmpty() && n != "-") for (int idx = s.indexOf(n) ; idx >= 0 ; idx = s.indexOf(n, idx + 1)) s.replace(idx, n.length(), "-"); // TODO: port KF5 (support for paren around negative numbers) #if 0 // Take care of monetary parens around the value if selected via // the locale settings. // If the lead-in or lead-out paren is present, remove it // before passing the string to the QDoubleValidator if (l->negativeMonetarySignPosition() == KLocale::ParensAround || l->positiveMonetarySignPosition() == KLocale::ParensAround) { QRegExp regExp("^(\\()?([\\d-\\.]*)(\\))?$"); if (s.indexOf(regExp) != -1) { s = regExp.cap(2); } } #endif // check for non numeric values (QDoubleValidator allows an 'e', we don't) QRegExp nonNumeric("[^\\d-\\.]+"); if (s.indexOf(nonNumeric) != -1) return Invalid; // check for minus sign trailing the number QRegExp trailingMinus("^([^-]*)\\w*-$"); if (s.indexOf(trailingMinus) != -1) { s = QString("-%1").arg(trailingMinus.cap(1)); } // check for the maximum allowed number of decimal places int decPos = s.indexOf('.'); if (decPos != -1) { if (decimals() == 0) return Invalid; if (((int)(s.length()) - decPos) > decimals()) return Invalid; } // If we have just a single minus sign, we are done if (s == QString("-")) return Acceptable; QValidator::State rc = QDoubleValidator::validate(s, _p); // TODO: port KF5 (support for paren around negative numbers) #if 0 if (rc == Acceptable) { // If the numeric value is acceptable, we check if the parens // are ok. If only the lead-in is present, the return value // is intermediate, if only the lead-out is present then it // definitely is invalid. Nevertheless, we check for parens // only, if the locale settings have it enabled. if (l->negativeMonetarySignPosition() == KLocale::ParensAround || l->positiveMonetarySignPosition() == KLocale::ParensAround) { int tmp = input.count('(') - input.count(')'); if (tmp > 0) rc = Intermediate; else if (tmp < 0) rc = Invalid; } } #endif return rc; } class AmountEdit::Private { public: Private(AmountEdit* q) : m_q(q) , m_allowEmpty(false) { m_calculatorFrame = new QFrame(m_q); m_calculatorFrame->setWindowFlags(Qt::Popup); m_calculatorFrame->setFrameStyle(QFrame::Panel | QFrame::Raised); m_calculatorFrame->setLineWidth(3); m_calculator = new kMyMoneyCalculator(m_calculatorFrame); m_calculatorFrame->hide(); } AmountEdit* m_q; QFrame* m_calculatorFrame; kMyMoneyCalculator* m_calculator; QToolButton* m_calculatorButton; int m_prec; bool m_allowEmpty; QString m_previousText; // keep track of what has been typed QString m_text; // keep track of what was the original value }; int AmountEdit::standardPrecision = 2; AmountEdit::AmountEdit(QWidget *parent, const int prec) : QLineEdit(parent) , d(new Private(this)) { d->m_prec = prec; if (prec < -1 || prec > 20) { d->m_prec = standardPrecision; } init(); } AmountEdit::AmountEdit(const MyMoneySecurity& sec, QWidget *parent) : QLineEdit(parent) , d(new Private(this)) { d->m_prec = MyMoneyMoney::denomToPrec(sec.smallestAccountFraction()); init(); } AmountEdit::~AmountEdit() { } void AmountEdit::setStandardPrecision(int prec) { if (prec >= 0 && prec < 20) { standardPrecision = prec; } } void AmountEdit::init() { // Yes, just a simple double validator ! AmountValidator *validator = new AmountValidator(this); setValidator(validator); setAlignment(Qt::AlignRight | Qt::AlignVCenter); int height = sizeHint().height(); int btnSize = sizeHint().height() - 5; d->m_calculatorButton = new QToolButton(this); d->m_calculatorButton->setIcon(QIcon::fromTheme(g_Icons[Icon::AccessoriesCalculator])); d->m_calculatorButton->setCursor(Qt::ArrowCursor); d->m_calculatorButton->setStyleSheet("QToolButton { border: none; padding: 2px}"); d->m_calculatorButton->setFixedSize(btnSize, btnSize); d->m_calculatorButton->show(); int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); setStyleSheet(QString("QLineEdit { padding-right: %1px }") .arg(btnSize - frameWidth)); setMinimumHeight(height); connect(d->m_calculatorButton, SIGNAL(clicked()), this, SLOT(slotCalculatorOpen())); KSharedConfig::Ptr kconfig = KSharedConfig::openConfig(); KConfigGroup grp = kconfig->group("General Options"); if (grp.readEntry("DontShowCalculatorButton", false) == true) setCalculatorButtonVisible(false); connect(this, SIGNAL(textChanged(QString)), this, SLOT(theTextChanged(QString))); connect(d->m_calculator, SIGNAL(signalResultAvailable()), this, SLOT(slotCalculatorResult())); connect(d->m_calculator, SIGNAL(signalQuit()), this, SLOT(slotCalculatorClose())); } void AmountEdit::resizeEvent(QResizeEvent* event) { Q_UNUSED(event); const int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); d->m_calculatorButton->move(width() - d->m_calculatorButton->width() - frameWidth - 2, 2); } void AmountEdit::focusOutEvent(QFocusEvent* event) { QLineEdit::focusOutEvent(event); // make sure we have a zero value in case the current text // is empty but this is not allowed if (text().isEmpty() && !d->m_allowEmpty) { QLineEdit::setText(QLatin1String("0")); } // make sure we have a fractional part if (!text().isEmpty()) ensureFractionalPart(); // in case the widget contains a different value we emit // the valueChanged signal if (MyMoneyMoney(text()) != MyMoneyMoney(d->m_text)) { emit valueChanged(text()); } } void AmountEdit::keyPressEvent(QKeyEvent* event) { switch(event->key()) { case Qt::Key_Plus: case Qt::Key_Minus: if (hasSelectedText()) { cut(); } if (text().length() == 0) { QLineEdit::keyPressEvent(event); break; } // in case of '-' we do not enter the calculator when // the current position is the beginning and there is // no '-' sign at the first position. if (event->key() == Qt::Key_Minus) { if (cursorPosition() == 0 && text()[0] != '-') { QLineEdit::keyPressEvent(event); break; } } // intentional fall through case Qt::Key_Slash: case Qt::Key_Asterisk: case Qt::Key_Percent: if (hasSelectedText()) { // remove the selected text cut(); } calculatorOpen(event); break; default: QLineEdit::keyPressEvent(event); break; } } void AmountEdit::setPrecision(const int prec) { if (prec >= -1 && prec <= 20) { if (prec != d->m_prec) { d->m_prec = prec; // update current display setValue(value()); } } } int AmountEdit::precision() const { return d->m_prec; } + bool AmountEdit::isValid() const { return !(text().isEmpty()); } -QString AmountEdit::numericalText() const -{ - return value().toString(); -} - MyMoneyMoney AmountEdit::value() const { MyMoneyMoney money(text()); if (d->m_prec != -1) money = money.convert(MyMoneyMoney::precToDenom(d->m_prec)); return money; } void AmountEdit::setValue(const MyMoneyMoney& value) { // load the value into the widget but don't use thousandsSeparators - setText(value.formatMoney(QString(), d->m_prec, false)); + setText(value.formatMoney("", d->m_prec, false)); } void AmountEdit::setText(const QString& txt) { d->m_text = txt; if (isEnabled() && !txt.isEmpty()) ensureFractionalPart(d->m_text); QLineEdit::setText(d->m_text); #if 0 m_resetButton->setEnabled(false); #endif } void AmountEdit::resetText() { #if 0 setText(d->m_text); m_resetButton->setEnabled(false); #endif } void AmountEdit::theTextChanged(const QString & theText) { QLocale locale; QString dec = locale.groupSeparator(); QString l_text = theText; QString nsign, psign; nsign = locale.negativeSign(); psign = locale.positiveSign(); - auto i = 0; + int i = 0; if (isEnabled()) { QValidator::State state = validator()->validate(l_text, i); if (state == QValidator::Intermediate) { if (l_text.length() == 1) { if (l_text != dec && l_text != nsign && l_text != psign) state = QValidator::Invalid; } } if (state == QValidator::Invalid) QLineEdit::setText(d->m_previousText); else { d->m_previousText = l_text; emit validatedTextChanged(text()); } } } void AmountEdit::ensureFractionalPart() { QString s(text()); ensureFractionalPart(s); // by setting the text only when it's different then the one that it is already there // we preserve the edit widget's state (like the selection for example) during a // call to ensureFractionalPart() that does not change anything if (s != text()) QLineEdit::setText(s); } void AmountEdit::ensureFractionalPart(QString& s) const { - s = MyMoneyMoney(s).formatMoney(QString(), d->m_prec, false); + s = MyMoneyMoney(s).formatMoney("", d->m_prec, false); } void AmountEdit::slotCalculatorOpen() { calculatorOpen(0); } void AmountEdit::calculatorOpen(QKeyEvent* k) { d->m_calculator->setInitialValues(text(), k); int h = d->m_calculatorFrame->height(); int w = d->m_calculatorFrame->width(); // usually, the calculator widget is shown underneath the MoneyEdit widget // if it does not fit on the screen, we show it above this widget QPoint p = mapToGlobal(QPoint(0, 0)); if (p.y() + height() + h > QApplication::desktop()->height()) p.setY(p.y() - h); else p.setY(p.y() + height()); // usually, it is shown left aligned. If it does not fit, we align it // to the right edge of the widget if (p.x() + w > QApplication::desktop()->width()) p.setX(p.x() + width() - w); QRect r = d->m_calculator->geometry(); r.moveTopLeft(p); d->m_calculatorFrame->setGeometry(r); d->m_calculatorFrame->show(); d->m_calculator->setFocus(); } void AmountEdit::slotCalculatorClose() { if (d->m_calculator != 0) { d->m_calculatorFrame->hide(); } } void AmountEdit::slotCalculatorResult() { slotCalculatorClose(); if (d->m_calculator != 0) { setText(d->m_calculator->result()); ensureFractionalPart(); #if 0 // I am not sure if getting a result from the calculator // is a good event to emit a value changed signal. We // should do this only on focusOutEvent() emit valueChanged(text()); d->m_text = text(); #endif } } void AmountEdit::setCalculatorButtonVisible(const bool show) { d->m_calculatorButton->setVisible(show); } void AmountEdit::setAllowEmpty(bool allowed) { d->m_allowEmpty = allowed; } bool AmountEdit::isEmptyAllowed() const { return d->m_allowEmpty; } bool AmountEdit::isCalculatorButtonVisible() const { return d->m_calculatorButton->isVisible(); } CreditDebitHelper::CreditDebitHelper(QObject* parent, AmountEdit* credit, AmountEdit* debit) : QObject(parent) , m_credit(credit) , m_debit(debit) { connect(m_credit, SIGNAL(valueChanged(QString)), this, SLOT(creditChanged())); connect(m_debit, SIGNAL(valueChanged(QString)), this, SLOT(debitChanged())); } CreditDebitHelper::~CreditDebitHelper() { } void CreditDebitHelper::creditChanged() { widgetChanged(m_credit, m_debit); } void CreditDebitHelper::debitChanged() { widgetChanged(m_debit, m_credit); } void CreditDebitHelper::widgetChanged(AmountEdit* src, AmountEdit* dst) { // make sure the objects exist if(!src || !dst) { return; } // in case both are filled with text, the src wins if(!src->text().isEmpty() && !dst->text().isEmpty()) { dst->clear(); } // in case the source is negative, we negate the value // and load it into destination. if(src->value().isNegative()) { dst->setValue(-(src->value())); src->clear(); } emit valueChanged(); } bool CreditDebitHelper::haveValue() const { return (!m_credit->text().isEmpty()) || (!m_debit->text().isEmpty()); } MyMoneyMoney CreditDebitHelper::value() const { MyMoneyMoney value; if(m_credit && m_debit) { if(!m_credit->text().isEmpty()) { value = -m_credit->value(); } else { value = m_debit->value(); } } else { qWarning() << "CreditDebitHelper::value() called with no objects attached. Zero returned."; } return value; } void CreditDebitHelper::setValue(const MyMoneyMoney& value) { if(m_credit && m_debit) { if(value.isNegative()) { m_credit->setValue(-value); m_debit->clear(); } else { m_debit->setValue(value); m_credit->clear(); } } else { qWarning() << "CreditDebitHelper::setValue() called with no objects attached. Skipped."; } } diff --git a/kmymoney/widgets/amountedit.h b/kmymoney/widgets/amountedit.h index a2a90f393..b7bd13892 100644 --- a/kmymoney/widgets/amountedit.h +++ b/kmymoney/widgets/amountedit.h @@ -1,278 +1,281 @@ /*************************************************************************** amountedit.h ------------------- copyright : (C) 2016 by Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef AMOUNTEDIT_H #define AMOUNTEDIT_H #include "kmm_widgets_export.h" // ---------------------------------------------------------------------------- // QT Includes -#include +#include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "mymoneymoney.h" class MyMoneySecurity; /** * This class is derived from KDoubleValidator and uses * the monetary symbols instead of the numeric symbols. * Also, it always accepts localized input. * * @author Thomas Baumgart */ class KMM_WIDGETS_EXPORT AmountValidator : public QDoubleValidator { Q_OBJECT public: /** * Constuct a locale-aware KDoubleValidator with default range * (whatever QDoubleValidator uses for that) and parent @p * parent */ AmountValidator(QObject * parent); /** * Constuct a locale-aware KDoubleValidator for range [@p bottom,@p * top] and a precision of @p digits after the decimal * point. */ AmountValidator(double bottom, double top, int decimals, QObject * parent); /** * Destructs the validator. */ virtual ~AmountValidator() {} /** Overloaded for internal reasons. The API is not affected. */ virtual QValidator::State validate(QString & input, int & pos) const; }; /** * This class represents a widget to enter monetary values. * It has an edit field and a button to select a popup * calculator. The result of the calculator (if used) is * stored in the edit field. * * @author Thomas Baumgart */ class KMM_WIDGETS_EXPORT AmountEdit : public QLineEdit { Q_OBJECT Q_PROPERTY(bool calculatorButtonVisibility READ isCalculatorButtonVisible WRITE setCalculatorButtonVisible) Q_PROPERTY(bool allowEmpty READ isEmptyAllowed WRITE setAllowEmpty) Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) Q_PROPERTY(MyMoneyMoney value READ value WRITE setValue DESIGNABLE false STORED false USER true) Q_PROPERTY(bool valid READ isValid DESIGNABLE false STORED false) private: class Private; QScopedPointer d; /** * This holds the number of precision to be used * when no other information (e.g. from account) * is available. * * @sa setStandardPrecision() */ static int standardPrecision; private: /** * Internal helper function for value() and ensureFractionalPart(). */ void ensureFractionalPart(QString& txt) const; protected: /** * This method ensures that the text version contains a * fractional part. */ void ensureFractionalPart(); /** * This method opens the calculator and replays the key * event pointed to by @p ev. If @p ev is 0, then no key * event is replayed. * * @param ev pointer to QKeyEvent that started the calculator. */ void calculatorOpen(QKeyEvent* ev); /** * Helper method for constructors. */ void init(); /** * Overridden to support calculator button. */ virtual void resizeEvent(QResizeEvent* event); /** * Overridden to support ensureFractionalPart(). */ virtual void focusOutEvent(QFocusEvent* event); /** * Overridden to support calculator button. */ virtual void keyPressEvent(QKeyEvent* event); protected Q_SLOTS: void theTextChanged(const QString & text); void slotCalculatorResult(); void slotCalculatorOpen(); void slotCalculatorClose(); public: - explicit AmountEdit(QWidget* parent = nullptr, const int prec = -2); - explicit AmountEdit(const MyMoneySecurity& eq, QWidget* parent = nullptr); + explicit AmountEdit(QWidget *parent = 0, const int prec = -2); + explicit AmountEdit(const MyMoneySecurity& eq, QWidget *parent = 0); virtual ~AmountEdit(); MyMoneyMoney value() const; void setValue(const MyMoneyMoney& value); bool isValid() const; /** * This method returns the value of the edit field in "numerator/denominator" format. * If you want to get the text of the edit field, use lineedit()->text() instead. */ - QString numericalText() const; + QString numericalText() const + { + return value().toString(); + } /** * Set the number of fractional digits that should be shown * * @param prec number of fractional digits. * * @note should be used prior to calling setText() * @sa precision */ void setPrecision(const int prec); /** * return the number of fractional digits * @sa setPrecision */ int precision() const; /** * This method allows to modify the behavior of the widget * such that it accepts an empty value (all blank) or not. * The default is to not accept an emtpy input and to * convert an empty field into 0.00 upon loss of focus. * * @param allowed if @a true, empty input is allowed, if @a false * emtpy input will be converted to 0.00 */ void setAllowEmpty(bool allowed = true); bool isEmptyAllowed() const; bool isCalculatorButtonVisible() const; /** * This allows to setup the standard precision (number of decimal places) * to be used when no other information is available. @a prec must be in * the range of 0..19. If never set, the default precision is 2. */ static void setStandardPrecision(int prec); public Q_SLOTS: void resetText(); void setText(const QString& txt); /** * This method allows to show/hide the calculator button of the widget. * The parameter @p show controls the behavior. Default is to show the * button. * * @param show if true, button is shown, if false it is hidden */ void setCalculatorButtonVisible(const bool show); Q_SIGNALS: /** * This signal is sent, when the focus leaves this widget and * the amount has been changed by user during this focus possesion. */ void valueChanged(const QString& text); /** * This signal is emitted when the contents of the widget * changed and was validated. Use this in favor of textChanged() * in your application. */ void validatedTextChanged(const QString& text); }; class KMM_WIDGETS_EXPORT CreditDebitHelper : public QObject { Q_OBJECT public: explicit CreditDebitHelper(QObject* parent, AmountEdit* credit, AmountEdit* debit); virtual ~CreditDebitHelper(); /** * Retruns the value of the widget that is filled. * A credit is retruned as negative, a debit as positive value. */ MyMoneyMoney value() const; /** * Loads the widgets with the @a value passed. If * @a value is negative it is loaded into the credit * widget, otherwise into the debit widget. */ void setValue(const MyMoneyMoney& value); /** * This method returns true if at least one * of the two widgets is filled with text. * It returns false if both widgets are empty. */ bool haveValue() const; Q_SIGNALS: void valueChanged(); private Q_SLOTS: void creditChanged(); void debitChanged(); private: void widgetChanged(AmountEdit* src, AmountEdit* dst); private: QPointer m_credit; QPointer m_debit; }; #endif diff --git a/kmymoney/widgets/daterangedlg.cpp b/kmymoney/widgets/daterangedlg.cpp index e36988fb8..fc489fd16 100644 --- a/kmymoney/widgets/daterangedlg.cpp +++ b/kmymoney/widgets/daterangedlg.cpp @@ -1,160 +1,124 @@ /*************************************************************************** daterange.cpp ------------------- copyright : (C) 2003, 2007 by Thomas Baumgart email : ipwizard@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include "daterangedlg.h" // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "mymoneytransactionfilter.h" -#include "mymoneyenums.h" #include "kmymoneydateinput.h" #include "kmymoneymvccombo.h" -#include "ui_daterangedlg.h" +#include "ui_daterangedlgdecl.h" using namespace eMyMoney; -class DateRangeDlgPrivate -{ - Q_DISABLE_COPY(DateRangeDlgPrivate) - Q_DECLARE_PUBLIC(DateRangeDlg) - -public: - DateRangeDlgPrivate(DateRangeDlg *qq) : - q_ptr(qq), - ui(new Ui::DateRangeDlg) - { - } - - ~DateRangeDlgPrivate() - { - delete ui; - } - - void setupDatePage() - { - Q_Q(DateRangeDlg); - for (auto i = (int)TransactionFilter::Date::All; i < (int)TransactionFilter::Date::LastDateItem; ++i) { - MyMoneyTransactionFilter::translateDateRange(static_cast(i), m_startDates[i], m_endDates[i]); - } - - q->connect(ui->m_dateRange, SIGNAL(currentIndexChanged(int)), q, SLOT(slotDateRangeSelectedByUser())); - q->connect(ui->m_fromDate, &kMyMoneyDateInput::dateChanged, q, &DateRangeDlg::slotDateChanged); - q->connect(ui->m_toDate, &kMyMoneyDateInput::dateChanged, q, &DateRangeDlg::slotDateChanged); - - q->setDateRange(TransactionFilter::Date::All); - } - - DateRangeDlg *q_ptr; - Ui::DateRangeDlg *ui; - QDate m_startDates[(int)eMyMoney::TransactionFilter::Date::LastDateItem]; - QDate m_endDates[(int)eMyMoney::TransactionFilter::Date::LastDateItem]; -}; - DateRangeDlg::DateRangeDlg(QWidget *parent) : - QWidget(parent), - d_ptr(new DateRangeDlgPrivate(this)) + QWidget(parent), + m_ui(new Ui::DateRangeDlgDecl) { - Q_D(DateRangeDlg); - d->ui->setupUi(this); - d->setupDatePage(); + m_ui->setupUi(this); + setupDatePage(); } DateRangeDlg::~DateRangeDlg() { - Q_D(DateRangeDlg); - delete d; + delete m_ui; } void DateRangeDlg::slotReset() { - Q_D(DateRangeDlg); - d->ui->m_dateRange->setCurrentItem(TransactionFilter::Date::All); + m_ui->m_dateRange->setCurrentItem(TransactionFilter::Date::All); + setDateRange(TransactionFilter::Date::All); +} + +void DateRangeDlg::setupDatePage() +{ + for (auto i = (int)TransactionFilter::Date::All; i < (int)TransactionFilter::Date::LastDateItem; ++i) { + MyMoneyTransactionFilter::translateDateRange(static_cast(i), m_startDates[i], m_endDates[i]); + } + + connect(m_ui->m_dateRange, SIGNAL(currentIndexChanged(int)), this, SLOT(slotDateRangeSelectedByUser())); + connect(m_ui->m_fromDate, SIGNAL(dateChanged(QDate)), this, SLOT(slotDateChanged())); + connect(m_ui->m_toDate, SIGNAL(dateChanged(QDate)), this, SLOT(slotDateChanged())); + setDateRange(TransactionFilter::Date::All); } void DateRangeDlg::slotDateRangeSelectedByUser() { - Q_D(DateRangeDlg); - setDateRange(d->ui->m_dateRange->currentItem()); + setDateRange(m_ui->m_dateRange->currentItem()); } void DateRangeDlg::setDateRange(const QDate& from, const QDate& to) { - Q_D(DateRangeDlg); - d->ui->m_fromDate->loadDate(from); - d->ui->m_toDate->loadDate(to); - d->ui->m_dateRange->setCurrentItem(TransactionFilter::Date::UserDefined); - emit rangeChanged(); + m_ui->m_fromDate->loadDate(from); + m_ui->m_toDate->loadDate(to); + m_ui->m_dateRange->setCurrentItem(TransactionFilter::Date::UserDefined); + emit rangeChanged(); } void DateRangeDlg::setDateRange(TransactionFilter::Date idx) { - Q_D(DateRangeDlg); - d->ui->m_dateRange->setCurrentItem(idx); + m_ui->m_dateRange->setCurrentItem(idx); switch (idx) { case TransactionFilter::Date::All: - d->ui->m_fromDate->loadDate(QDate()); - d->ui->m_toDate->loadDate(QDate()); + m_ui->m_fromDate->loadDate(QDate()); + m_ui->m_toDate->loadDate(QDate()); break; case TransactionFilter::Date::UserDefined: break; default: - d->ui->m_fromDate->blockSignals(true); - d->ui->m_toDate->blockSignals(true); - d->ui->m_fromDate->loadDate(d->m_startDates[(int)idx]); - d->ui->m_toDate->loadDate(d->m_endDates[(int)idx]); - d->ui->m_fromDate->blockSignals(false); - d->ui->m_toDate->blockSignals(false); + m_ui->m_fromDate->blockSignals(true); + m_ui->m_toDate->blockSignals(true); + m_ui->m_fromDate->loadDate(m_startDates[(int)idx]); + m_ui->m_toDate->loadDate(m_endDates[(int)idx]); + m_ui->m_fromDate->blockSignals(false); + m_ui->m_toDate->blockSignals(false); break; } emit rangeChanged(); } TransactionFilter::Date DateRangeDlg::dateRange() const { - Q_D(const DateRangeDlg); - return d->ui->m_dateRange->currentItem(); + return m_ui->m_dateRange->currentItem(); } void DateRangeDlg::slotDateChanged() { - Q_D(DateRangeDlg); - d->ui->m_dateRange->blockSignals(true); - d->ui->m_dateRange->setCurrentItem(TransactionFilter::Date::UserDefined); - d->ui->m_dateRange->blockSignals(false); + m_ui->m_dateRange->blockSignals(true); + m_ui->m_dateRange->setCurrentItem(TransactionFilter::Date::UserDefined); + m_ui->m_dateRange->blockSignals(false); } QDate DateRangeDlg::fromDate() const { - Q_D(const DateRangeDlg); - return d->ui->m_fromDate->date(); + return m_ui->m_fromDate->date(); } QDate DateRangeDlg::toDate() const { - Q_D(const DateRangeDlg); - return d->ui->m_toDate->date(); + return m_ui->m_toDate->date(); } diff --git a/kmymoney/widgets/daterangedlg.h b/kmymoney/widgets/daterangedlg.h index cd22fa30d..66e9dd3c5 100644 --- a/kmymoney/widgets/daterangedlg.h +++ b/kmymoney/widgets/daterangedlg.h @@ -1,79 +1,88 @@ /*************************************************************************** daterange.h ------------------- copyright : (C) 2003 by Thomas Baumgart email : ipwizard@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef DATERANGEDLG_H #define DATERANGEDLG_H // ---------------------------------------------------------------------------- // QT Includes -#include +#include // ---------------------------------------------------------------------------- // KDE Includes +#include + // ---------------------------------------------------------------------------- // Project Includes -namespace eMyMoney { namespace TransactionFilter { enum class Date; } } +#include "mymoneytransactionfilter.h" +#include "mymoneyenums.h" + +namespace Ui +{ +class DateRangeDlgDecl; +} -class DateRangeDlgPrivate; class DateRangeDlg : public QWidget { Q_OBJECT - Q_DISABLE_COPY(DateRangeDlg) public: - explicit DateRangeDlg(QWidget* parent = nullptr); + DateRangeDlg(QWidget *parent = 0); ~DateRangeDlg(); /*! * Setup a sliding date range which is relative to the * current date (sliding date range) */ void setDateRange(eMyMoney::TransactionFilter::Date); /*! * Setup a fixed user selected date range (does not slide) */ void setDateRange(const QDate& from, const QDate& to); /*! * Return the currently selected date range option */ eMyMoney::TransactionFilter::Date dateRange() const; QDate fromDate() const; QDate toDate() const; public slots: void slotReset(); void slotDateRangeSelectedByUser(); void slotDateChanged(); Q_SIGNALS: /*! * The rangeChanged() signal is emitted whenever a range * is changed (user interaction or call to setDateRange() ) */ void rangeChanged(); private: - DateRangeDlgPrivate * const d_ptr; - Q_DECLARE_PRIVATE(DateRangeDlg) + void setupDatePage(); + + Ui::DateRangeDlgDecl* m_ui; + QDate m_startDates[(int)eMyMoney::TransactionFilter::Date::LastDateItem]; + QDate m_endDates[(int)eMyMoney::TransactionFilter::Date::LastDateItem]; }; #endif diff --git a/kmymoney/widgets/daterangedlg.ui b/kmymoney/widgets/daterangedlgdecl.ui similarity index 92% rename from kmymoney/widgets/daterangedlg.ui rename to kmymoney/widgets/daterangedlgdecl.ui index 3f42b6057..10f23f465 100644 --- a/kmymoney/widgets/daterangedlg.ui +++ b/kmymoney/widgets/daterangedlgdecl.ui @@ -1,63 +1,63 @@ - DateRangeDlg - + DateRangeDlgDecl + Range false From false To false KMyMoneyPeriodCombo QWidget -
kmymoneyperiodcombo.h
+
kmymoneymvccombo.h
1
kMyMoneyDateInput QWidget
kmymoneydateinput.h
diff --git a/kmymoney/widgets/fancydategroupmarker.cpp b/kmymoney/widgets/fancydategroupmarker.cpp deleted file mode 100644 index 5c670e12b..000000000 --- a/kmymoney/widgets/fancydategroupmarker.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/*************************************************************************** - fancydategroupmarker.cpp - description - ------------------- - begin : Fri Mar 10 2006 - copyright : (C) 2006 by Thomas Baumgart - email : Thomas Baumgart - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include - -#include "fancydategroupmarker.h" -#include "fancydategroupmarker_p.h" - -#include - -// ---------------------------------------------------------------------------- -// QT Includes - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -#include - -// ---------------------------------------------------------------------------- -// Project Includes -#include "stdtransactiondownloaded.h" -#include "stdtransactionmatched.h" -#include "scheduledtransaction.h" -#include "kmymoneyglobalsettings.h" -#include "mymoneyfile.h" -#include "groupmarker.h" -#include "mymoneyenums.h" - -FancyDateGroupMarker::FancyDateGroupMarker(Register* parent, - const QDate& date, - const QString& txt) : - GroupMarker(*new FancyDateGroupMarkerPrivate, parent, txt) -{ - Q_D(FancyDateGroupMarker); - d->m_date = date; -} - -FancyDateGroupMarker::FancyDateGroupMarker(FancyDateGroupMarkerPrivate &dd, Register *parent, const QDate& date, const QString& txt) : - GroupMarker(dd, parent, txt) -{ - Q_D(FancyDateGroupMarker); - d->m_date = date; -} - -FancyDateGroupMarker::~FancyDateGroupMarker() -{ -} - -QDate FancyDateGroupMarker::sortPostDate() const -{ - Q_D(const FancyDateGroupMarker); - return d->m_date; -} - -QDate FancyDateGroupMarker::sortEntryDate() const -{ - Q_D(const FancyDateGroupMarker); - return d->m_date; -} - -const char* FancyDateGroupMarker::className() -{ - return "FancyDateGroupMarker"; -} diff --git a/kmymoney/widgets/fancydategroupmarker.h b/kmymoney/widgets/fancydategroupmarker.h deleted file mode 100644 index 8ab16633b..000000000 --- a/kmymoney/widgets/fancydategroupmarker.h +++ /dev/null @@ -1,56 +0,0 @@ -/*************************************************************************** - fancydategroupmarker.h - ---------- - begin : Fri Mar 10 2006 - copyright : (C) 2006 by Thomas Baumgart - email : Thomas Baumgart - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef FANCYDATEGROUPMARKER_H -#define FANCYDATEGROUPMARKER_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "groupmarker.h" - -class MyMoneyTransaction; - -namespace KMyMoneyRegister -{ - class FancyDateGroupMarkerPrivate; - class FancyDateGroupMarker : public GroupMarker - { - Q_DISABLE_COPY(FancyDateGroupMarker) - - public: - explicit FancyDateGroupMarker(Register* getParent, const QDate& date, const QString& txt); - ~FancyDateGroupMarker() override; - - QDate sortPostDate() const override; - QDate sortEntryDate() const override; - const char* className() override; - - protected: - FancyDateGroupMarker(FancyDateGroupMarkerPrivate &dd, Register *parent, const QDate& date, const QString& txt); - Q_DECLARE_PRIVATE(FancyDateGroupMarker) - }; -} // namespace - -#endif diff --git a/kmymoney/widgets/fancydategroupmarker_p.h b/kmymoney/widgets/fancydategroupmarker_p.h deleted file mode 100644 index 6843c2868..000000000 --- a/kmymoney/widgets/fancydategroupmarker_p.h +++ /dev/null @@ -1,44 +0,0 @@ -/*************************************************************************** - fancydategroupmarker_p.h - description - ------------------- - begin : Fri Mar 10 2006 - copyright : (C) 2006 by Thomas Baumgart - email : Thomas Baumgart - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef FANCYDATEGROUPMARKER_P_H -#define FANCYDATEGROUPMARKER_P_H - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "groupmarker_p.h" - -namespace KMyMoneyRegister -{ - class FancyDateGroupMarkerPrivate : public GroupMarkerPrivate - { - public: - QDate m_date; - }; -} - -#endif diff --git a/kmymoney/widgets/fixedcolumntreeview.cpp b/kmymoney/widgets/fixedcolumntreeview.cpp index bcd13ab7b..ecf840ea5 100644 --- a/kmymoney/widgets/fixedcolumntreeview.cpp +++ b/kmymoney/widgets/fixedcolumntreeview.cpp @@ -1,285 +1,285 @@ /* * Copyright 2014 Cristian Oneț * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "fixedcolumntreeview.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes class FixedColumnDelegate : public QStyledItemDelegate { QTreeView *m_sourceView; public: explicit FixedColumnDelegate(FixedColumnTreeView *fixedColumView, QTreeView *sourceView) : QStyledItemDelegate(fixedColumView), m_sourceView(sourceView) { } virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem optV4 = option; initStyleOption(&optV4, index); // the fixed column's position has always this value optV4.viewItemPosition = QStyleOptionViewItem::Beginning; if (m_sourceView->hasFocus()) { // draw the current row as active if the source list has focus QModelIndex currentIndex = m_sourceView->currentIndex(); if (currentIndex.isValid() && currentIndex.row() == index.row() && currentIndex.parent() == index.parent()) { optV4.state |= QStyle::State_Active; } } QStyledItemDelegate::paint(painter, optV4, index); } }; struct FixedColumnTreeView::Private { Private(FixedColumnTreeView *pub, QTreeView *parent) : pub(pub), parent(parent) { } void syncExpanded(const QModelIndex& parentIndex = QModelIndex()) { const int rows = parent->model()->rowCount(parentIndex); - for (auto i = 0; i < rows; ++i) { + for (int i = 0; i < rows; ++i) { const QModelIndex &index = parent->model()->index(i, 0, parentIndex); if (parent->isExpanded(index)) { pub->expand(index); syncExpanded(index); } } } void syncModels() { if (pub->model() != parent->model()) { // set the model pub->setModel(parent->model()); // hide all but the first column for (int col = 1; col < pub->model()->columnCount(); ++col) pub->setColumnHidden(col, true); // set the selection model pub->setSelectionModel(parent->selectionModel()); // when the model has changed we need to sync the expanded state of the views syncExpanded(); } } void syncProperties() { //pub->setAllColumnsShowFocus(parent->allColumnsShowFocus()); pub->setAlternatingRowColors(parent->alternatingRowColors()); pub->setIconSize(parent->iconSize()); pub->setSortingEnabled(parent->isSortingEnabled()); pub->setUniformRowHeights(pub->uniformRowHeights()); } void syncGeometry() { // update the geometry of the fixed column view to match that of the source model's geometry pub->setGeometry(parent->frameWidth(), parent->frameWidth(), parent->columnWidth(0), parent->viewport()->height() + (parent->header()->isVisible() ? parent->header()->height() : 0)); } FixedColumnTreeView *pub; QTreeView *parent; }; FixedColumnTreeView::FixedColumnTreeView(QTreeView *parent) : QTreeView(parent) , d(new Private(this, parent)) { // no borders and scrollbars for the fixed column view setStyleSheet("QTreeView { border: none; }"); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // the focus proxy is forwarded to the source list setFocusProxy(parent); // perform the special selection and hover drawing even when the fixed column view has no focus setItemDelegate(new FixedColumnDelegate(this, d->parent)); // stack the source view under the fixed column view d->parent->viewport()->stackUnder(this); // the resize mode of the fixed view needs to be fixed to allow a user resize only from the parent tree header()->sectionResizeMode(QHeaderView::Fixed); // connect the scroll bars to keep the two views in sync connect(verticalScrollBar(), SIGNAL(valueChanged(int)), d->parent->verticalScrollBar(), SLOT(setValue(int))); connect(d->parent->verticalScrollBar(), SIGNAL(valueChanged(int)), verticalScrollBar(), SLOT(setValue(int))); // keep the expanded/collapsed states synchronized between the two views connect(d->parent, SIGNAL(expanded(QModelIndex)), this, SLOT(onExpanded(QModelIndex))); connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(onExpanded(QModelIndex))); connect(d->parent, SIGNAL(collapsed(QModelIndex)), this, SLOT(onCollapsed(QModelIndex))); connect(this, SIGNAL(collapsed(QModelIndex)), this, SLOT(onCollapsed(QModelIndex))); // keep the sort indicators synchronized between the two views connect(d->parent->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(updateSortIndicator(int,Qt::SortOrder))); connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(updateSortIndicator(int,Qt::SortOrder))); // forward all signals connect(this, SIGNAL(activated(QModelIndex)), d->parent, SIGNAL(activated(QModelIndex))); connect(this, SIGNAL(clicked(QModelIndex)), d->parent, SIGNAL(clicked(QModelIndex))); connect(this, SIGNAL(doubleClicked(QModelIndex)), d->parent, SIGNAL(doubleClicked(QModelIndex))); connect(this, SIGNAL(entered(QModelIndex)), d->parent, SIGNAL(entered(QModelIndex))); connect(this, SIGNAL(pressed(QModelIndex)), d->parent, SIGNAL(pressed(QModelIndex))); connect(this, SIGNAL(viewportEntered()), d->parent, SIGNAL(viewportEntered())); // handle the resize of the first column in the source view connect(d->parent->header(), SIGNAL(sectionResized(int,int,int)), this, SLOT(updateSectionWidth(int,int,int))); // forward right click to the source list setContextMenuPolicy(d->parent->contextMenuPolicy()); if (contextMenuPolicy() == Qt::CustomContextMenu) { connect(this, SIGNAL(customContextMenuRequested(QPoint)), d->parent, SIGNAL(customContextMenuRequested(QPoint))); } // enable hover indicator synchronization between the two views d->parent->viewport()->installEventFilter(this); d->parent->viewport()->setMouseTracking(true); viewport()->setMouseTracking(true); d->syncProperties(); if (d->parent->isVisible()) { // the source view is already visible so show the frozen column also d->syncModels(); show(); d->syncGeometry(); } } FixedColumnTreeView::~FixedColumnTreeView() { delete d; } void FixedColumnTreeView::sourceModelUpdated() { d->syncModels(); d->syncGeometry(); } void FixedColumnTreeView::onExpanded(const QModelIndex& index) { if (sender() == this && !d->parent->isExpanded(index)) { d->parent->expand(index); } if (sender() == d->parent && !isExpanded(index)) { expand(index); } } void FixedColumnTreeView::onCollapsed(const QModelIndex& index) { if (sender() == this && d->parent->isExpanded(index)) { d->parent->collapse(index); } if (sender() == d->parent && isExpanded(index)) { collapse(index); } } bool FixedColumnTreeView::viewportEvent(QEvent *event) { if (underMouse()) { // forward mouse move and hover leave events to the source list if (event->type() == QEvent::MouseMove || event->type() == QEvent::HoverLeave) { QApplication::sendEvent(d->parent->viewport(), event); } } return QTreeView::viewportEvent(event); } bool FixedColumnTreeView::eventFilter(QObject *object, QEvent *event) { if (object == d->parent->viewport()) { switch (event->type()) { case QEvent::MouseMove: if (!underMouse() && d->parent->underMouse()) { QMouseEvent *me = static_cast(event); // translate the position of the event but don't send buttons or modifiers because we only need the movement for the hover QMouseEvent translatedMouseEvent(me->type(), QPoint(width() - 2, me->pos().y()), Qt::NoButton, Qt::MouseButtons(), Qt::KeyboardModifiers()); QApplication::sendEvent(viewport(), &translatedMouseEvent); } break; case QEvent::HoverLeave: if (!underMouse() && d->parent->underMouse()) { QApplication::sendEvent(viewport(), event); } break; case QEvent::Show: d->syncModels(); show(); // intentional fall through case QEvent::Resize: d->syncGeometry(); break; default: break; } } return QTreeView::eventFilter(object, event); } void FixedColumnTreeView::updateSectionWidth(int logicalIndex, int, int newSize) { if (logicalIndex == 0) { const int maxFirstColumnWidth = d->parent->width() * 0.8; if (newSize > maxFirstColumnWidth) { // limit the size of the first column so that it will not become larger than the view's width d->parent->setColumnWidth(logicalIndex, maxFirstColumnWidth); } else { // update the size of the fixed column setColumnWidth(0, newSize); // update the geometry d->syncGeometry(); } } } void FixedColumnTreeView::updateSortIndicator(int logicalIndex, Qt::SortOrder order) { if (sender() == header() && d->parent->header()->sortIndicatorSection() != logicalIndex) { d->parent->header()->setSortIndicator(logicalIndex, order); } if (sender() == d->parent->header() && header()->sortIndicatorSection() != logicalIndex) { header()->setSortIndicator(logicalIndex, order); } } diff --git a/kmymoney/widgets/fixedcolumntreeview.h b/kmymoney/widgets/fixedcolumntreeview.h index 7fe2c13d3..d7b40ccc6 100644 --- a/kmymoney/widgets/fixedcolumntreeview.h +++ b/kmymoney/widgets/fixedcolumntreeview.h @@ -1,67 +1,67 @@ /* * Copyright 2014 Cristian Oneț * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef FIXEDCOLUMNTREEVIEW_H #define FIXEDCOLUMNTREEVIEW_H #include /** * This tree view should be used together with a source * tree view to obtain a view that has the first column * fixed just like in the "Frozen Column" Qt example. * * @usage Just create this view by passing the source view * as a parameter. If you intend to change the model * of the source view after this view was attached call * sourceModelUpdated(). */ class FixedColumnTreeView : public QTreeView { Q_OBJECT public: - explicit FixedColumnTreeView(QTreeView *parent); + FixedColumnTreeView(QTreeView *parent); ~FixedColumnTreeView(); public slots: /** * Call this if the model of the source view is changed * after the fixed column view was attached. */ void sourceModelUpdated(); protected: - bool viewportEvent(QEvent *event) override; - bool eventFilter(QObject *object, QEvent *event) override; + bool viewportEvent(QEvent *event); + bool eventFilter(QObject *object, QEvent *event); protected slots: void onExpanded(const QModelIndex& index); void onCollapsed(const QModelIndex& index); void updateSectionWidth(int logicalIndex, int, int newSize); void updateSortIndicator(int logicalIndex, Qt::SortOrder order); private: struct Private; Private * const d; }; #endif // FIXEDCOLUMNTREEVIEW_H diff --git a/kmymoney/widgets/groupmarker.cpp b/kmymoney/widgets/groupmarker.cpp deleted file mode 100644 index b516e3739..000000000 --- a/kmymoney/widgets/groupmarker.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/*************************************************************************** - groupmarker.cpp - description - ------------------- - begin : Fri Mar 10 2006 - copyright : (C) 2006 by Thomas Baumgart - email : Thomas Baumgart - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "groupmarker.h" -#include "groupmarker_p.h" - -#include -#include - -// ---------------------------------------------------------------------------- -// QT Includes - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "mymoneyenums.h" - -using namespace KMyMoneyRegister; -using namespace eMyMoney; - -QPixmap* GroupMarkerPrivate::GroupMarkerPrivate::m_bg = nullptr; -int GroupMarkerPrivate::GroupMarkerPrivate::m_bgRefCnt = 0; - -GroupMarker::GroupMarker(Register *parent, const QString& txt) : - RegisterItem(*new GroupMarkerPrivate, parent) -{ - Q_D(GroupMarker); - d->m_txt = txt; - d->init(); -} - -GroupMarker::GroupMarker(GroupMarkerPrivate &dd, Register *parent, const QString& txt) : - RegisterItem(dd, parent) -{ - Q_D(GroupMarker); - d->m_txt = txt; - d->init(); -} - -GroupMarker::~GroupMarker() -{ - Q_D(GroupMarker); - --d->GroupMarkerPrivate::m_bgRefCnt; - if (!d->GroupMarkerPrivate::m_bgRefCnt) { - delete d->GroupMarkerPrivate::m_bg; - d->GroupMarkerPrivate::m_bg = 0; - } -} - -void GroupMarker::setText(const QString& txt) -{ - Q_D(GroupMarker); - d->m_txt = txt; -} - -const QString& GroupMarker::text() const -{ - Q_D(const GroupMarker); - return d->m_txt; -} - -bool GroupMarker::isSelectable() const -{ - return false; -} - -bool GroupMarker::canHaveFocus() const -{ - return false; -} - -int GroupMarker::numRows() const -{ - return 1; -} - -const char* GroupMarker::className() -{ - return "GroupMarker"; -} - -bool GroupMarker::isErroneous() const -{ - Q_D(const GroupMarker); - return d->m_erroneous; -} - -void GroupMarker::paintRegisterCell(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) -{ - Q_D(GroupMarker); - QRect r(option.rect); - painter->save(); - - // the group marker always uses all cols - r.setX(d->m_parent->horizontalHeader()->sectionPosition(0)); - r.setWidth(d->m_parent->viewport()->width()); - painter->translate(r.x(), r.y()); - - QRect cellRect; - cellRect.setX(0); - cellRect.setY(0); - cellRect.setWidth(d->m_parent->viewport()->width()); - cellRect.setHeight(d->m_parent->rowHeight(index.row())); - - option.palette.setColor(QPalette::Base, isErroneous() ? KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionErroneous) : KMyMoneyGlobalSettings::schemeColor(SchemeColor::GroupMarker)); - - QBrush backgroundBrush(option.palette.color(QPalette::Base)); - painter->fillRect(cellRect, backgroundBrush); - painter->setPen(KMyMoneyGlobalSettings::schemeColor(SchemeColor::ListGrid)); - painter->drawLine(cellRect.x(), cellRect.height() - 1, cellRect.width(), cellRect.height() - 1); - - // now write the text - painter->setPen(option.palette.color(isErroneous() ? QPalette::HighlightedText : QPalette::Text)); - QFont font = painter->font(); - font.setBold(true); - painter->setFont(font); - - painter->drawText(cellRect, Qt::AlignVCenter | Qt::AlignCenter, d->m_txt); - - cellRect.setHeight(d->GroupMarkerPrivate::m_bg->height()); - - // now it's time to draw the background - painter->drawPixmap(cellRect, *d->GroupMarkerPrivate::m_bg); - - // in case we need to show the date, we just paint it in col 1 - if (d->m_showDate) { - font.setBold(false); - cellRect.setX(d->m_parent->horizontalHeader()->sectionPosition(DateColumn)); - cellRect.setWidth(d->m_parent->horizontalHeader()->sectionSize(DateColumn)); - painter->setFont(font); - painter->drawText(cellRect, Qt::AlignVCenter | Qt::AlignCenter, QLocale().toString(sortPostDate(), QLocale::ShortFormat)); - } - - painter->restore(); -} - -void GroupMarker::paintFormCell(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) -{ - Q_UNUSED(painter); - Q_UNUSED(option); - Q_UNUSED(index); -} - -int GroupMarker::rowHeightHint() const -{ - Q_D(const GroupMarker); - if (!d->m_visible) - return 0; - - return d->GroupMarkerPrivate::m_bg->height(); -} - -bool GroupMarker::matches(const RegisterFilter&) const -{ - return true; -} - -int GroupMarker::sortSamePostDate() const -{ - return 0; -} - -void GroupMarker::setErroneous(bool condition) -{ - Q_D(GroupMarker); - d->m_erroneous = condition; -} diff --git a/kmymoney/widgets/groupmarker.h b/kmymoney/widgets/groupmarker.h deleted file mode 100644 index 236ae18bf..000000000 --- a/kmymoney/widgets/groupmarker.h +++ /dev/null @@ -1,74 +0,0 @@ -/*************************************************************************** - groupmarker.h - ---------- - begin : Fri Mar 10 2006 - copyright : (C) 2006 by Thomas Baumgart - email : Thomas Baumgart - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef GROUPMARKER_H -#define GROUPMARKER_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "registeritem.h" - -class MyMoneyTransaction; - -namespace KMyMoneyRegister -{ - -class Register; -class RegisterItem; -class ItemPtrVector; - -class GroupMarkerPrivate; -class GroupMarker : public RegisterItem -{ - Q_DISABLE_COPY(GroupMarker) - -public: - explicit GroupMarker(Register* getParent, const QString& txt); - ~GroupMarker() override; - - void setText(const QString& txt); - const QString& text() const; - bool isSelectable() const override; - bool canHaveFocus() const override; - int numRows() const; - const char* className() override; - bool isErroneous() const override; - void paintRegisterCell(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) override; - void paintFormCell(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) override; - - int rowHeightHint() const override; - - bool matches(const RegisterFilter&) const override; - int sortSamePostDate() const override; - void setErroneous(bool condition = true); - -protected: - GroupMarker(GroupMarkerPrivate &dd, Register *parent, const QString& txt); - Q_DECLARE_PRIVATE(GroupMarker) -}; - -} // namespace - -#endif diff --git a/kmymoney/widgets/groupmarker_p.h b/kmymoney/widgets/groupmarker_p.h deleted file mode 100644 index 908874936..000000000 --- a/kmymoney/widgets/groupmarker_p.h +++ /dev/null @@ -1,114 +0,0 @@ -/*************************************************************************** - groupmarker_p.h - description - ------------------- - begin : Fri Mar 10 2006 - copyright : (C) 2006 by Thomas Baumgart - email : Thomas Baumgart - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef GROUPMARKER_P_H -#define GROUPMARKER_P_H - -// ---------------------------------------------------------------------------- -// QT Includes - -#include -#include -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmymoneyglobalsettings.h" -#include "register.h" -#include "registeritem_p.h" - -static unsigned char fancymarker_bg_image[] = { - /* 200x49 */ - 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, - 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, - 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x31, - 0x08, 0x06, 0x00, 0x00, 0x00, 0x9F, 0xC5, 0xE6, - 0x4F, 0x00, 0x00, 0x00, 0x06, 0x62, 0x4B, 0x47, - 0x44, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xA0, - 0xBD, 0xA7, 0x93, 0x00, 0x00, 0x00, 0x09, 0x70, - 0x48, 0x59, 0x73, 0x00, 0x00, 0x0B, 0x13, 0x00, - 0x00, 0x0B, 0x13, 0x01, 0x00, 0x9A, 0x9C, 0x18, - 0x00, 0x00, 0x00, 0x86, 0x49, 0x44, 0x41, 0x54, - 0x78, 0xDA, 0xED, 0xDD, 0x31, 0x0A, 0x84, 0x40, - 0x10, 0x44, 0xD1, 0x1A, 0x19, 0x10, 0xCF, 0xE6, - 0xFD, 0x4F, 0xB2, 0x88, 0x08, 0x22, 0x9B, 0x18, - 0x4E, 0x1B, 0x2C, 0x1B, 0x18, 0xBC, 0x07, 0x7D, - 0x81, 0x82, 0x1F, 0x77, 0x4B, 0xB2, 0x06, 0x18, - 0xEA, 0x49, 0x3E, 0x66, 0x00, 0x81, 0x80, 0x40, - 0xE0, 0xDF, 0x81, 0x6C, 0x66, 0x80, 0x3A, 0x90, - 0xDD, 0x0C, 0x50, 0x07, 0x72, 0x98, 0x01, 0xEA, - 0x40, 0x4E, 0x33, 0x40, 0x1D, 0xC8, 0x65, 0x06, - 0x18, 0x6B, 0xF7, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x16, 0x3E, - 0x4C, 0xC1, 0x83, 0x9E, 0x64, 0x32, 0x03, 0x08, - 0x04, 0x7E, 0x0A, 0xA4, 0x9B, 0x01, 0xEA, 0x40, - 0x66, 0x33, 0x40, 0x1D, 0xC8, 0x62, 0x06, 0x18, - 0xFB, 0x02, 0x05, 0x87, 0x08, 0x55, 0xFE, 0xDE, - 0xA2, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, - 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 -}; - -namespace KMyMoneyRegister -{ - class GroupMarkerPrivate : public RegisterItemPrivate - { - public: - void init() - { - m_showDate = false; - m_erroneous = false; - int h; - if (m_parent) { - h = m_parent->rowHeightHint(); - } else { - QFontMetrics fm(KMyMoneyGlobalSettings::listCellFont()); - h = fm.lineSpacing() + 6; - } - - if (GroupMarkerPrivate::m_bg && (GroupMarkerPrivate::m_bg->height() != h)) { - delete GroupMarkerPrivate::m_bg; - GroupMarkerPrivate::m_bg = 0; - } - - // convert the backgroud once - if (GroupMarkerPrivate::m_bg == 0) { - GroupMarkerPrivate::m_bg = new QPixmap; - GroupMarkerPrivate::m_bg->loadFromData(fancymarker_bg_image, sizeof(fancymarker_bg_image)); - *GroupMarkerPrivate::m_bg = GroupMarkerPrivate::m_bg->scaled(GroupMarkerPrivate::m_bg->width(), h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - } - - ++GroupMarkerPrivate::m_bgRefCnt; - } - - QString m_txt; - bool m_showDate; - - static QPixmap* m_bg; - static int m_bgRefCnt; - - bool m_erroneous; - }; -} - -#endif diff --git a/kmymoney/widgets/investtransaction.cpp b/kmymoney/widgets/investtransaction.cpp deleted file mode 100644 index e25f92e4c..000000000 --- a/kmymoney/widgets/investtransaction.cpp +++ /dev/null @@ -1,957 +0,0 @@ -/*************************************************************************** - investtransaction.cpp - description - ------------------- - begin : Tue Jun 13 2006 - copyright : (C) 2000-2006 by Thomas Baumgart - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "investtransaction.h" -#include "investtransaction_p.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -#include - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmymoneypayeecombo.h" -#include "transaction.h" - -#include "mymoneyutils.h" -#include "mymoneytransaction.h" -#include "mymoneysplit.h" -#include "mymoneyfile.h" -#include "mymoneypayee.h" -#include "mymoneytag.h" -#include "register.h" -#include "kmymoneycategory.h" -#include "kmymoneydateinput.h" -#include "transactionform.h" -#include "kmymoneylineedit.h" -#include "kmymoneyedit.h" -#include "transactioneditor.h" -#include "investtransactioneditor.h" -#include "kmymoneyutils.h" -#include "kmymoneymvccombo.h" -#ifndef KMM_DESIGNER -#include "stdtransactioneditor.h" -#endif - -#include "kmymoneyglobalsettings.h" - -using namespace KMyMoneyRegister; -using namespace KMyMoneyTransactionForm; - -InvestTransaction::InvestTransaction() : - Transaction(*new InvestTransactionPrivate, nullptr, MyMoneyTransaction(), MyMoneySplit(), 0) -{ -} - -InvestTransaction::InvestTransaction(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : - Transaction(*new InvestTransactionPrivate, parent, transaction, split, uniqueId) -{ - Q_D(InvestTransaction); -#ifndef KMM_DESIGNER - // dissect the transaction into its type, splits, currency, security etc. - KMyMoneyUtils::dissectTransaction(d->m_transaction, d->m_split, - d->m_assetAccountSplit, - d->m_feeSplits, - d->m_interestSplits, - d->m_security, - d->m_currency, - d->m_transactionType); -#endif - - QList::ConstIterator it_s; - for (it_s = d->m_feeSplits.constBegin(); it_s != d->m_feeSplits.constEnd(); ++it_s) { - d->m_feeAmount += (*it_s).value(); - } - for (it_s = d->m_interestSplits.constBegin(); it_s != d->m_interestSplits.constEnd(); ++it_s) { - d->m_interestAmount += (*it_s).value(); - } - - // check the count of the fee splits and setup the text - switch (d->m_feeSplits.count()) { - case 0: - break; - - case 1: - d->m_feeCategory = MyMoneyFile::instance()->accountToCategory(d->m_feeSplits[0].accountId()); - break; - - default: - d->m_feeCategory = i18nc("Split transaction (category replacement)", "Split transaction"); - break; - } - - // check the count of the interest splits and setup the text - switch (d->m_interestSplits.count()) { - case 0: - break; - - case 1: - d->m_interestCategory = MyMoneyFile::instance()->accountToCategory(d->m_interestSplits[0].accountId()); - break; - - default: - d->m_interestCategory = i18nc("Split transaction (category replacement)", "Split transaction"); - break; - } - - d->m_rowsForm = 7; - - // setup initial size - setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); - - emit parent->itemAdded(this); -} - -InvestTransaction::InvestTransaction(const InvestTransaction& other) : - Transaction(*new InvestTransactionPrivate(*other.d_func())) -{ -} - -InvestTransaction::~InvestTransaction() -{ -} - -const QString InvestTransaction::sortSecurity() const -{ - Q_D(const InvestTransaction); - return d->m_security.name(); -} - -const char* InvestTransaction::className() -{ - return "InvestTransaction"; -} - -void InvestTransaction::setupForm(TransactionForm* form) -{ - Transaction::setupForm(form); - form->setSpan(5, 1, 2, 1); -} - -void InvestTransaction::activity(QString& txt, eMyMoney::Split::InvestmentTransactionType type) const -{ - switch (type) { - case eMyMoney::Split::InvestmentTransactionType::AddShares: - txt = i18n("Add shares"); - break; - case eMyMoney::Split::InvestmentTransactionType::RemoveShares: - txt = i18n("Remove shares"); - break; - case eMyMoney::Split::InvestmentTransactionType::BuyShares: - txt = i18n("Buy shares"); - break; - case eMyMoney::Split::InvestmentTransactionType::SellShares: - txt = i18n("Sell shares"); - break; - case eMyMoney::Split::InvestmentTransactionType::Dividend: - txt = i18n("Dividend"); - break; - case eMyMoney::Split::InvestmentTransactionType::ReinvestDividend: - txt = i18n("Reinvest Dividend"); - break; - case eMyMoney::Split::InvestmentTransactionType::Yield: - txt = i18n("Yield"); - break; - case eMyMoney::Split::InvestmentTransactionType::SplitShares: - txt = i18n("Split shares"); - break; - case eMyMoney::Split::InvestmentTransactionType::InterestIncome: - txt = i18n("Interest Income"); - break; - default: - txt = i18nc("Unknown investment activity", "Unknown"); - break; - } -} - -bool InvestTransaction::formCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* /* painter */) -{ - Q_D(InvestTransaction); - bool fieldEditable = false; - - switch (row) { - case 0: - switch (col) { - case LabelColumn1: - align |= Qt::AlignLeft; - txt = i18n("Activity"); - break; - - case ValueColumn1: - align |= Qt::AlignLeft; - fieldEditable = true; - activity(txt, d->m_transactionType); - break; - - case LabelColumn2: - align |= Qt::AlignLeft; - txt = i18n("Date"); - break; - - case ValueColumn2: - align |= Qt::AlignRight; - fieldEditable = true; - if (d->m_transaction != MyMoneyTransaction()) - txt = QLocale().toString(d->m_transaction.postDate(), QLocale::ShortFormat); - break; - } - break; - - case 1: - switch (col) { - case LabelColumn1: - align |= Qt::AlignLeft; - txt = i18n("Security"); - break; - - case ValueColumn1: - align |= Qt::AlignLeft; - fieldEditable = true; - if (d->m_account.isInvest()) - txt = d->m_security.name(); - break; - - case LabelColumn2: - align |= Qt::AlignLeft; - if (haveShares()) { - txt = i18n("Shares"); - } else if (haveSplitRatio()) { - txt = i18n("Ratio"); - } - break; - - case ValueColumn2: - align |= Qt::AlignRight; - if ((fieldEditable = haveShares()) == true) { - txt = d->m_split.shares().abs().formatMoney(QString(), MyMoneyMoney::denomToPrec(d->m_security.smallestAccountFraction())); - } else if (haveSplitRatio()) { - txt = QString("1 / %1").arg(d->m_split.shares().abs().formatMoney(QString(), -1)); - } - break; - } - break; - - case 2: - switch (col) { - case LabelColumn1: - align |= Qt::AlignLeft; - if (haveAssetAccount()) - txt = i18n("Account"); - break; - - case ValueColumn1: - align |= Qt::AlignLeft; - if ((fieldEditable = haveAssetAccount()) == true) { - txt = MyMoneyFile::instance()->accountToCategory(d->m_assetAccountSplit.accountId()); - } - break; - - case LabelColumn2: - align |= Qt::AlignLeft; - if (havePrice()) - txt = i18n("Price/share"); - break; - - case ValueColumn2: - align |= Qt::AlignRight; - if ((fieldEditable = havePrice()) == true && !d->m_split.shares().isZero()) { - txt = d->m_split.price().formatMoney(QString(), d->m_security.pricePrecision()); - } - break; - } - break; - - case 3: - switch (col) { - case LabelColumn1: - align |= Qt::AlignLeft; - if (haveFees()) - txt = i18n("Fees"); - break; - - case ValueColumn1: - align |= Qt::AlignLeft; - if ((fieldEditable = haveFees()) == true) { - txt = d->m_feeCategory; - } - break; - - case LabelColumn2: - align |= Qt::AlignLeft; - if (haveFees() && !d->m_feeCategory.isEmpty()) - txt = i18n("Fee Amount"); - break; - - case ValueColumn2: - align |= Qt::AlignRight; - if (haveFees()) { - if ((fieldEditable = !d->m_feeCategory.isEmpty()) == true) { - txt = MyMoneyUtils::formatMoney(d->m_feeAmount, d->m_currency); - } - } - break; - } - break; - - case 4: - switch (col) { - case LabelColumn1: - align |= Qt::AlignLeft; - if (haveInterest()) - txt = i18n("Interest"); - break; - - case ValueColumn1: - align |= Qt::AlignLeft; - if ((fieldEditable = haveInterest()) == true) { - txt = d->m_interestCategory; - } - break; - - case LabelColumn2: - align |= Qt::AlignLeft; - if (haveInterest() && !d->m_interestCategory.isEmpty()) - txt = i18n("Interest"); - break; - - case ValueColumn2: - align |= Qt::AlignRight; - if (haveInterest()) { - if ((fieldEditable = !d->m_interestCategory.isEmpty()) == true) { - txt = MyMoneyUtils::formatMoney(-d->m_interestAmount, d->m_currency); - } - } - break; - } - break; - - case 5: - switch (col) { - case LabelColumn1: - align |= Qt::AlignLeft; - txt = i18n("Memo"); - break; - - case ValueColumn1: - align &= ~Qt::AlignVCenter; - align |= Qt::AlignTop; - align |= Qt::AlignLeft; - fieldEditable = true; - if (d->m_transaction != MyMoneyTransaction()) - txt = d->m_split.memo().section('\n', 0, 2); - break; - - case LabelColumn2: - align |= Qt::AlignLeft; - if (haveAmount()) - txt = i18nc("Total balance", "Total"); - break; - - case ValueColumn2: - align |= Qt::AlignRight; - if ((fieldEditable = haveAmount()) == true) { - txt = d->m_assetAccountSplit.value().abs() - .formatMoney(d->m_currency.tradingSymbol(), MyMoneyMoney::denomToPrec(d->m_currency.smallestAccountFraction())); - } - } - break; - - case 6: - switch (col) { - case LabelColumn2: - align |= Qt::AlignLeft; - txt = i18n("Status"); - break; - - case ValueColumn2: - align |= Qt::AlignRight; - fieldEditable = true; - txt = reconcileState(); - break; - } - } - - return fieldEditable; -} - -void InvestTransaction::registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* /* painter */) -{ - Q_D(InvestTransaction); - switch (row) { - case 0: - switch (col) { - case DateColumn: - align |= Qt::AlignLeft; - txt = QLocale().toString(d->m_transaction.postDate(), QLocale::ShortFormat); - break; - - case DetailColumn: - align |= Qt::AlignLeft; - activity(txt, d->m_transactionType); - break; - - case SecurityColumn: - align |= Qt::AlignLeft; - if (d->m_account.isInvest()) - txt = d->m_security.name(); - break; - - case ReconcileFlagColumn: - align |= Qt::AlignHCenter; - txt = reconcileState(false); - break; - - case QuantityColumn: - align |= Qt::AlignRight; - if (haveShares()) - txt = d->m_split.shares().abs().formatMoney(QString(), MyMoneyMoney::denomToPrec(d->m_security.smallestAccountFraction())); - else if (haveSplitRatio()) { - txt = QString("1 / %1").arg(d->m_split.shares().abs().formatMoney(QString(), -1)); - } - break; - - case PriceColumn: - align |= Qt::AlignRight; - if (havePrice() && !d->m_split.shares().isZero()) { - txt = d->m_split.price().formatMoney(d->m_currency.tradingSymbol(), d->m_security.pricePrecision()); - } - break; - - case ValueColumn: - align |= Qt::AlignRight; - if (haveAmount()) { - txt = MyMoneyUtils::formatMoney(d->m_assetAccountSplit.value().abs(), d->m_currency); - - } else if (haveInterest()) { - txt = MyMoneyUtils::formatMoney(-d->m_interestAmount, d->m_currency); - } - break; - - case BalanceColumn: - align |= Qt::AlignRight; - if (d->m_showBalance) - txt = d->m_balance.formatMoney(QString(), MyMoneyMoney::denomToPrec(d->m_security.smallestAccountFraction())); - else - txt = "----"; - break; - - default: - break; - } - break; - - case 1: - switch (col) { - case DetailColumn: - align |= Qt::AlignLeft; - if (haveAssetAccount() && !d->m_assetAccountSplit.accountId().isEmpty()) { - txt = MyMoneyFile::instance()->accountToCategory(d->m_assetAccountSplit.accountId()); - } else if (haveInterest() && d->m_interestSplits.count()) { - txt = d->m_interestCategory; - } else if (haveFees() && d->m_feeSplits.count()) { - txt = d->m_feeCategory; - } else - singleLineMemo(txt, d->m_split); - break; - - case QuantityColumn: - align |= Qt::AlignRight; - if (haveAssetAccount() && !d->m_assetAccountSplit.accountId().isEmpty()) { - // txt = m_interestAmount.abs().formatMoney(m_currency); - } else if (haveInterest() && d->m_interestSplits.count()) { - txt = MyMoneyUtils::formatMoney(-d->m_interestAmount, d->m_currency); - } else if (haveFees() && d->m_feeSplits.count()) { - txt = MyMoneyUtils::formatMoney(d->m_feeAmount, d->m_currency); - } - break; - - default: - break; - } - break; - - case 2: - switch (col) { - case DetailColumn: - align |= Qt::AlignLeft; - if (haveAssetAccount() && !d->m_assetAccountSplit.accountId().isEmpty() - && haveInterest() && d->m_interestSplits.count()) { - txt = d->m_interestCategory; - } else if (haveFees() && d->m_feeSplits.count()) { - txt = d->m_feeCategory; - } else - singleLineMemo(txt, d->m_split); - break; - - case QuantityColumn: - align |= Qt::AlignRight; - if (haveAssetAccount() && !d->m_assetAccountSplit.accountId().isEmpty() - && haveInterest() && d->m_interestSplits.count()) { - txt = MyMoneyUtils::formatMoney(-d->m_interestAmount, d->m_currency); - } else if (haveFees() && d->m_feeSplits.count()) { - txt = MyMoneyUtils::formatMoney(d->m_feeAmount, d->m_currency); - } - break; - - default: - break; - } - break; - - case 3: - switch (col) { - case DetailColumn: - align |= Qt::AlignLeft; - if (haveAssetAccount() && !d->m_assetAccountSplit.accountId().isEmpty() - && haveInterest() && d->m_interestSplits.count() - && haveFees() && d->m_feeSplits.count()) { - txt = d->m_feeCategory; - } else - singleLineMemo(txt, d->m_split); - break; - - case QuantityColumn: - align |= Qt::AlignRight; - if (haveAssetAccount() && !d->m_assetAccountSplit.accountId().isEmpty() - && haveInterest() && d->m_interestSplits.count() - && haveFees() && d->m_feeSplits.count()) { - txt = MyMoneyUtils::formatMoney(d->m_feeAmount, d->m_currency); - } - break; - - default: - break; - } - break; - - case 4: - switch (col) { - case DetailColumn: - align |= Qt::AlignLeft; - singleLineMemo(txt, d->m_split); - break; - - default: - break; - } - break; - } -} - -int InvestTransaction::registerColWidth(int col, const QFontMetrics& cellFontMetrics) -{ - Q_D(InvestTransaction); - QString txt; - MyMoneyMoney amount; - int nw = 0; - - // for now just check all rows in that column - for (int row = 0; row < d->m_rowsRegister; ++row) { - int w; - Transaction::registerCellText(txt, row, col); - w = cellFontMetrics.width(txt + " "); - nw = qMax(nw, w); - } - - // TODO the optimized way would be to base the size on the contents of a single row - // as we do it in StdTransaction::registerColWidth() -#if 0 - switch (col) { - default: - break; - - case PriceColumn: - if (havePrice()) { - txt = (m_split.value() / m_split.shares()).formatMoney(QString(), KMyMoneyGlobalSettings::pricePrecision()); - nw = cellFontMetrics.width(txt + " "); - } - break; - } -#endif - return nw; -} - -void InvestTransaction::loadTab(KMyMoneyTransactionForm::TransactionForm* /* form */) -{ -} - -int InvestTransaction::numColsForm() const -{ - return 4; -} - -void InvestTransaction::arrangeWidgetsInForm(QMap& editWidgets) -{ - Q_D(InvestTransaction); - if (!d->m_form || !d->m_parent) - return; - - setupFormPalette(editWidgets); - - // arrange the edit widgets - arrangeWidget(d->m_form, 0, ValueColumn1, editWidgets["activity"]); - arrangeWidget(d->m_form, 0, ValueColumn2, editWidgets["postdate"]); - arrangeWidget(d->m_form, 1, ValueColumn1, editWidgets["security"]); - arrangeWidget(d->m_form, 1, ValueColumn2, editWidgets["shares"]); - arrangeWidget(d->m_form, 2, ValueColumn1, editWidgets["asset-account"]); - arrangeWidget(d->m_form, 2, ValueColumn2, editWidgets["price"]); - arrangeWidget(d->m_form, 3, ValueColumn1, editWidgets["fee-account"]->parentWidget()); - arrangeWidget(d->m_form, 3, ValueColumn2, editWidgets["fee-amount"]); - arrangeWidget(d->m_form, 4, ValueColumn1, editWidgets["interest-account"]->parentWidget()); - arrangeWidget(d->m_form, 4, ValueColumn2, editWidgets["interest-amount"]); - arrangeWidget(d->m_form, 5, ValueColumn1, editWidgets["memo"]); - arrangeWidget(d->m_form, 5, ValueColumn2, editWidgets["total"]); - arrangeWidget(d->m_form, 6, ValueColumn2, editWidgets["status"]); - - // arrange dynamic labels - arrangeWidget(d->m_form, 0, LabelColumn1, editWidgets["activity-label"]); - arrangeWidget(d->m_form, 0, LabelColumn2, editWidgets["postdate-label"]); - arrangeWidget(d->m_form, 1, LabelColumn1, editWidgets["security-label"]); - arrangeWidget(d->m_form, 1, LabelColumn2, editWidgets["shares-label"]); - arrangeWidget(d->m_form, 2, LabelColumn1, editWidgets["asset-label"]); - arrangeWidget(d->m_form, 2, LabelColumn2, editWidgets["price-label"]); - arrangeWidget(d->m_form, 3, LabelColumn1, editWidgets["fee-label"]); - arrangeWidget(d->m_form, 3, LabelColumn2, editWidgets["fee-amount-label"]); - arrangeWidget(d->m_form, 4, LabelColumn1, editWidgets["interest-label"]); - arrangeWidget(d->m_form, 4, LabelColumn2, editWidgets["interest-amount-label"]); - arrangeWidget(d->m_form, 5, LabelColumn1, editWidgets["memo-label"]); - arrangeWidget(d->m_form, 5, LabelColumn2, editWidgets["total-label"]); - arrangeWidget(d->m_form, 6, LabelColumn2, editWidgets["status-label"]); - - // get rid of the hints. we don't need them for the form - QMap::iterator it; - for (it = editWidgets.begin(); it != editWidgets.end(); ++it) { - KMyMoneyCombo* combo = dynamic_cast(*it); - kMyMoneyLineEdit* lineedit = dynamic_cast(*it); - kMyMoneyEdit* edit = dynamic_cast(*it); - KMyMoneyPayeeCombo* payee = dynamic_cast(*it); - if (combo) - combo->setPlaceholderText(QString()); - if (edit) - edit->setPlaceholderText(QString()); - if (lineedit) - lineedit->setPlaceholderText(QString()); - if (payee) - payee->setPlaceholderText(QString()); - } -} - -void InvestTransaction::tabOrderInForm(QWidgetList& tabOrderWidgets) const -{ - Q_D(const InvestTransaction); - // activity - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(0, ValueColumn1))); - - // date - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(0, ValueColumn2))); - - // security - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(1, ValueColumn1))); - - // shares - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(1, ValueColumn2))); - - // account - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(2, ValueColumn1))); - - // price - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(2, ValueColumn2))); - - // make sure to have the fee category field and the split button as separate tab order widgets - // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but - // it's one of our own widgets, so we actually don't care. Just make sure, that we don't - // go haywire when someone changes the KMyMoneyCategory object ... - QWidget* w = d->m_form->cellWidget(3, ValueColumn1); - tabOrderWidgets.append(focusWidget(w)); - w = w->findChild("splitButton"); - if (w) - tabOrderWidgets.append(w); - - // fee amount - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(3, ValueColumn2))); - - // the same applies for the interest categories - w = d->m_form->cellWidget(4, ValueColumn1); - tabOrderWidgets.append(focusWidget(w)); - w = w->findChild("splitButton"); - if (w) - tabOrderWidgets.append(w); - - // interest amount - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(4, ValueColumn2))); - - // memo - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(5, ValueColumn1))); - - // state - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(6, ValueColumn2))); -} - -void InvestTransaction::arrangeWidgetsInRegister(QMap& editWidgets) -{ - Q_D(InvestTransaction); - if (!d->m_parent) - return; - - setupRegisterPalette(editWidgets); - - arrangeWidget(d->m_parent, d->m_startRow + 0, DateColumn, editWidgets["postdate"]); - arrangeWidget(d->m_parent, d->m_startRow + 0, SecurityColumn, editWidgets["security"]); - arrangeWidget(d->m_parent, d->m_startRow + 0, DetailColumn, editWidgets["activity"]); - arrangeWidget(d->m_parent, d->m_startRow + 1, DetailColumn, editWidgets["asset-account"]); - arrangeWidget(d->m_parent, d->m_startRow + 2, DetailColumn, editWidgets["interest-account"]->parentWidget()); - arrangeWidget(d->m_parent, d->m_startRow + 3, DetailColumn, editWidgets["fee-account"]->parentWidget()); - arrangeWidget(d->m_parent, d->m_startRow + 4, DetailColumn, editWidgets["memo"]); - arrangeWidget(d->m_parent, d->m_startRow + 0, QuantityColumn, editWidgets["shares"]); - arrangeWidget(d->m_parent, d->m_startRow + 0, PriceColumn, editWidgets["price"]); - arrangeWidget(d->m_parent, d->m_startRow + 2, QuantityColumn, editWidgets["interest-amount"]); - arrangeWidget(d->m_parent, d->m_startRow + 3, QuantityColumn, editWidgets["fee-amount"]); - arrangeWidget(d->m_parent, d->m_startRow + 0, ValueColumn, editWidgets["total"]); - arrangeWidget(d->m_parent, d->m_startRow + 1, DateColumn, editWidgets["status"]); - - // increase the height of the row containing the memo widget - d->m_parent->setRowHeight(d->m_startRow + 4, d->m_parent->rowHeightHint() * 3); -} - -void InvestTransaction::tabOrderInRegister(QWidgetList& tabOrderWidgets) const -{ - Q_D(const InvestTransaction); - QWidget* w; - - // date - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, DateColumn))); - // security - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, SecurityColumn))); - // activity - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, DetailColumn))); - // shares - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, QuantityColumn))); - // price - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, PriceColumn))); - // asset account - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 1, DetailColumn))); - - // make sure to have the category fields and the split button as separate tab order widgets - // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but - // it's one of our own widgets, so we actually don't care. Just make sure, that we don't - // go haywire when someone changes the KMyMoneyCategory object ... - w = d->m_parent->cellWidget(d->m_startRow + 2, DetailColumn); // interest account - tabOrderWidgets.append(focusWidget(w)); - w = w->findChild("splitButton"); - if (w) - tabOrderWidgets.append(w); - - // interest amount - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 2, QuantityColumn))); - - w = d->m_parent->cellWidget(d->m_startRow + 3, DetailColumn); // fee account - tabOrderWidgets.append(focusWidget(w)); - w = w->findChild("splitButton"); - if (w) - tabOrderWidgets.append(w); - - // fee amount - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 3, QuantityColumn))); - - // memo - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 4, DetailColumn))); - - // status - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 1, DateColumn))); -} - -KMyMoneyRegister::Action InvestTransaction::actionType() const -{ - return KMyMoneyRegister::ActionNone; -} - -int InvestTransaction::numRowsRegister(bool expanded) const -{ - Q_D(const InvestTransaction); - int numRows = 1; - if (expanded) { - if (!d->m_inEdit) { - if (haveAssetAccount() && !d->m_assetAccountSplit.accountId().isEmpty()) - ++numRows; - if (haveInterest() && d->m_interestSplits.count()) - ++numRows; - if (haveFees() && d->m_feeSplits.count()) - ++numRows; - if (!d->m_split.memo().isEmpty()) - ++numRows; - } else - numRows = 5; - } - return numRows; -} - -bool InvestTransaction::haveShares() const -{ - Q_D(const InvestTransaction); - auto rc = true; - switch (d->m_transactionType) { - case eMyMoney::Split::InvestmentTransactionType::Dividend: - case eMyMoney::Split::InvestmentTransactionType::Yield: - case eMyMoney::Split::InvestmentTransactionType::SplitShares: - case eMyMoney::Split::InvestmentTransactionType::InterestIncome: - rc = false; - break; - - default: - break; - } - return rc; -} - -bool InvestTransaction::haveFees() const -{ - Q_D(const InvestTransaction); - auto rc = true; - switch (d->m_transactionType) { - case eMyMoney::Split::InvestmentTransactionType::AddShares: - case eMyMoney::Split::InvestmentTransactionType::RemoveShares: - case eMyMoney::Split::InvestmentTransactionType::SplitShares: - rc = false; - break; - - default: - break; - } - return rc; -} - -bool InvestTransaction::haveInterest() const -{ - Q_D(const InvestTransaction); - auto rc = false; - switch (d->m_transactionType) { - case eMyMoney::Split::InvestmentTransactionType::BuyShares: - case eMyMoney::Split::InvestmentTransactionType::SellShares: - case eMyMoney::Split::InvestmentTransactionType::Dividend: - case eMyMoney::Split::InvestmentTransactionType::ReinvestDividend: - case eMyMoney::Split::InvestmentTransactionType::Yield: - case eMyMoney::Split::InvestmentTransactionType::InterestIncome: - rc = true; - break; - - default: - break; - } - return rc; -} - -bool InvestTransaction::havePrice() const -{ - Q_D(const InvestTransaction); - auto rc = false; - switch (d->m_transactionType) { - case eMyMoney::Split::InvestmentTransactionType::BuyShares: - case eMyMoney::Split::InvestmentTransactionType::SellShares: - case eMyMoney::Split::InvestmentTransactionType::ReinvestDividend: - rc = true; - break; - - default: - break; - } - return rc; -} - -bool InvestTransaction::haveAmount() const -{ - Q_D(const InvestTransaction); - auto rc = false; - switch (d->m_transactionType) { - case eMyMoney::Split::InvestmentTransactionType::BuyShares: - case eMyMoney::Split::InvestmentTransactionType::SellShares: - case eMyMoney::Split::InvestmentTransactionType::Dividend: - case eMyMoney::Split::InvestmentTransactionType::Yield: - case eMyMoney::Split::InvestmentTransactionType::InterestIncome: - rc = true; - break; - - default: - break; - } - return rc; -} - -bool InvestTransaction::haveAssetAccount() const -{ - Q_D(const InvestTransaction); - auto rc = true; - switch (d->m_transactionType) { - case eMyMoney::Split::InvestmentTransactionType::AddShares: - case eMyMoney::Split::InvestmentTransactionType::RemoveShares: - case eMyMoney::Split::InvestmentTransactionType::SplitShares: - case eMyMoney::Split::InvestmentTransactionType::ReinvestDividend: - rc = false; - break; - - default: - break; - } - return rc; -} - -bool InvestTransaction::haveSplitRatio() const -{ - Q_D(const InvestTransaction); - return d->m_transactionType == eMyMoney::Split::InvestmentTransactionType::SplitShares; -} - -void InvestTransaction::splits(MyMoneySplit& assetAccountSplit, QList& interestSplits, QList& feeSplits) const -{ - Q_D(const InvestTransaction); - assetAccountSplit = d->m_assetAccountSplit; - interestSplits = d->m_interestSplits; - feeSplits = d->m_feeSplits; -} - -int InvestTransaction::numRowsRegister() const -{ - return RegisterItem::numRowsRegister(); -} - -TransactionEditor* InvestTransaction::createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) -{ -#ifndef KMM_DESIGNER - Q_D(InvestTransaction); - d->m_inRegisterEdit = regForm == d->m_parent; - return new InvestTransactionEditor(regForm, this, list, lastPostDate); -#else - return NULL; -#endif -} - diff --git a/kmymoney/widgets/investtransaction.h b/kmymoney/widgets/investtransaction.h deleted file mode 100644 index eedb63be9..000000000 --- a/kmymoney/widgets/investtransaction.h +++ /dev/null @@ -1,128 +0,0 @@ -/*************************************************************************** - investtransaction.h - description - ------------------- - begin : Tue Jun 13 2006 - copyright : (C) 2000-2006 by Thomas Baumgart - email : Thomas Baumgart - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef INVESTTRANSACTION_H -#define INVESTTRANSACTION_H - -// ---------------------------------------------------------------------------- -// QT Includes - -#include -#include -#include -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "transaction.h" - -namespace eMyMoney { namespace Split { enum class InvestmentTransactionType; } } - -namespace KMyMoneyRegister -{ - - class InvestTransactionPrivate; - class InvestTransaction : public Transaction - { - public: - explicit InvestTransaction(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - InvestTransaction(const InvestTransaction & other); - InvestTransaction(InvestTransaction && other); - InvestTransaction & operator=(InvestTransaction other); - friend void swap(InvestTransaction& first, InvestTransaction& second); - ~InvestTransaction() override; - - virtual const QString sortSecurity() const override; - virtual const char* className() override; - - bool formCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter = 0) override; - void registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter = 0) override; - - int registerColWidth(int col, const QFontMetrics& cellFontMetrics) override; - void setupForm(KMyMoneyTransactionForm::TransactionForm* form) override; - - /** - * provide NOP here as the investment transaction form does not supply a tab - */ - void loadTab(KMyMoneyTransactionForm::TransactionForm* /* form */) override; - - int numColsForm() const override; - - void arrangeWidgetsInForm(QMap& editWidgets) override; - void arrangeWidgetsInRegister(QMap& editWidgets) override; - void tabOrderInForm(QWidgetList& tabOrderWidgets) const override; - void tabOrderInRegister(QWidgetList& tabOrderWidgets) const override; - KMyMoneyRegister::Action actionType() const override; - - int numRowsRegister(bool expanded) const override; - - /** - * Provided for internal reasons. No API change. See RegisterItem::numRowsRegister() - */ - int numRowsRegister() const override; - - TransactionEditor* createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) override; - - void splits(MyMoneySplit& assetAccountSplit, QList& interestSplits, QList& feeSplits) const; - - protected: - bool haveShares() const; - bool haveFees() const; - bool haveInterest() const; - bool havePrice() const; - bool haveAmount() const; - bool haveAssetAccount() const; - bool haveSplitRatio() const; - - /** - * Returns textual representation of the activity identified - * by @p type. - * - * @param txt reference to QString where to store the result - * @param type activity represented as investTransactionTypeE - */ - void activity(QString& txt, eMyMoney::Split::InvestmentTransactionType type) const; - - private: - InvestTransaction(); - Q_DECLARE_PRIVATE(InvestTransaction) - }; - - inline void swap(InvestTransaction& first, InvestTransaction& second) // krazy:exclude=inline - { - using std::swap; - swap(first.d_ptr, second.d_ptr); - } - - inline InvestTransaction::InvestTransaction(InvestTransaction && other) : InvestTransaction() // krazy:exclude=inline - { - swap(*this, other); - } - - inline InvestTransaction & InvestTransaction::operator=(InvestTransaction other) // krazy:exclude=inline - { - swap(*this, other); - return *this; - } - -} // namespace - -#endif diff --git a/kmymoney/widgets/investtransaction_p.h b/kmymoney/widgets/investtransaction_p.h deleted file mode 100644 index 1a09577a9..000000000 --- a/kmymoney/widgets/investtransaction_p.h +++ /dev/null @@ -1,94 +0,0 @@ -/*************************************************************************** - investtransaction_p.h - description - ------------------- - begin : Tue Jun 13 2006 - copyright : (C) 2000-2006 by Thomas Baumgart - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef INVESTTRANSACTION_P_H -#define INVESTTRANSACTION_P_H - -#include "investtransaction.h" -#include "transaction_p.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -#include - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "transaction.h" - -#include "mymoneyutils.h" -#include "mymoneytransaction.h" -#include "mymoneysplit.h" -#include "mymoneyfile.h" -#include "mymoneypayee.h" -#include "mymoneytag.h" -#include "mymoneysecurity.h" -#include "register.h" -#include "kmymoneycategory.h" -#include "kmymoneydateinput.h" -#include "transactionform.h" -#include "kmymoneylineedit.h" -#include "kmymoneyedit.h" -#include "transactioneditor.h" -#include "investtransactioneditor.h" -#include "kmymoneyutils.h" -#include "kmymoneymvccombo.h" -#ifndef KMM_DESIGNER -#include "stdtransactioneditor.h" -#endif - -#include "kmymoneyglobalsettings.h" - -using namespace KMyMoneyRegister; -using namespace KMyMoneyTransactionForm; - -namespace KMyMoneyRegister -{ - class InvestTransactionPrivate : public TransactionPrivate - { - public: - QList m_feeSplits; - QList m_interestSplits; - MyMoneySplit m_assetAccountSplit; - MyMoneySecurity m_security; - MyMoneySecurity m_currency; - eMyMoney::Split::InvestmentTransactionType m_transactionType; - QString m_feeCategory; - QString m_interestCategory; - MyMoneyMoney m_feeAmount; - MyMoneyMoney m_interestAmount; - MyMoneyMoney m_totalAmount; - }; -} - -#endif diff --git a/kmymoney/widgets/kaccounttemplateselector.cpp b/kmymoney/widgets/kaccounttemplateselector.cpp index 1c6be3099..deda48d86 100644 --- a/kmymoney/widgets/kaccounttemplateselector.cpp +++ b/kmymoney/widgets/kaccounttemplateselector.cpp @@ -1,289 +1,277 @@ /*************************************************************************** kaccounttemplateselector.cpp - description ------------------- begin : Tue Feb 5 2008 copyright : (C) 2008 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include "kaccounttemplateselector.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes - -#include "ui_kaccounttemplateselector.h" - #include -class KAccountTemplateSelectorPrivate +class KAccountTemplateSelector::Private { - Q_DISABLE_COPY(KAccountTemplateSelectorPrivate) - public: - KAccountTemplateSelectorPrivate() : - ui(new Ui::KAccountTemplateSelector), - id(0) + Private(KAccountTemplateSelector* p) : + id(0) { + m_parent = p; } +#ifndef KMM_DESIGNER + QList selectedTemplates() const; + QTreeWidgetItem* hierarchyItem(const QString& parent, const QString& name); + void loadHierarchy(); +#endif + +public: + KAccountTemplateSelector* m_parent; + QMap m_templateHierarchy; +#ifndef KMM_DESIGNER + QMap m_templates; + // a map of country name or country name (language name) -> localeId (lang_country) so be careful how you use it + QMap countries; + QString currentLocaleId; + QMap::iterator it_m; + QStringList dirlist; + int id; +#endif +}; - ~KAccountTemplateSelectorPrivate() - { - delete ui; - } #ifndef KMM_DESIGNER -QTreeWidgetItem* hierarchyItem(const QString& parent, const QString& name) +QTreeWidgetItem* KAccountTemplateSelector::Private::hierarchyItem(const QString& parent, const QString& name) { if (!m_templateHierarchy.contains(parent) || m_templateHierarchy[parent] == 0) { QRegExp exp("(.*):(.*)"); if (exp.indexIn(parent) != -1) m_templateHierarchy[parent] = hierarchyItem(exp.cap(1), exp.cap(2)); } QTreeWidgetItem *item = new QTreeWidgetItem(m_templateHierarchy[parent]); item->setText(0, name); return item; } -void loadHierarchy() +void KAccountTemplateSelector::Private::loadHierarchy() { m_templateHierarchy.clear(); - QTreeWidgetItemIterator it(ui->m_groupList, QTreeWidgetItemIterator::Selected); + QTreeWidgetItemIterator it(m_parent->m_groupList, QTreeWidgetItemIterator::Selected); QTreeWidgetItem* it_v; while ((it_v = *it) != 0) { - m_templates[it_v->data(0, Qt::UserRole).toInt()].hierarchy(m_templateHierarchy); + m_templates[it_v->data(0, IdRole).toInt()].hierarchy(m_templateHierarchy); ++it; } // I need to think about this some more. The code works and shows // the current account hierarchy. It might be useful, to show // existing accounts dimmed and the new ones in bold or so. #if 0 // add the hierarchy from the MyMoneyFile object QList aList; QList::const_iterator it_a; - auto file = MyMoneyFile::instance(); + MyMoneyFile* file = MyMoneyFile::instance(); file->accountList(aList); if (aList.count() > 0) { m_templateHierarchy[file->accountToCategory(file->asset().id(), true)] = 0; m_templateHierarchy[file->accountToCategory(file->liability().id(), true)] = 0; m_templateHierarchy[file->accountToCategory(file->income().id(), true)] = 0; m_templateHierarchy[file->accountToCategory(file->expense().id(), true)] = 0; m_templateHierarchy[file->accountToCategory(file->equity().id(), true)] = 0; } for (it_a = aList.begin(); it_a != aList.end(); ++it_a) { m_templateHierarchy[file->accountToCategory((*it_a).id(), true)] = 0; } #endif - ui->m_accountList->clear(); + m_parent->m_accountList->clear(); QRegExp exp("(.*):(.*)"); for (QMap::iterator it_m = m_templateHierarchy.begin(); it_m != m_templateHierarchy.end(); ++it_m) { if (exp.indexIn(it_m.key()) == -1) { - (*it_m) = new QTreeWidgetItem(ui->m_accountList); + (*it_m) = new QTreeWidgetItem(m_parent->m_accountList); (*it_m)->setText(0, it_m.key()); } else { (*it_m) = hierarchyItem(exp.cap(1), exp.cap(2)); } (*it_m)->setExpanded(true); } - ui->m_description->clear(); - if (ui->m_groupList->currentItem()) { - ui->m_description->setText(m_templates[ui->m_groupList->currentItem()->data(0, Qt::UserRole).toInt()].longDescription()); + m_parent->m_description->clear(); + if (m_parent->m_groupList->currentItem()) { + m_parent->m_description->setText(m_templates[m_parent->m_groupList->currentItem()->data(0, IdRole).toInt()].longDescription()); } } -QList selectedTemplates() const +QList KAccountTemplateSelector::Private::selectedTemplates() const { QList list; - QTreeWidgetItemIterator it(ui->m_groupList, QTreeWidgetItemIterator::Selected); + QTreeWidgetItemIterator it(m_parent->m_groupList, QTreeWidgetItemIterator::Selected); QTreeWidgetItem* it_v; while ((it_v = *it) != 0) { - list << m_templates[it_v->data(0, Qt::UserRole).toInt()]; + list << m_templates[it_v->data(0, IdRole).toInt()]; ++it; } return list; } #endif - -public: - Ui::KAccountTemplateSelector *ui; - QMap m_templateHierarchy; -#ifndef KMM_DESIGNER - QMap m_templates; - // a map of country name or country name (language name) -> localeId (lang_country) so be careful how you use it - QMap countries; - QString currentLocaleId; - QMap::iterator it_m; - QStringList dirlist; - int id; -#endif -}; - KAccountTemplateSelector::KAccountTemplateSelector(QWidget* parent) : - QWidget(parent), - d_ptr(new KAccountTemplateSelectorPrivate) + KAccountTemplateSelectorDecl(parent), + d(new Private(this)) { - Q_D(KAccountTemplateSelector); - d->ui->setupUi(this); - d->ui->m_accountList->header()->hide(); - d->ui->m_groupList->setSelectionMode(QAbstractItemView::ExtendedSelection); - connect(d->ui->m_groupList, &QTreeWidget::itemSelectionChanged, this, &KAccountTemplateSelector::slotLoadHierarchy); + m_accountList->header()->hide(); + m_groupList->setSelectionMode(QAbstractItemView::ExtendedSelection); + connect(m_groupList, SIGNAL(itemSelectionChanged()), this, SLOT(slotLoadHierarchy())); // kick off loading of account template data QTimer::singleShot(0, this, SLOT(slotLoadTemplateList())); } KAccountTemplateSelector::~KAccountTemplateSelector() { - Q_D(KAccountTemplateSelector); delete d; } void KAccountTemplateSelector::slotLoadTemplateList() { #ifndef KMM_DESIGNER - Q_D(KAccountTemplateSelector); QStringList dirs; // get list of template subdirs and scan them for the list of subdirs d->dirlist = QStandardPaths::locateAll(QStandardPaths::DataLocation, "templates", QStandardPaths::LocateDirectory); QStringList::iterator it; for (it = d->dirlist.begin(); it != d->dirlist.end(); ++it) { QDir dir(*it); dirs = dir.entryList(QStringList("*"), QDir::Dirs); QStringList::iterator it_d; for (it_d = dirs.begin(); it_d != dirs.end(); ++it_d) { // we don't care about . and .. if ((*it_d) == ".." || (*it_d) == "." || (*it_d) == "C") continue; QLocale templateLocale(*it_d); if (templateLocale.language() != QLocale::C) { QString country = QLocale().countryToString(templateLocale.country()); QString lang = QLocale().languageToString(templateLocale.language()); if (d->countries.contains(country)) { if (d->countries[country] != *it_d) { QString otherName = d->countries[country]; QLocale otherTemplateLocale(otherName); QString otherCountry = QLocale().countryToString(otherTemplateLocale.country()); QString otherLang = QLocale().languageToString(otherTemplateLocale.language()); d->countries.remove(country); d->countries[QString("%1 (%2)").arg(otherCountry, otherLang)] = otherName; d->countries[QString("%1 (%2)").arg(country, lang)] = *it_d; // retain the item corresponding to the current locale if (QLocale().country() == templateLocale.country()) { d->currentLocaleId = *it_d; } } } else { d->countries[country] = *it_d; // retain the item corresponding to the current locale if (QLocale().country() == templateLocale.country()) { d->currentLocaleId = *it_d; } } } else { qDebug("'%s/%s' not scanned", qPrintable(*it), qPrintable(*it_d)); } } } // now that we know, what we can get at max, we scan everything // and parse the templates into memory - d->ui->m_groupList->clear(); + m_groupList->clear(); d->m_templates.clear(); d->it_m = d->countries.begin(); d->id = 1; if (d->it_m != d->countries.end()) QTimer::singleShot(0, this, SLOT(slotLoadCountry())); else { d->loadHierarchy(); } #endif } void KAccountTemplateSelector::slotLoadCountry() { #ifndef KMM_DESIGNER - Q_D(KAccountTemplateSelector); - QTreeWidgetItem *parent = new QTreeWidgetItem(d->ui->m_groupList); + QTreeWidgetItem *parent = new QTreeWidgetItem(m_groupList); parent->setText(0, d->it_m.key()); parent->setFlags(parent->flags() & ~Qt::ItemIsSelectable); for (QStringList::iterator it = d->dirlist.begin(); it != d->dirlist.end(); ++it) { QDir dir(QString("%1/%2").arg(*it).arg(*(d->it_m))); if (dir.exists()) { QStringList files = dir.entryList(QStringList("*"), QDir::Files); for (QStringList::iterator it_f = files.begin(); it_f != files.end(); ++it_f) { MyMoneyTemplate templ(QUrl::fromUserInput(QString("%1/%2").arg(dir.canonicalPath(), *it_f))); d->m_templates[d->id] = templ; QTreeWidgetItem *item = new QTreeWidgetItem(parent); item->setText(0, templ.title()); item->setText(1, templ.shortDescription()); - item->setData(0, Qt::UserRole, QString("%1").arg(d->id)); + item->setData(0, IdRole, QString("%1").arg(d->id)); ++d->id; } } } // make visible the templates of the current locale if (d->it_m.value() == d->currentLocaleId) { - d->ui->m_groupList->setCurrentItem(parent); - d->ui->m_groupList->expandItem(parent); - d->ui->m_groupList->scrollToItem(parent, QTreeView::PositionAtTop); + m_groupList->setCurrentItem(parent); + m_groupList->expandItem(parent); + m_groupList->scrollToItem(parent, QTreeView::PositionAtTop); } ++d->it_m; if (d->it_m != d->countries.end()) QTimer::singleShot(0, this, SLOT(slotLoadCountry())); else { d->loadHierarchy(); } #endif } void KAccountTemplateSelector::slotLoadHierarchy() { #ifndef KMM_DESIGNER - Q_D(KAccountTemplateSelector); d->loadHierarchy(); #endif } QList KAccountTemplateSelector::selectedTemplates() const { #ifndef KMM_DESIGNER - Q_D(const KAccountTemplateSelector); return d->selectedTemplates(); #else return QList(); #endif } diff --git a/kmymoney/widgets/kaccounttemplateselector.h b/kmymoney/widgets/kaccounttemplateselector.h index b1d22a4e2..a5f5b7e5d 100644 --- a/kmymoney/widgets/kaccounttemplateselector.h +++ b/kmymoney/widgets/kaccounttemplateselector.h @@ -1,60 +1,73 @@ /*************************************************************************** kaccounttemplateselector.h - description ------------------- begin : Tue Feb 5 2008 copyright : (C) 2008 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KACCOUNTTEMPLATESELECTOR_H #define KACCOUNTTEMPLATESELECTOR_H // ---------------------------------------------------------------------------- // QT Includes -#include +#include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +#include "ui_kaccounttemplateselectordecl.h" + class MyMoneyTemplate; +class KAccountTemplateSelectorDecl : public QWidget, public Ui::KAccountTemplateSelectorDecl +{ +public: + KAccountTemplateSelectorDecl(QWidget *parent) : QWidget(parent) { + setupUi(this); + } +}; /** * @author Thomas Baumgart */ -class KAccountTemplateSelectorPrivate; -class KAccountTemplateSelector : public QWidget +class KAccountTemplateSelector : public KAccountTemplateSelectorDecl { Q_OBJECT - Q_DISABLE_COPY(KAccountTemplateSelector) public: - explicit KAccountTemplateSelector(QWidget* parent = nullptr); + enum KAccountTemplateSelectorItemRoles { + IdRole = Qt::UserRole, /**< The id is stored in this role in column 0 as a string.*/ + }; + + KAccountTemplateSelector(QWidget* parent = 0); ~KAccountTemplateSelector(); QList selectedTemplates() const; private slots: void slotLoadHierarchy(); void slotLoadCountry(); void slotLoadTemplateList(); private: - KAccountTemplateSelectorPrivate * const d_ptr; - Q_DECLARE_PRIVATE(KAccountTemplateSelector) + /// \internal d-pointer class. + class Private; + /// \internal d-pointer instance. + Private* const d; }; #endif diff --git a/kmymoney/widgets/kaccounttemplateselector.ui b/kmymoney/widgets/kaccounttemplateselectordecl.ui similarity index 95% rename from kmymoney/widgets/kaccounttemplateselector.ui rename to kmymoney/widgets/kaccounttemplateselectordecl.ui index 11c02788b..62282a5b9 100644 --- a/kmymoney/widgets/kaccounttemplateselector.ui +++ b/kmymoney/widgets/kaccounttemplateselectordecl.ui @@ -1,84 +1,84 @@ - KAccountTemplateSelector - + KAccountTemplateSelectorDecl + 0 0 665 460 Account Types Description Detailed description 0 3 true Accounts Name KTextEdit QTextEdit
ktextedit.h
diff --git a/kmymoney/widgets/kbudgetvalues.cpp b/kmymoney/widgets/kbudgetvalues.cpp index 2e99b17eb..baa30328d 100644 --- a/kmymoney/widgets/kbudgetvalues.cpp +++ b/kmymoney/widgets/kbudgetvalues.cpp @@ -1,380 +1,345 @@ /*************************************************************************** kbudgetvalues - description ------------------- begin : Wed Nov 28 2007 copyright : (C) 2007 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kbudgetvalues.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include +#include "kmymoneyglobalsettings.h" // ---------------------------------------------------------------------------- // Project Includes -#include "ui_kbudgetvalues.h" - -#include "mymoneybudget.h" #include "kmymoneyedit.h" -#include "kmymoneyglobalsettings.h" - -class KBudgetValuesPrivate -{ - Q_DISABLE_COPY(KBudgetValuesPrivate) - -public: - KBudgetValuesPrivate() : - ui(new Ui::KBudgetValues) - { - } - - ~KBudgetValuesPrivate() - { - delete ui; - } - - void enableMonths(bool enabled) - { - for (int i = 1; i < 12; ++i) { - m_label[i]->setEnabled(enabled); - m_field[i]->setEnabled(enabled); - } - } - - void fillMonthLabels() - { - QDate date(m_budgetDate); - for (auto i = 0; i < 12; ++i) { - m_label[i]->setText(QLocale().standaloneMonthName(date.month(), QLocale::ShortFormat)); - date = date.addMonths(1); - } - } - - Ui::KBudgetValues *ui; - kMyMoneyEdit* m_field[12]; - QLabel* m_label[12]; - QWidget* m_currentTab; - QDate m_budgetDate; -}; KBudgetValues::KBudgetValues(QWidget* parent) : - QWidget(parent), - d_ptr(new KBudgetValuesPrivate) + KBudgetValuesDecl(parent), + m_currentTab(m_monthlyButton) { - Q_D(KBudgetValues); - d->ui->setupUi(this); - d->m_currentTab = d->ui->m_monthlyButton; - d->m_budgetDate = QDate(2007, KMyMoneyGlobalSettings::firstFiscalMonth(), KMyMoneyGlobalSettings::firstFiscalDay()); - - d->m_field[0] = d->ui->m_amount1; - d->m_field[1] = d->ui->m_amount2; - d->m_field[2] = d->ui->m_amount3; - d->m_field[3] = d->ui->m_amount4; - d->m_field[4] = d->ui->m_amount5; - d->m_field[5] = d->ui->m_amount6; - d->m_field[6] = d->ui->m_amount7; - d->m_field[7] = d->ui->m_amount8; - d->m_field[8] = d->ui->m_amount9; - d->m_field[9] = d->ui->m_amount10; - d->m_field[10] = d->ui->m_amount11; - d->m_field[11] = d->ui->m_amount12; - - d->m_label[0] = d->ui->m_label1; - d->m_label[1] = d->ui->m_label2; - d->m_label[2] = d->ui->m_label3; - d->m_label[3] = d->ui->m_label4; - d->m_label[4] = d->ui->m_label5; - d->m_label[5] = d->ui->m_label6; - d->m_label[6] = d->ui->m_label7; - d->m_label[7] = d->ui->m_label8; - d->m_label[8] = d->ui->m_label9; - d->m_label[9] = d->ui->m_label10; - d->m_label[10] = d->ui->m_label11; - d->m_label[11] = d->ui->m_label12; + m_budgetDate = QDate(2007, KMyMoneyGlobalSettings::firstFiscalMonth(), KMyMoneyGlobalSettings::firstFiscalDay()); + + m_field[0] = m_amount1; + m_field[1] = m_amount2; + m_field[2] = m_amount3; + m_field[3] = m_amount4; + m_field[4] = m_amount5; + m_field[5] = m_amount6; + m_field[6] = m_amount7; + m_field[7] = m_amount8; + m_field[8] = m_amount9; + m_field[9] = m_amount10; + m_field[10] = m_amount11; + m_field[11] = m_amount12; + + m_label[0] = m_label1; + m_label[1] = m_label2; + m_label[2] = m_label3; + m_label[3] = m_label4; + m_label[4] = m_label5; + m_label[5] = m_label6; + m_label[6] = m_label7; + m_label[7] = m_label8; + m_label[8] = m_label9; + m_label[9] = m_label10; + m_label[10] = m_label11; + m_label[11] = m_label12; // fill with standard labels - d->ui->m_monthlyButton->setChecked(true); - d->ui->m_periodGroup->setId(d->ui->m_monthlyButton, 0); - d->ui->m_periodGroup->setId(d->ui->m_yearlyButton, 1); - d->ui->m_periodGroup->setId(d->ui->m_individualButton, 2); - slotChangePeriod(d->ui->m_periodGroup->id(d->ui->m_monthlyButton)); + m_monthlyButton->setChecked(true); + m_periodGroup->setId(m_monthlyButton, 0); + m_periodGroup->setId(m_yearlyButton, 1); + m_periodGroup->setId(m_individualButton, 2); + slotChangePeriod(m_periodGroup->id(m_monthlyButton)); // connect(m_budgetLevel, SIGNAL(currentChanged(QWidget*)), this, SIGNAL(valuesChanged())); - connect(d->ui->m_amountMonthly, SIGNAL(valueChanged(QString)), this, SLOT(slotNeedUpdate())); - connect(d->ui->m_amountYearly, SIGNAL(valueChanged(QString)), this, SLOT(slotNeedUpdate())); - d->ui->m_amountMonthly->installEventFilter(this); - d->ui->m_amountYearly->installEventFilter(this); - - for (auto i = 0; i < 12; ++i) { - connect(d->m_field[i], SIGNAL(valueChanged(QString)), this, SLOT(slotNeedUpdate())); - d->m_field[i]->installEventFilter(this); + connect(m_amountMonthly, SIGNAL(valueChanged(QString)), this, SLOT(slotNeedUpdate())); + connect(m_amountYearly, SIGNAL(valueChanged(QString)), this, SLOT(slotNeedUpdate())); + m_amountMonthly->installEventFilter(this); + m_amountYearly->installEventFilter(this); + + for (int i = 0; i < 12; ++i) { + connect(m_field[i], SIGNAL(valueChanged(QString)), this, SLOT(slotNeedUpdate())); + m_field[i]->installEventFilter(this); } - connect(d->ui->m_clearButton, SIGNAL(clicked()), this, SLOT(slotClearAllValues())); - connect(d->ui->m_periodGroup, SIGNAL(buttonClicked(int)), this, SLOT(slotChangePeriod(int))); + connect(m_clearButton, SIGNAL(clicked()), this, SLOT(slotClearAllValues())); + connect(m_periodGroup, SIGNAL(buttonClicked(int)), this, SLOT(slotChangePeriod(int))); connect(this, SIGNAL(valuesChanged()), this, SLOT(slotUpdateClearButton())); KGuiItem clearItem(KStandardGuiItem::clear()); - KGuiItem::assign(d->ui->m_clearButton, clearItem); - d->ui->m_clearButton->setText(QString()); - d->ui->m_clearButton->setToolTip(clearItem.toolTip()); + KGuiItem::assign(m_clearButton, clearItem); + m_clearButton->setText(""); + m_clearButton->setToolTip(clearItem.toolTip()); } + KBudgetValues::~KBudgetValues() { - Q_D(KBudgetValues); - delete d; } bool KBudgetValues::eventFilter(QObject* o, QEvent* e) { - auto rc = false; + bool rc = false; if (o->isWidgetType() && (e->type() == QEvent::KeyPress)) { QKeyEvent* k = dynamic_cast(e); if ((k->modifiers() & Qt::KeyboardModifierMask) == 0 || (k->modifiers() & Qt::KeypadModifier) != 0) { QKeyEvent evt(e->type(), Qt::Key_Tab, k->modifiers(), QString(), k->isAutoRepeat(), k->count()); switch (k->key()) { case Qt::Key_Return: case Qt::Key_Enter: // send out a TAB key event QApplication::sendEvent(o, &evt); // don't process this one any further rc = true; break; default: break; } } } return rc; } void KBudgetValues::clear() { - Q_D(KBudgetValues); blockSignals(true); - for (auto i = 0; i < 12; ++i) - d->m_field[i]->setValue(MyMoneyMoney()); - d->ui->m_amountMonthly->setValue(MyMoneyMoney()); - d->ui->m_amountYearly->setValue(MyMoneyMoney()); + for (int i = 0; i < 12; ++i) + m_field[i]->setValue(MyMoneyMoney()); + m_amountMonthly->setValue(MyMoneyMoney()); + m_amountYearly->setValue(MyMoneyMoney()); blockSignals(false); } void KBudgetValues::slotClearAllValues() { - Q_D(KBudgetValues); - int tab = d->ui->m_periodGroup->checkedId(); - if (tab == d->ui->m_periodGroup->id(d->ui->m_monthlyButton)) { - d->ui->m_amountMonthly->setValue(MyMoneyMoney()); - } else if (tab == d->ui->m_periodGroup->id(d->ui->m_yearlyButton)) { - d->ui->m_amountYearly->setValue(MyMoneyMoney()); - } else if (tab == d->ui->m_periodGroup->id(d->ui->m_individualButton)) { - for (auto i = 0; i < 12; ++i) - d->m_field[i]->setValue(MyMoneyMoney()); + int tab = m_periodGroup->checkedId(); + if (tab == m_periodGroup->id(m_monthlyButton)) { + m_amountMonthly->setValue(MyMoneyMoney()); + } else if (tab == m_periodGroup->id(m_yearlyButton)) { + m_amountYearly->setValue(MyMoneyMoney()); + } else if (tab == m_periodGroup->id(m_individualButton)) { + for (int i = 0; i < 12; ++i) + m_field[i]->setValue(MyMoneyMoney()); } emit valuesChanged(); } void KBudgetValues::slotChangePeriod(int id) { - Q_D(KBudgetValues); // Prevent a recursive entry of this method due to widget changes // performed during execution of this method static bool inside = false; if (inside) return; inside = true; - QWidget *tab = d->ui->m_periodGroup->button(id); - d->fillMonthLabels(); + QWidget *tab = m_periodGroup->button(id); + fillMonthLabels(); MyMoneyMoney newValue; - if (tab == d->ui->m_monthlyButton) { - d->ui->m_firstItemStack->setCurrentIndex(d->ui->m_firstItemStack->indexOf(d->ui->m_monthlyPage)); - d->enableMonths(false); - d->m_label[0]->setText(" "); - if (d->ui->m_amountMonthly->value().isZero()) { - if (d->m_currentTab == d->ui->m_yearlyButton) { - newValue = (d->ui->m_amountYearly->value() / MyMoneyMoney(12, 1)).convert(); - - } else if (d->m_currentTab == d->ui->m_individualButton) { - for (auto i = 0; i < 12; ++i) - newValue += d->m_field[i]->value(); + if (tab == m_monthlyButton) { + m_firstItemStack->setCurrentIndex(m_firstItemStack->indexOf(m_monthlyPage)); + enableMonths(false); + m_label[0]->setText(" "); + if (m_amountMonthly->value().isZero()) { + if (m_currentTab == m_yearlyButton) { + newValue = (m_amountYearly->value() / MyMoneyMoney(12, 1)).convert(); + + } else if (m_currentTab == m_individualButton) { + for (int i = 0; i < 12; ++i) + newValue += m_field[i]->value(); newValue = (newValue / MyMoneyMoney(12, 1)).convert(); } if (!newValue.isZero()) { - if (KMessageBox::questionYesNo(this, QString("") + i18n("You have entered budget values using a different base which would result in a monthly budget of %1. Should this value be used to fill the monthly budget?", newValue.formatMoney(QString(), 2)) + QString(""), i18nc("Auto assignment (caption)", "Auto assignment"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "use_previous_budget_values") == KMessageBox::Yes) { - d->ui->m_amountMonthly->setValue(newValue); + if (KMessageBox::questionYesNo(this, QString("") + i18n("You have entered budget values using a different base which would result in a monthly budget of %1. Should this value be used to fill the monthly budget?", newValue.formatMoney("", 2)) + QString(""), i18nc("Auto assignment (caption)", "Auto assignment"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "use_previous_budget_values") == KMessageBox::Yes) { + m_amountMonthly->setValue(newValue); } } } - } else if (tab == d->ui->m_yearlyButton) { - d->ui->m_firstItemStack->setCurrentIndex(d->ui->m_firstItemStack->indexOf(d->ui->m_yearlyPage)); - d->enableMonths(false); - d->m_label[0]->setText(" "); - if (d->ui->m_amountYearly->value().isZero()) { - if (d->m_currentTab == d->ui->m_monthlyButton) { - newValue = (d->ui->m_amountMonthly->value() * MyMoneyMoney(12, 1)).convert(); - - } else if (d->m_currentTab == d->ui->m_individualButton) { - for (auto i = 0; i < 12; ++i) - newValue += d->m_field[i]->value(); + } else if (tab == m_yearlyButton) { + m_firstItemStack->setCurrentIndex(m_firstItemStack->indexOf(m_yearlyPage)); + enableMonths(false); + m_label[0]->setText(" "); + if (m_amountYearly->value().isZero()) { + if (m_currentTab == m_monthlyButton) { + newValue = (m_amountMonthly->value() * MyMoneyMoney(12, 1)).convert(); + + } else if (m_currentTab == m_individualButton) { + for (int i = 0; i < 12; ++i) + newValue += m_field[i]->value(); } if (!newValue.isZero()) { - if (KMessageBox::questionYesNo(this, QString("") + i18n("You have entered budget values using a different base which would result in a yearly budget of %1. Should this value be used to fill the monthly budget?", newValue.formatMoney(QString(), 2)) + QString(""), i18nc("Auto assignment (caption)", "Auto assignment"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "use_previous_budget_values") == KMessageBox::Yes) { - d->ui->m_amountYearly->setValue(newValue); + if (KMessageBox::questionYesNo(this, QString("") + i18n("You have entered budget values using a different base which would result in a yearly budget of %1. Should this value be used to fill the monthly budget?", newValue.formatMoney("", 2)) + QString(""), i18nc("Auto assignment (caption)", "Auto assignment"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "use_previous_budget_values") == KMessageBox::Yes) { + m_amountYearly->setValue(newValue); } } } - } else if (tab == d->ui->m_individualButton) { - d->ui->m_firstItemStack->setCurrentIndex(d->ui->m_firstItemStack->indexOf(d->ui->m_individualPage)); - d->enableMonths(true); - for (auto i = 0; i < 12; ++i) - newValue += d->m_field[i]->value(); + } else if (tab == m_individualButton) { + m_firstItemStack->setCurrentIndex(m_firstItemStack->indexOf(m_individualPage)); + enableMonths(true); + for (int i = 0; i < 12; ++i) + newValue += m_field[i]->value(); if (newValue.isZero()) { - if (d->m_currentTab == d->ui->m_monthlyButton) { - newValue = d->ui->m_amountMonthly->value(); - } else if (d->m_currentTab == d->ui->m_yearlyButton) { - newValue = (d->ui->m_amountYearly->value() / MyMoneyMoney(12, 1)).convert(); + if (m_currentTab == m_monthlyButton) { + newValue = m_amountMonthly->value(); + } else if (m_currentTab == m_yearlyButton) { + newValue = (m_amountYearly->value() / MyMoneyMoney(12, 1)).convert(); } if (!newValue.isZero()) { - if (KMessageBox::questionYesNo(this, QString("") + i18n("You have entered budget values using a different base which would result in an individual monthly budget of %1. Should this value be used to fill the monthly budgets?", newValue.formatMoney(QString(), 2)) + QString(""), i18nc("Auto assignment (caption)", "Auto assignment"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "use_previous_budget_values") == KMessageBox::Yes) { - for (auto i = 0; i < 12; ++i) - d->m_field[i]->setValue(newValue); + if (KMessageBox::questionYesNo(this, QString("") + i18n("You have entered budget values using a different base which would result in an individual monthly budget of %1. Should this value be used to fill the monthly budgets?", newValue.formatMoney("", 2)) + QString(""), i18nc("Auto assignment (caption)", "Auto assignment"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "use_previous_budget_values") == KMessageBox::Yes) { + for (int i = 0; i < 12; ++i) + m_field[i]->setValue(newValue); } } } } slotNeedUpdate(); - d->m_currentTab = tab; + m_currentTab = tab; inside = false; } void KBudgetValues::slotNeedUpdate() { if (!signalsBlocked()) QTimer::singleShot(0, this, SIGNAL(valuesChanged())); } +void KBudgetValues::enableMonths(bool enabled) +{ + for (int i = 1; i < 12; ++i) { + m_label[i]->setEnabled(enabled); + m_field[i]->setEnabled(enabled); + } +} + +void KBudgetValues::fillMonthLabels() +{ + QDate date(m_budgetDate); + for (int i = 0; i < 12; ++i) { + m_label[i]->setText(QLocale().standaloneMonthName(date.month(), QLocale::ShortFormat)); + date = date.addMonths(1); + } +} + void KBudgetValues::setBudgetValues(const MyMoneyBudget& budget, const MyMoneyBudget::AccountGroup& budgetAccount) { - Q_D(KBudgetValues); MyMoneyBudget::PeriodGroup period; - d->m_budgetDate = budget.budgetStart(); + m_budgetDate = budget.budgetStart(); QDate date; // make sure all values are zero so that slotChangePeriod() // doesn't check for anything. clear(); blockSignals(true); switch (budgetAccount.budgetLevel()) { case MyMoneyBudget::AccountGroup::eMonthly: default: - d->ui->m_monthlyButton->setChecked(true); - slotChangePeriod(d->ui->m_periodGroup->id(d->ui->m_monthlyButton)); - d->ui->m_amountMonthly->setValue(budgetAccount.period(d->m_budgetDate).amount()); + m_monthlyButton->setChecked(true); + slotChangePeriod(m_periodGroup->id(m_monthlyButton)); + m_amountMonthly->setValue(budgetAccount.period(m_budgetDate).amount()); break; case MyMoneyBudget::AccountGroup::eYearly: - d->ui->m_yearlyButton->setChecked(true); - slotChangePeriod(d->ui->m_periodGroup->id(d->ui->m_yearlyButton)); - d->ui->m_amountYearly->setValue(budgetAccount.period(d->m_budgetDate).amount()); + m_yearlyButton->setChecked(true); + slotChangePeriod(m_periodGroup->id(m_yearlyButton)); + m_amountYearly->setValue(budgetAccount.period(m_budgetDate).amount()); break; case MyMoneyBudget::AccountGroup::eMonthByMonth: - d->ui->m_individualButton->setChecked(true); - slotChangePeriod(d->ui->m_periodGroup->id(d->ui->m_individualButton)); - date.setDate(d->m_budgetDate.year(), d->m_budgetDate.month(), d->m_budgetDate.day()); - for (auto i = 0; i < 12; ++i) { - d->m_field[i]->setValue(budgetAccount.period(date).amount()); + m_individualButton->setChecked(true); + slotChangePeriod(m_periodGroup->id(m_individualButton)); + date.setDate(m_budgetDate.year(), m_budgetDate.month(), m_budgetDate.day()); + for (int i = 0; i < 12; ++i) { + m_field[i]->setValue(budgetAccount.period(date).amount()); date = date.addMonths(1); } break; } slotUpdateClearButton(); blockSignals(false); } void KBudgetValues::budgetValues(const MyMoneyBudget& budget, MyMoneyBudget::AccountGroup& budgetAccount) { - Q_D(KBudgetValues); MyMoneyBudget::PeriodGroup period; - d->m_budgetDate = budget.budgetStart(); - period.setStartDate(d->m_budgetDate); + m_budgetDate = budget.budgetStart(); + period.setStartDate(m_budgetDate); QDate date; budgetAccount.clearPeriods(); - int tab = d->ui->m_periodGroup->checkedId(); - if (tab == d->ui->m_periodGroup->id(d->ui->m_monthlyButton)) { + int tab = m_periodGroup->checkedId(); + if (tab == m_periodGroup->id(m_monthlyButton)) { budgetAccount.setBudgetLevel(MyMoneyBudget::AccountGroup::eMonthly); - period.setAmount(d->ui->m_amountMonthly->value()); - budgetAccount.addPeriod(d->m_budgetDate, period); - } else if (tab == d->ui->m_periodGroup->id(d->ui->m_yearlyButton)) { + period.setAmount(m_amountMonthly->value()); + budgetAccount.addPeriod(m_budgetDate, period); + } else if (tab == m_periodGroup->id(m_yearlyButton)) { budgetAccount.setBudgetLevel(MyMoneyBudget::AccountGroup::eYearly); - period.setAmount(d->ui->m_amountYearly->value()); - budgetAccount.addPeriod(d->m_budgetDate, period); - } else if (tab == d->ui->m_periodGroup->id(d->ui->m_individualButton)) { + period.setAmount(m_amountYearly->value()); + budgetAccount.addPeriod(m_budgetDate, period); + } else if (tab == m_periodGroup->id(m_individualButton)) { budgetAccount.setBudgetLevel(MyMoneyBudget::AccountGroup::eMonthByMonth); - date.setDate(d->m_budgetDate.year(), d->m_budgetDate.month(), d->m_budgetDate.day()); - for (auto i = 0; i < 12; ++i) { + date.setDate(m_budgetDate.year(), m_budgetDate.month(), m_budgetDate.day()); + for (int i = 0; i < 12; ++i) { period.setStartDate(date); - period.setAmount(d->m_field[i]->value()); + period.setAmount(m_field[i]->value()); budgetAccount.addPeriod(date, period); date = date.addMonths(1); } } } void KBudgetValues::slotUpdateClearButton() { - Q_D(KBudgetValues); - auto rc = false; - int tab = d->ui->m_periodGroup->checkedId(); - if (tab == d->ui->m_periodGroup->id(d->ui->m_monthlyButton)) { - rc = !d->ui->m_amountMonthly->value().isZero(); - } else if (tab == d->ui->m_periodGroup->id(d->ui->m_yearlyButton)) { - rc = !d->ui->m_amountYearly->value().isZero(); - } else if (tab == d->ui->m_periodGroup->id(d->ui->m_individualButton)) { - for (auto i = 0; (i < 12) && (rc == false); ++i) { - rc |= !d->m_field[i]->value().isZero(); + bool rc = false; + int tab = m_periodGroup->checkedId(); + if (tab == m_periodGroup->id(m_monthlyButton)) { + rc = !m_amountMonthly->value().isZero(); + } else if (tab == m_periodGroup->id(m_yearlyButton)) { + rc = !m_amountYearly->value().isZero(); + } else if (tab == m_periodGroup->id(m_individualButton)) { + for (int i = 0; (i < 12) && (rc == false); ++i) { + rc |= !m_field[i]->value().isZero(); } } - d->ui->m_clearButton->setEnabled(rc); + m_clearButton->setEnabled(rc); } diff --git a/kmymoney/widgets/kbudgetvalues.h b/kmymoney/widgets/kbudgetvalues.h index 18a315e52..3f8c039f0 100644 --- a/kmymoney/widgets/kbudgetvalues.h +++ b/kmymoney/widgets/kbudgetvalues.h @@ -1,79 +1,93 @@ /*************************************************************************** kbudgetvalues - description ------------------- begin : Wed Nov 28 2007 copyright : (C) 2007 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KBUDGETVALUES_H #define KBUDGETVALUES_H // ---------------------------------------------------------------------------- // QT Includes -#include +#include +class QLabel; // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "mymoneybudget.h" +#include "ui_kbudgetvaluesdecl.h" +#include +class kMyMoneyEdit; /** * @author Thomas Baumgart */ -class KBudgetValuesPrivate; -class KBudgetValues : public QWidget +class KBudgetValuesDecl : public QWidget, public Ui::KBudgetValuesDecl { - Q_OBJECT - Q_DISABLE_COPY(KBudgetValues) +public: + KBudgetValuesDecl(QWidget *parent) : QWidget(parent) { + setupUi(this); + } +}; +class KBudgetValues : public KBudgetValuesDecl +{ + Q_OBJECT public: - explicit KBudgetValues(QWidget* parent = nullptr); + KBudgetValues(QWidget* parent = 0); ~KBudgetValues(); void setBudgetValues(const MyMoneyBudget& budget, const MyMoneyBudget::AccountGroup& budgetAccount); void budgetValues(const MyMoneyBudget& budget, MyMoneyBudget::AccountGroup& budgetAccount); void clear(); -signals: - void valuesChanged(); +private: + void enableMonths(bool enabled); + void fillMonthLabels(); protected slots: void slotChangePeriod(int id); /** * This slot clears the value in the value widgets of the selected budget type. * Values of the other types are unaffected. */ void slotClearAllValues(); /** * Helper slot used to postpone sending the valuesChanged() signal. */ void slotNeedUpdate(); void slotUpdateClearButton(); protected: - bool eventFilter(QObject* o, QEvent* e) override; + bool eventFilter(QObject* o, QEvent* e); private: - KBudgetValuesPrivate * const d_ptr; - Q_DECLARE_PRIVATE(KBudgetValues) + kMyMoneyEdit* m_field[12]; + QLabel* m_label[12]; + QWidget* m_currentTab; + QDate m_budgetDate; + +signals: + void valuesChanged(); }; #endif diff --git a/kmymoney/widgets/kbudgetvalues.ui b/kmymoney/widgets/kbudgetvaluesdecl.ui similarity index 99% rename from kmymoney/widgets/kbudgetvalues.ui rename to kmymoney/widgets/kbudgetvaluesdecl.ui index 7a026f091..b954fdd5f 100644 --- a/kmymoney/widgets/kbudgetvalues.ui +++ b/kmymoney/widgets/kbudgetvaluesdecl.ui @@ -1,383 +1,383 @@ - KBudgetValues - + KBudgetValuesDecl + 0 0 228 111 0 Period Monthly m_periodGroup Yearly m_periodGroup Individual m_periodGroup Qt::Vertical QSizePolicy::Expanding 20 21 Qt::Vertical QSizePolicy::Expanding 20 21 xxx false 0 0 0 false 0 false 0 false xxx false false xxx false false xxx false false xxx false false xxx false false xxx false false xxx false false xxx false false xxx false false xxx false false xxx false false kMyMoneyEdit QWidget
kmymoneyedit.h
m_monthlyButton m_yearlyButton m_individualButton m_clearButton m_amount1 m_amountMonthly m_amountYearly m_amount2 m_amount3 m_amount4 m_amount5 m_amount6 m_amount7 m_amount8 m_amount9 m_amount10 m_amount11 m_amount12
diff --git a/kmymoney/widgets/kguiutils.cpp b/kmymoney/widgets/kguiutils.cpp index 004c45c0e..49d449c09 100644 --- a/kmymoney/widgets/kguiutils.cpp +++ b/kmymoney/widgets/kguiutils.cpp @@ -1,255 +1,254 @@ /*************************************************************************** kguiutils.cpp - description ------------------- begin : Fri Jan 27 2006 copyright : (C) 2006 Tony Bloomfield email : Tony Bloomfield ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kguiutils.h" // ---------------------------------------------------------------------------- // QT Includes // No need for QDateEdit, QSpinBox, etc., since these always return values #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include "kmymoneyedit.h" - +#include "kmymoneymvccombo.h" // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyglobalsettings.h" #include "onlinetasks/interfaces/ui/ionlinejobedit.h" #include "kmymoneytextedit.h" -#include "kmymoneypayeecombo.h" /************************************************************************** * * * The MandatoryFieldGroup code is courtesy of * * Mark Summerfield in Qt Quarterly * * http://doc.trolltech.com/qq/qq11-mandatoryfields.html * * * * Enhanced by Thomas Baumgart to support the lineedit field of a * * a KComboBox. * * * * With further widgets added by Allan Anderson for missing fields. * **************************************************************************/ void kMandatoryFieldGroup::add(QWidget *widget) { if (!m_widgets.contains(widget)) { if (qobject_cast(widget)) connect(qobject_cast(widget), SIGNAL(clicked()), this, SLOT(changed())); else if (qobject_cast(widget)) { KComboBox* combo = qobject_cast(widget); KLineEdit* lineedit = qobject_cast(combo->lineEdit()); if (lineedit) { connect(lineedit, SIGNAL(textChanged(QString)), this, SLOT(changed())); } else { connect(combo, SIGNAL(highlighted(int)), this, SLOT(changed())); } } else if (qobject_cast(widget)) { kMyMoneyEdit* amount = qobject_cast(widget); KLineEdit* lineedit = qobject_cast(amount->lineedit()); if (lineedit) { connect(lineedit, SIGNAL(textChanged(QString)), this, SLOT(changed())); } else { connect(amount, SIGNAL(highlighted(int)), this, SLOT(changed())); } } else if (qobject_cast(widget)) { connect(qobject_cast(widget), SIGNAL(textChanged(QString)), this, SLOT(changed())); } else if (qobject_cast(widget)) connect(qobject_cast(widget), SIGNAL(valueChanged(QString)), this, SLOT(changed())); else if (qobject_cast(widget)) connect(qobject_cast(widget), SIGNAL(itemSelectionChanged()), this, SLOT(changed())); else if (qobject_cast(widget)) connect(qobject_cast(widget), SIGNAL(textChanged(QString)), this, SLOT(changed())); else if (qobject_cast(widget)) connect(qobject_cast(widget), SIGNAL(textChanged()), this, SLOT(changed())); else if (qobject_cast(widget)) { connect(qobject_cast(widget), SIGNAL(validityChanged(bool)), this, SLOT(changed())); // Do not set palette for IonlineJobEdits as they contain subwidgets m_widgets.append(widget); changed(); return; } else { qWarning("MandatoryFieldGroup: unsupported class %s", (widget->metaObject()->className())); return; } QPalette palette = widget->palette(); palette.setColor(QPalette::Base, KMyMoneyGlobalSettings::schemeColor(SchemeColor::FieldRequired)); widget->setPalette(palette); m_widgets.append(widget); changed(); } } void kMandatoryFieldGroup::removeAll() { while(!m_widgets.isEmpty()) { remove(m_widgets.at(0)); } } void kMandatoryFieldGroup::remove(QWidget *widget) { widget->setPalette(QApplication::palette()); m_widgets.removeOne(widget); changed(); } void kMandatoryFieldGroup::setOkButton(QPushButton *button) { if (m_okButton && m_okButton != button) m_okButton->setEnabled(true); m_okButton = button; changed(); } void kMandatoryFieldGroup::changed() { bool enable = true; QList::ConstIterator i; for (i = m_widgets.constBegin(); i != m_widgets.constEnd(); ++i) { QWidget *widget = *i; // disabled widgets don't count if (!(widget->isEnabled())) { continue; } if (qobject_cast(widget)) { if ((dynamic_cast(widget))->lineEdit()->text().isEmpty()) { enable = false; break; } else continue; } if (qobject_cast(widget)) { if ((qobject_cast(widget))->checkState() == Qt::PartiallyChecked) { enable = false; break; } else continue; } if (qobject_cast(widget)) { if ((qobject_cast(widget))->currentText().isEmpty()) { enable = false; break; } else continue; } if (qobject_cast(widget)) { if ((qobject_cast(widget))->text().isEmpty()) { enable = false; break; } else continue; } if ((qobject_cast(widget))) { if ((qobject_cast(widget))->selectedItems().count() == 0) { enable = false; break; } else continue; } if ((qobject_cast(widget))) { if ((qobject_cast(widget))->text().isEmpty()) { enable = false; break; } else continue; } if ((qobject_cast(widget))) { if ((qobject_cast(widget))->text() == "0/1") { enable = false; break; } else continue; } if (qobject_cast(widget)) { if (!(qobject_cast(widget))->isValid()) { enable = false; break; } else { continue; } } if (qobject_cast(widget)) { if (!(qobject_cast(widget))->isValid()) { enable = false; break; } else { continue; } } } if (m_okButton) m_okButton->setEnabled(enable); m_enabled = enable; emit stateChanged(); emit stateChanged(enable); } void kMandatoryFieldGroup::clear() { QList::Iterator i; for (i = m_widgets.begin(); i != m_widgets.end(); ++i) (*i)->setPalette(QApplication::palette()); m_widgets.clear(); if (m_okButton) { m_okButton->setEnabled(true); m_okButton = 0; m_enabled = true; } } diff --git a/kmymoney/widgets/kmymoneyaccountcombo.cpp b/kmymoney/widgets/kmymoneyaccountcombo.cpp index 0fe3dbaea..666da24d2 100644 --- a/kmymoney/widgets/kmymoneyaccountcombo.cpp +++ b/kmymoney/widgets/kmymoneyaccountcombo.cpp @@ -1,355 +1,357 @@ /*************************************************************************** kmymoneyaccountbutton - description ------------------- begin : Mon May 31 2004 copyright : (C) 2000-2004 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ +#include + #include "kmymoneyaccountcombo.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyfile.h" #include "modelenums.h" class KMyMoneyAccountCombo::Private { public: Private(KMyMoneyAccountCombo* q) : m_q(q) , m_popupView(0) , m_inMakeCompletion(false) { m_q->setInsertPolicy(QComboBox::NoInsert); m_q->setMinimumWidth(m_q->fontMetrics().width(QLatin1Char('W')) * 15); m_q->setMaxVisibleItems(15); } KMyMoneyAccountCombo* m_q; QTreeView* m_popupView; QString m_lastSelectedAccount; bool m_inMakeCompletion; QString fullAccountName(const QAbstractItemModel* model, const QModelIndex& index, bool includeMainCategory = false) const; void selectFirstMatchingItem(); }; QString KMyMoneyAccountCombo::Private::fullAccountName(const QAbstractItemModel* model, const QModelIndex& _index, bool includeMainCategory) const { QString rc; if(_index.isValid()) { QModelIndex index = _index; QString sep; do { rc = QString("%1%2%3").arg(model->data(index).toString()).arg(sep).arg(rc); sep = QLatin1String(":"); index = index.parent(); } while(index.isValid()); if(!includeMainCategory) { QRegExp mainCategory(QString("[^%1]+%2(.*)").arg(sep).arg(sep)); if(mainCategory.exactMatch(rc)) { rc = mainCategory.cap(1); } } } return rc; } void KMyMoneyAccountCombo::Private::selectFirstMatchingItem() { if(m_popupView) { bool isBlocked = m_popupView->blockSignals(true); m_popupView->setCurrentIndex(QModelIndex()); - for (auto i = 0; i < m_q->model()->rowCount(QModelIndex()); ++i) { + for (int i = 0; i < m_q->model()->rowCount(QModelIndex()); ++i) { QModelIndex childIndex = m_q->model()->index(i, 0); if (m_q->model()->hasChildren(childIndex)) { // search the first leaf do { childIndex = m_q->model()->index(0, 0, childIndex); } while(m_q->model()->hasChildren(childIndex)); // make it the current selection if it's selectable if(m_q->model()->flags(childIndex) & Qt::ItemIsSelectable) { m_popupView->setCurrentIndex(childIndex); } break; } } m_popupView->blockSignals(isBlocked); } } KMyMoneyAccountCombo::KMyMoneyAccountCombo(QSortFilterProxyModel *model, QWidget *parent) : KComboBox(parent) , d(new Private(this)) { setModel(model); } KMyMoneyAccountCombo::KMyMoneyAccountCombo(QWidget *parent) : KComboBox(parent) , d(new Private(this)) { } KMyMoneyAccountCombo::~KMyMoneyAccountCombo() { } void KMyMoneyAccountCombo::setEditable(bool isEditable) { KComboBox::setEditable(isEditable); // don't do the standard behavior if(lineEdit()) { lineEdit()->setObjectName("AccountComboLineEdit"); connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(makeCompletion(QString))); } } void KMyMoneyAccountCombo::wheelEvent(QWheelEvent *ev) { Q_UNUSED(ev) // don't change anything with the help of the wheel, yet (due to the tree model) } void KMyMoneyAccountCombo::expandAll() { if (d->m_popupView) d->m_popupView->expandAll(); } void KMyMoneyAccountCombo::collapseAll() { if (d->m_popupView) d->m_popupView->collapseAll(); } void KMyMoneyAccountCombo::activated() { QVariant data = view()->currentIndex().data((int)eAccountsModel::Role::ID); if (data.isValid()) { setSelected(data.toString()); } } bool KMyMoneyAccountCombo::eventFilter(QObject* o, QEvent* e) { if(isEditable() && o == d->m_popupView) { // propagate all relevant key press events to the lineEdit widget if(e->type() == QEvent::KeyPress) { QKeyEvent* kev = static_cast(e); bool forLineEdit = (kev->text().length() > 0); switch(kev->key()) { case Qt::Key_Escape: case Qt::Key_Up: case Qt::Key_Down: forLineEdit = false; break; default: break; } if(forLineEdit) { return lineEdit()->event(e); } } else if(e->type() == QEvent::KeyRelease) { QKeyEvent* kev = static_cast(e); switch(kev->key()) { case Qt::Key_Enter: case Qt::Key_Return: activated(); hidePopup(); break; } } else if(e->type() == QEvent::FocusOut) { // if we tab out and have a selection in the popup view // than we use that entry completely activated(); hidePopup(); } } return KComboBox::eventFilter(o, e); } void KMyMoneyAccountCombo::setSelected(const QString& id) { // make sure, we have all items available for search if(isEditable()) { lineEdit()->clear(); } // find which item has this id and set is as the current item QModelIndexList list = model()->match(model()->index(0, 0), (int)eAccountsModel::Role::ID, QVariant(id), 1, Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap | Qt::MatchRecursive)); // CAUTION: Without Qt::MatchWrap no results for credit card, so nothing happens in ledger view if (!list.isEmpty()) { // make sure the popup is closed from here on hidePopup(); d->m_lastSelectedAccount = id; QModelIndex index = list.front(); if(isEditable()) { lineEdit()->setText(d->fullAccountName(model(), index)); } else { // ensure that combobox is properly set when KMyMoneyAccountCombo::setSelected is called programmatically blockSignals(true); setRootModelIndex(index.parent()); setCurrentIndex(index.row()); setRootModelIndex(QModelIndex()); blockSignals(false); } emit accountSelected(id); } } const QString& KMyMoneyAccountCombo::getSelected() const { return d->m_lastSelectedAccount; } void KMyMoneyAccountCombo::setModel(QSortFilterProxyModel *model) { delete d->m_popupView; KComboBox::setModel(model); model->setFilterKeyColumn((int)eAccountsModel::Column::Account); // CAUTION! Assumption is being made that Account column number is always 0 model->setFilterRole((int)eAccountsModel::Role::FullName); d->m_popupView = new QTreeView(this); d->m_popupView->setModel(model); d->m_popupView->setSelectionMode(QAbstractItemView::SingleSelection); setView(d->m_popupView); d->m_popupView->setHeaderHidden(true); d->m_popupView->setRootIsDecorated(true); d->m_popupView->setAlternatingRowColors(true); d->m_popupView->setAnimated(true); d->m_popupView->expandAll(); connect(d->m_popupView, SIGNAL(activated(QModelIndex)), this, SLOT(selectItem(QModelIndex))); if(isEditable()) { connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(makeCompletion(QString))); } else { connect(this, SIGNAL(activated(int)), SLOT(activated())); } } void KMyMoneyAccountCombo::selectItem(const QModelIndex& index) { if(index.isValid() && (model()->flags(index) & Qt::ItemIsSelectable)) { setSelected(model()->data(index, (int)eAccountsModel::Role::ID).toString()); } } void KMyMoneyAccountCombo::makeCompletion(const QString& txt) { if(!d->m_inMakeCompletion) { d->m_inMakeCompletion = true; AccountNamesFilterProxyModel* filterModel = qobject_cast(model()); if(filterModel) { const auto completionStr = QStringLiteral(".*"); if (txt.contains(MyMoneyFile::AccountSeperator) == 0) { // for some reason it helps to avoid internal errors if we // clear the filter before setting it to a new value filterModel->setFilterFixedString(QString()); const auto filterString = QString::fromLatin1("%1%2%3").arg(completionStr).arg(QRegExp::escape(txt)).arg(completionStr); filterModel->setFilterRegExp(QRegExp(filterString, Qt::CaseInsensitive)); } else { QStringList parts = txt.split(MyMoneyFile::AccountSeperator /*, QString::SkipEmptyParts */); QString pattern; QStringList::iterator it; for (it = parts.begin(); it != parts.end(); ++it) { if (pattern.length() > 1) pattern += MyMoneyFile::AccountSeperator; pattern += QRegExp::escape(QString(*it).trimmed()) + completionStr; } // for some reason it helps to avoid internal errors if we // clear the filter before setting it to a new value filterModel->setFilterFixedString(QString()); filterModel->setFilterRegExp(QRegExp(pattern, Qt::CaseInsensitive)); // if we don't have a match, we try it again, but this time // we add a wildcard for the top level if (filterModel->visibleItems() == 0) { // for some reason it helps to avoid internal errors if we // clear the filter before setting it to a new value pattern = pattern.prepend(completionStr + MyMoneyFile::AccountSeperator); filterModel->setFilterFixedString(QString()); filterModel->setFilterRegExp(QRegExp(pattern, Qt::CaseInsensitive)); } } // if nothing is shown, we might as well close the popup switch(filterModel->visibleItems()) { case 0: hidePopup(); break; default: setMaxVisibleItems(15); expandAll(); showPopup(); break; } d->selectFirstMatchingItem(); // keep current text in edit widget no matter what bool blocked = lineEdit()->signalsBlocked(); lineEdit()->blockSignals(true); lineEdit()->setText(txt); lineEdit()->blockSignals(blocked); } d->m_inMakeCompletion = false; } } void KMyMoneyAccountCombo::showPopup() { if(d->m_popupView) { d->m_popupView->show(); d->m_popupView->installEventFilter(this); } KComboBox::showPopup(); } void KMyMoneyAccountCombo::hidePopup() { if(d->m_popupView) { d->m_popupView->hide(); d->m_popupView->removeEventFilter(this); } KComboBox::hidePopup(); } diff --git a/kmymoney/widgets/kmymoneyaccountcombo.h b/kmymoney/widgets/kmymoneyaccountcombo.h index 035981c4d..a6e2a9cb2 100644 --- a/kmymoney/widgets/kmymoneyaccountcombo.h +++ b/kmymoney/widgets/kmymoneyaccountcombo.h @@ -1,165 +1,165 @@ /*************************************************************************** kmymoneyaccountcombo - description ------------------- begin : Mon May 31 2004 copyright : (C) 2000-2004 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYACCOUNTCOMBO_H #define KMYMONEYACCOUNTCOMBO_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "accountsproxymodel.h" #include "onlinebankingaccountsfilterproxymodel.h" /** * A proxy model used to filter all the data from the core accounts model leaving * only the name of the accounts so this model can be used in the account * completion combo. * * It shows only the first column (account name) and makes top level items non-selectable. * * @see AccountsModel * @see AccountsFilterProxyModel * * @author Cristian Onet 2010 * @author Christian David */ template class AccountNamesFilterProxyModelTpl : public baseProxyModel { public: AccountNamesFilterProxyModelTpl(QObject *parent = 0); virtual Qt::ItemFlags flags(const QModelIndex &index) const; protected: bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const; }; /** * @brief "typedef" for AccountNamesFilterProxyModelTpl * * To create valid Qt moc data this class inherits the template and uses Q_OBJECT. * * @code typedef AccountNamesFilterProxyModelTpl AccountNamesFilterProxyModel; * should work as well */ class AccountNamesFilterProxyModel : public AccountNamesFilterProxyModelTpl { Q_OBJECT public: AccountNamesFilterProxyModel(QObject* parent = 0) : AccountNamesFilterProxyModelTpl< AccountsProxyModel >(parent) {} }; /** * @brief OnlineBankingAccountFilterProxyModel showing only the name column * * Is equivalent to AccountNamesFilterProxyModel using OnlineBankingAccountFilterProxyModel as base. */ typedef AccountNamesFilterProxyModelTpl OnlineBankingAccountNamesFilterProxyModel; /** * * * @author Cristian Onet */ class KMyMoneyAccountCombo : public KComboBox { Q_OBJECT public: - explicit KMyMoneyAccountCombo(QSortFilterProxyModel *model, QWidget* parent = nullptr); - explicit KMyMoneyAccountCombo(QWidget* parent = nullptr); + explicit KMyMoneyAccountCombo(QSortFilterProxyModel *model, QWidget *parent = 0); + explicit KMyMoneyAccountCombo(QWidget *parent = 0); ~KMyMoneyAccountCombo(); void setSelected(const QString& id); const QString& getSelected() const; void setModel(QSortFilterProxyModel *model); /** * Overridden to get specific behavior */ void setEditable(bool isEditable); - virtual bool eventFilter(QObject* o, QEvent* e) override; + virtual bool eventFilter(QObject* o, QEvent* e); public slots: void expandAll(); void collapseAll(); virtual void showPopup(); virtual void hidePopup(); protected: virtual void wheelEvent(QWheelEvent *ev); protected slots: void activated(); void makeCompletion(const QString& txt); void selectItem(const QModelIndex& index); signals: void accountSelected(const QString&); private: class Private; QScopedPointer const d; }; template AccountNamesFilterProxyModelTpl::AccountNamesFilterProxyModelTpl(QObject *parent) : baseProxyModel(parent) { } /** * Top items are not selectable because they are not real accounts but are only used for grouping. */ template Qt::ItemFlags AccountNamesFilterProxyModelTpl::flags(const QModelIndex &index) const { if (!index.parent().isValid()) return baseProxyModel::flags(index) & ~Qt::ItemIsSelectable; return baseProxyModel::flags(index); } /** * Filter all but the first column. */ template bool AccountNamesFilterProxyModelTpl::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const { Q_UNUSED(source_parent) if (source_column == 0) return true; return false; } #endif diff --git a/kmymoney/widgets/kmymoneyaccountcompletion.h b/kmymoney/widgets/kmymoneyaccountcompletion.h index eacedee6d..0111affc8 100644 --- a/kmymoney/widgets/kmymoneyaccountcompletion.h +++ b/kmymoney/widgets/kmymoneyaccountcompletion.h @@ -1,66 +1,66 @@ /*************************************************************************** kmymoneyaccountcompletion.h - description ------------------- begin : Mon Apr 26 2004 copyright : (C) 2000-2004 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYACCOUNTCOMPLETION_H #define KMYMONEYACCOUNTCOMPLETION_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyaccountselector.h" #include "kmymoneycompletion.h" /** * @author Thomas Baumgart */ class kMyMoneyAccountCompletion : public kMyMoneyCompletion { Q_OBJECT public: - kMyMoneyAccountCompletion(QWidget* parent = nullptr); + kMyMoneyAccountCompletion(QWidget *parent = 0); virtual ~kMyMoneyAccountCompletion(); QStringList accountList(const QList& list = QList()) const { return selector()->accountList(list); } /** * reimplemented from kMyMoneyCompletion */ kMyMoneyAccountSelector* selector() const { return dynamic_cast(m_selector); } public slots: void slotMakeCompletion(const QString& txt); }; #endif diff --git a/kmymoney/widgets/kmymoneyaccountselector.cpp b/kmymoney/widgets/kmymoneyaccountselector.cpp index 665329cbd..bbb792146 100644 --- a/kmymoney/widgets/kmymoneyaccountselector.cpp +++ b/kmymoney/widgets/kmymoneyaccountselector.cpp @@ -1,583 +1,487 @@ /*************************************************************************** kmymoneyaccountselector.cpp - description ------------------- begin : Thu Sep 18 2003 copyright : (C) 2003 by Thomas Baumgart email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kmymoneyaccountselector.h" -#include "kmymoneyselector_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyfile.h" #include "mymoneyaccount.h" +#include "kmymoneyutils.h" #include "kmymoneyglobalsettings.h" #include "icons/icons.h" #include "mymoneyenums.h" #include "dialogenums.h" using namespace Icons; using namespace eMyMoney; -class kMyMoneyAccountSelectorPrivate : public KMyMoneySelectorPrivate -{ - Q_DISABLE_COPY(kMyMoneyAccountSelectorPrivate) - -public: - kMyMoneyAccountSelectorPrivate(kMyMoneyAccountSelector *qq) : - KMyMoneySelectorPrivate(qq), +kMyMoneyAccountSelector::kMyMoneyAccountSelector(QWidget *parent, Qt::WindowFlags flags, const bool createButtons) : + KMyMoneySelector(parent, flags), m_allAccountsButton(0), m_noAccountButton(0), m_incomeCategoriesButton(0), m_expenseCategoriesButton(0) - { - } - - QPushButton* m_allAccountsButton; - QPushButton* m_noAccountButton; - QPushButton* m_incomeCategoriesButton; - QPushButton* m_expenseCategoriesButton; - QList m_typeList; - QStringList m_accountList; -}; - -kMyMoneyAccountSelector::kMyMoneyAccountSelector(QWidget *parent, Qt::WindowFlags flags, const bool createButtons) : - KMyMoneySelector(*new kMyMoneyAccountSelectorPrivate(this), parent, flags) { - Q_D(kMyMoneyAccountSelector); + if (createButtons) { QVBoxLayout* buttonLayout = new QVBoxLayout(); buttonLayout->setSpacing(6); - d->m_allAccountsButton = new QPushButton(this); - d->m_allAccountsButton->setObjectName("m_allAccountsButton"); - d->m_allAccountsButton->setText(i18nc("Select all accounts", "All")); - buttonLayout->addWidget(d->m_allAccountsButton); + m_allAccountsButton = new QPushButton(this); + m_allAccountsButton->setObjectName("m_allAccountsButton"); + m_allAccountsButton->setText(i18nc("Select all accounts", "All")); + buttonLayout->addWidget(m_allAccountsButton); - d->m_incomeCategoriesButton = new QPushButton(this); - d->m_incomeCategoriesButton->setObjectName("m_incomeCategoriesButton"); - d->m_incomeCategoriesButton->setText(i18n("Income")); - buttonLayout->addWidget(d->m_incomeCategoriesButton); + m_incomeCategoriesButton = new QPushButton(this); + m_incomeCategoriesButton->setObjectName("m_incomeCategoriesButton"); + m_incomeCategoriesButton->setText(i18n("Income")); + buttonLayout->addWidget(m_incomeCategoriesButton); - d->m_expenseCategoriesButton = new QPushButton(this); - d->m_expenseCategoriesButton->setObjectName("m_expenseCategoriesButton"); - d->m_expenseCategoriesButton->setText(i18n("Expense")); - buttonLayout->addWidget(d->m_expenseCategoriesButton); + m_expenseCategoriesButton = new QPushButton(this); + m_expenseCategoriesButton->setObjectName("m_expenseCategoriesButton"); + m_expenseCategoriesButton->setText(i18n("Expense")); + buttonLayout->addWidget(m_expenseCategoriesButton); - d->m_noAccountButton = new QPushButton(this); - d->m_noAccountButton->setObjectName("m_noAccountButton"); - d->m_noAccountButton->setText(i18nc("No account", "None")); - buttonLayout->addWidget(d->m_noAccountButton); + m_noAccountButton = new QPushButton(this); + m_noAccountButton->setObjectName("m_noAccountButton"); + m_noAccountButton->setText(i18nc("No account", "None")); + buttonLayout->addWidget(m_noAccountButton); QSpacerItem* spacer = new QSpacerItem(0, 67, QSizePolicy::Minimum, QSizePolicy::Expanding); buttonLayout->addItem(spacer); - d->m_layout->addLayout(buttonLayout); + m_layout->addLayout(buttonLayout); - connect(d->m_allAccountsButton, &QAbstractButton::clicked, this, &kMyMoneyAccountSelector::slotSelectAllAccounts); - connect(d->m_noAccountButton, &QAbstractButton::clicked, this, &kMyMoneyAccountSelector::slotDeselectAllAccounts); - connect(d->m_incomeCategoriesButton, &QAbstractButton::clicked, this, &kMyMoneyAccountSelector::slotSelectIncomeCategories); - connect(d->m_expenseCategoriesButton, &QAbstractButton::clicked, this, &kMyMoneyAccountSelector::slotSelectExpenseCategories); + connect(m_allAccountsButton, SIGNAL(clicked()), this, SLOT(slotSelectAllAccounts())); + connect(m_noAccountButton, SIGNAL(clicked()), this, SLOT(slotDeselectAllAccounts())); + connect(m_incomeCategoriesButton, SIGNAL(clicked()), this, SLOT(slotSelectIncomeCategories())); + connect(m_expenseCategoriesButton, SIGNAL(clicked()), this, SLOT(slotSelectExpenseCategories())); } } kMyMoneyAccountSelector::~kMyMoneyAccountSelector() { } void kMyMoneyAccountSelector::removeButtons() { - Q_D(kMyMoneyAccountSelector); - delete d->m_allAccountsButton; - delete d->m_incomeCategoriesButton; - delete d->m_expenseCategoriesButton; - delete d->m_noAccountButton; -} - -void kMyMoneyAccountSelector::slotSelectAllAccounts() -{ - selectAllItems(true); -} - -void kMyMoneyAccountSelector::slotDeselectAllAccounts() -{ - selectAllItems(false); + delete m_allAccountsButton; + delete m_incomeCategoriesButton; + delete m_expenseCategoriesButton; + delete m_noAccountButton; } void kMyMoneyAccountSelector::selectCategories(const bool income, const bool expense) { - Q_D(kMyMoneyAccountSelector); - QTreeWidgetItemIterator it_v(d->m_treeWidget); + QTreeWidgetItemIterator it_v(m_treeWidget); for (; *it_v != 0; ++it_v) { if ((*it_v)->text(0) == i18n("Income categories")) selectAllSubItems(*it_v, income); else if ((*it_v)->text(0) == i18n("Expense categories")) selectAllSubItems(*it_v, expense); } emit stateChanged(); } -void kMyMoneyAccountSelector::slotSelectIncomeCategories() -{ - selectCategories(true, false); -} - -void kMyMoneyAccountSelector::slotSelectExpenseCategories() -{ - selectCategories(false, true); -} - void kMyMoneyAccountSelector::setSelectionMode(QTreeWidget::SelectionMode mode) { - Q_D(kMyMoneyAccountSelector); - d->m_incomeCategoriesButton->setHidden(mode == QTreeWidget::MultiSelection); - d->m_expenseCategoriesButton->setHidden(mode == QTreeWidget::MultiSelection); + m_incomeCategoriesButton->setHidden(mode == QTreeWidget::MultiSelection); + m_expenseCategoriesButton->setHidden(mode == QTreeWidget::MultiSelection); KMyMoneySelector::setSelectionMode(mode); } QStringList kMyMoneyAccountSelector::accountList(const QList& filterList) const { - Q_D(const kMyMoneyAccountSelector); QStringList list; - QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); + QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); while (*it) { QVariant id = (*it)->data(0, KMyMoneySelector::IdRole); MyMoneyAccount acc = MyMoneyFile::instance()->account(id.toString()); if (filterList.count() == 0 || filterList.contains(acc.accountType())) list << id.toString(); it++; } return list; } -QStringList kMyMoneyAccountSelector::accountList() const -{ - return accountList(QList()); -} - bool kMyMoneyAccountSelector::match(const QRegExp& exp, QTreeWidgetItem* item) const { if (!item->flags().testFlag(Qt::ItemIsSelectable)) return false; return exp.indexIn(item->data(0, KMyMoneySelector::KeyRole).toString().mid(1)) != -1; } bool kMyMoneyAccountSelector::contains(const QString& txt) const { - Q_D(const kMyMoneyAccountSelector); - QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); + QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; QString baseName = i18n("Asset") + '|' + i18n("Liability") + '|' + i18n("Income") + '|' + i18n("Expense") + '|' + i18n("Equity") + '|' + i18n("Security"); while ((it_v = *it) != 0) { QRegExp exp(QString("^(?:%1):%2$").arg(baseName).arg(QRegExp::escape(txt))); if (exp.indexIn(it_v->data(0, KMyMoneySelector::KeyRole).toString().mid(1)) != -1) { return true; } it++; } return false; } -class AccountSetPrivate -{ - Q_DISABLE_COPY(AccountSetPrivate) - -public: - AccountSetPrivate() : +AccountSet::AccountSet() : m_count(0), m_file(MyMoneyFile::instance()), m_favorites(0), m_hideClosedAccounts(true) - { - } - - int m_count; - MyMoneyFile* m_file; - QList m_typeList; - QTreeWidgetItem* m_favorites; - bool m_hideClosedAccounts; -}; - -AccountSet::AccountSet() : - d_ptr(new AccountSetPrivate) -{ -} - -AccountSet::~AccountSet() { - Q_D(AccountSet); - delete d; } void AccountSet::addAccountGroup(Account group) { - Q_D(AccountSet); if (group == Account::Asset) { - d->m_typeList << Account::Checkings; - d->m_typeList << Account::Savings; - d->m_typeList << Account::Cash; - d->m_typeList << Account::AssetLoan; - d->m_typeList << Account::CertificateDep; - d->m_typeList << Account::Investment; - d->m_typeList << Account::Stock; - d->m_typeList << Account::MoneyMarket; - d->m_typeList << Account::Asset; - d->m_typeList << Account::Currency; + m_typeList << Account::Checkings; + m_typeList << Account::Savings; + m_typeList << Account::Cash; + m_typeList << Account::AssetLoan; + m_typeList << Account::CertificateDep; + m_typeList << Account::Investment; + m_typeList << Account::Stock; + m_typeList << Account::MoneyMarket; + m_typeList << Account::Asset; + m_typeList << Account::Currency; } else if (group == Account::Liability) { - d->m_typeList << Account::CreditCard; - d->m_typeList << Account::Loan; - d->m_typeList << Account::Liability; + m_typeList << Account::CreditCard; + m_typeList << Account::Loan; + m_typeList << Account::Liability; } else if (group == Account::Income) { - d->m_typeList << Account::Income; + m_typeList << Account::Income; } else if (group == Account::Expense) { - d->m_typeList << Account::Expense; + m_typeList << Account::Expense; } else if (group == Account::Equity) { - d->m_typeList << Account::Equity; + m_typeList << Account::Equity; } } void AccountSet::addAccountType(Account type) { - Q_D(AccountSet); - d->m_typeList << type; + m_typeList << type; } void AccountSet::removeAccountType(Account type) { - Q_D(AccountSet); - int index = d->m_typeList.indexOf(type); + int index = m_typeList.indexOf(type); if (index != -1) { - d->m_typeList.removeAt(index); + m_typeList.removeAt(index); } } void AccountSet::clear() { - Q_D(AccountSet); - d->m_typeList.clear(); + m_typeList.clear(); } int AccountSet::load(kMyMoneyAccountSelector* selector) { - Q_D(AccountSet); QStringList list; QStringList::ConstIterator it_l; int count = 0; int typeMask = 0; QString currentId; if (selector->selectionMode() == QTreeWidget::SingleSelection) { QStringList list; selector->selectedItems(list); if (!list.isEmpty()) currentId = list.first(); } - if (d->m_typeList.contains(Account::Checkings) - || d->m_typeList.contains(Account::Savings) - || d->m_typeList.contains(Account::Cash) - || d->m_typeList.contains(Account::AssetLoan) - || d->m_typeList.contains(Account::CertificateDep) - || d->m_typeList.contains(Account::Investment) - || d->m_typeList.contains(Account::Stock) - || d->m_typeList.contains(Account::MoneyMarket) - || d->m_typeList.contains(Account::Asset) - || d->m_typeList.contains(Account::Currency)) + if (m_typeList.contains(Account::Checkings) + || m_typeList.contains(Account::Savings) + || m_typeList.contains(Account::Cash) + || m_typeList.contains(Account::AssetLoan) + || m_typeList.contains(Account::CertificateDep) + || m_typeList.contains(Account::Investment) + || m_typeList.contains(Account::Stock) + || m_typeList.contains(Account::MoneyMarket) + || m_typeList.contains(Account::Asset) + || m_typeList.contains(Account::Currency)) typeMask |= eDialogs::Category::asset; - if (d->m_typeList.contains(Account::CreditCard) - || d->m_typeList.contains(Account::Loan) - || d->m_typeList.contains(Account::Liability)) + if (m_typeList.contains(Account::CreditCard) + || m_typeList.contains(Account::Loan) + || m_typeList.contains(Account::Liability)) typeMask |= eDialogs::Category::liability; - if (d->m_typeList.contains(Account::Income)) + if (m_typeList.contains(Account::Income)) typeMask |= eDialogs::Category::income; - if (d->m_typeList.contains(Account::Expense)) + if (m_typeList.contains(Account::Expense)) typeMask |= eDialogs::Category::expense; - if (d->m_typeList.contains(Account::Equity)) + if (m_typeList.contains(Account::Equity)) typeMask |= eDialogs::Category::equity; selector->clear(); QTreeWidget* lv = selector->listView(); - d->m_count = 0; + m_count = 0; QString key; QTreeWidgetItem* after = 0; // create the favorite section first and sort it to the beginning key = QString("A%1").arg(i18n("Favorites")); - d->m_favorites = selector->newItem(i18n("Favorites"), key); + m_favorites = selector->newItem(i18n("Favorites"), key); //get the account icon from cache or insert it if it is not there QPixmap accountPixmap; if (!QPixmapCache::find("account", accountPixmap)) { QIcon icon = QIcon::fromTheme(g_Icons[Icon::ViewBankAccount]); if (!icon.availableSizes().isEmpty()) accountPixmap = icon.pixmap(icon.availableSizes().first()); QPixmapCache::insert("account", accountPixmap); } - d->m_favorites->setIcon(0, QIcon(accountPixmap)); + m_favorites->setIcon(0, QIcon(accountPixmap)); for (auto mask = 0x01; mask != eDialogs::Category::last; mask <<= 1) { QTreeWidgetItem* item = 0; if ((typeMask & mask & eDialogs::Category::asset) != 0) { - ++d->m_count; + ++m_count; key = QString("B%1").arg(i18n("Asset")); item = selector->newItem(i18n("Asset accounts"), key); - item->setIcon(0, d->m_file->asset().accountPixmap()); - list = d->m_file->asset().accountList(); + item->setIcon(0, m_file->asset().accountPixmap()); + list = m_file->asset().accountList(); } if ((typeMask & mask & eDialogs::Category::liability) != 0) { - ++d->m_count; + ++m_count; key = QString("C%1").arg(i18n("Liability")); item = selector->newItem(i18n("Liability accounts"), key); - item->setIcon(0, d->m_file->liability().accountPixmap()); - list = d->m_file->liability().accountList(); + item->setIcon(0, m_file->liability().accountPixmap()); + list = m_file->liability().accountList(); } if ((typeMask & mask & eDialogs::Category::income) != 0) { - ++d->m_count; + ++m_count; key = QString("D%1").arg(i18n("Income")); item = selector->newItem(i18n("Income categories"), key); - item->setIcon(0, d->m_file->income().accountPixmap()); - list = d->m_file->income().accountList(); + item->setIcon(0, m_file->income().accountPixmap()); + list = m_file->income().accountList(); if (selector->selectionMode() == QTreeWidget::MultiSelection) { - selector->d_func()->m_incomeCategoriesButton->show(); + selector->m_incomeCategoriesButton->show(); } } if ((typeMask & mask & eDialogs::Category::expense) != 0) { - ++d->m_count; + ++m_count; key = QString("E%1").arg(i18n("Expense")); item = selector->newItem(i18n("Expense categories"), key); - item->setIcon(0, d->m_file->expense().accountPixmap()); - list = d->m_file->expense().accountList(); + item->setIcon(0, m_file->expense().accountPixmap()); + list = m_file->expense().accountList(); if (selector->selectionMode() == QTreeWidget::MultiSelection) { - selector->d_func()->m_expenseCategoriesButton->show(); + selector->m_expenseCategoriesButton->show(); } } if ((typeMask & mask & eDialogs::Category::equity) != 0) { - ++d->m_count; + ++m_count; key = QString("F%1").arg(i18n("Equity")); item = selector->newItem(i18n("Equity accounts"), key); - item->setIcon(0, d->m_file->equity().accountPixmap()); - list = d->m_file->equity().accountList(); + item->setIcon(0, m_file->equity().accountPixmap()); + list = m_file->equity().accountList(); } if (!after) after = item; if (item != 0) { // scan all matching accounts found in the engine for (it_l = list.constBegin(); it_l != list.constEnd(); ++it_l) { - const MyMoneyAccount& acc = d->m_file->account(*it_l); - ++d->m_count; + const MyMoneyAccount& acc = m_file->account(*it_l); + ++m_count; ++count; //this will include an account if it matches the account type and //if it is still open or it has been set to show closed accounts if (includeAccount(acc) && (!isHidingClosedAccounts() || !acc.isClosed())) { QString tmpKey; tmpKey = key + MyMoneyFile::AccountSeperator + acc.name(); QTreeWidgetItem* subItem = selector->newItem(item, acc.name(), tmpKey, acc.id()); subItem->setIcon(0, acc.accountPixmap()); if (acc.value("PreferredAccount") == "Yes" - && d->m_typeList.contains(acc.accountType())) { - selector->newItem(d->m_favorites, acc.name(), tmpKey, acc.id())->setIcon(0, acc.accountPixmap());; + && m_typeList.contains(acc.accountType())) { + selector->newItem(m_favorites, acc.name(), tmpKey, acc.id())->setIcon(0, acc.accountPixmap());; } if (acc.accountList().count() > 0) { subItem->setExpanded(true); count += loadSubAccounts(selector, subItem, tmpKey, acc.accountList()); } // the item is not selectable if it has been added only because a subaccount matches the type - if (!d->m_typeList.contains(acc.accountType())) { + if (!m_typeList.contains(acc.accountType())) { selector->setSelectable(subItem, false); } subItem->sortChildren(1, Qt::AscendingOrder); } } item->sortChildren(1, Qt::AscendingOrder); } } - d->m_favorites->sortChildren(1, Qt::AscendingOrder); + m_favorites->sortChildren(1, Qt::AscendingOrder); lv->invisibleRootItem()->sortChildren(1, Qt::AscendingOrder); // if we don't have a favorite account or the selector is for multi-mode // we get rid of the favorite entry and subentries. - if (d->m_favorites->childCount() == 0 || selector->selectionMode() == QTreeWidget::MultiSelection) { - delete d->m_favorites; - d->m_favorites = 0; + if (m_favorites->childCount() == 0 || selector->selectionMode() == QTreeWidget::MultiSelection) { + delete m_favorites; + m_favorites = 0; } if (lv->itemAt(0, 0)) { if (currentId.isEmpty()) { lv->setCurrentItem(lv->itemAt(0, 0)); lv->clearSelection(); } else { selector->setSelected(currentId); } } selector->update(); return count; } int AccountSet::load(kMyMoneyAccountSelector* selector, const QString& baseName, const QList& accountIdList, const bool clear) { - Q_D(AccountSet); int count = 0; QTreeWidgetItem* item = 0; - d->m_typeList.clear(); + m_typeList.clear(); if (clear) { - d->m_count = 0; + m_count = 0; selector->clear(); } item = selector->newItem(baseName); - ++d->m_count; + ++m_count; QList::ConstIterator it; for (it = accountIdList.constBegin(); it != accountIdList.constEnd(); ++it) { - const MyMoneyAccount& acc = d->m_file->account(*it); + const MyMoneyAccount& acc = m_file->account(*it); if (acc.isClosed()) continue; QString tmpKey; // the first character must be preset. Since we don't know any sort order here, we just use A tmpKey = QString("A%1%2%3").arg(baseName, MyMoneyFile::AccountSeperator, acc.name()); selector->newItem(item, acc.name(), tmpKey, acc.id())->setIcon(0, acc.accountPixmap()); - ++d->m_count; + ++m_count; ++count; } QTreeWidget* lv = selector->listView(); if (lv->itemAt(0, 0)) { lv->setCurrentItem(lv->itemAt(0, 0)); lv->clearSelection(); } selector->update(); return count; } -int AccountSet::count() const -{ - Q_D(const AccountSet); - return d->m_count; -} - -void AccountSet::setHideClosedAccounts(bool _bool) -{ - Q_D(AccountSet); - d->m_hideClosedAccounts = _bool; -} -bool AccountSet::isHidingClosedAccounts() const -{ - Q_D(const AccountSet); - return d->m_hideClosedAccounts; -} - int AccountSet::loadSubAccounts(kMyMoneyAccountSelector* selector, QTreeWidgetItem* parent, const QString& key, const QStringList& list) { - Q_D(AccountSet); QStringList::ConstIterator it_l; int count = 0; for (it_l = list.constBegin(); it_l != list.constEnd(); ++it_l) { - const MyMoneyAccount& acc = d->m_file->account(*it_l); + const MyMoneyAccount& acc = m_file->account(*it_l); // don't include stock accounts if not in expert mode if (acc.isInvest() && !KMyMoneyGlobalSettings::expertMode()) continue; //this will include an account if it matches the account type and //if it is still open or it has been set to show closed accounts if (includeAccount(acc) && (!isHidingClosedAccounts() || !acc.isClosed())) { QString tmpKey; tmpKey = key + MyMoneyFile::AccountSeperator + acc.name(); ++count; - ++d->m_count; + ++m_count; QTreeWidgetItem* item = selector->newItem(parent, acc.name(), tmpKey, acc.id()); item->setIcon(0, acc.accountPixmap()); if (acc.value("PreferredAccount") == "Yes" - && d->m_typeList.contains(acc.accountType())) { - selector->newItem(d->m_favorites, acc.name(), tmpKey, acc.id())->setIcon(0, acc.accountPixmap()); + && m_typeList.contains(acc.accountType())) { + selector->newItem(m_favorites, acc.name(), tmpKey, acc.id())->setIcon(0, acc.accountPixmap()); } if (acc.accountList().count() > 0) { item->setExpanded(true); count += loadSubAccounts(selector, item, tmpKey, acc.accountList()); } // the item is not selectable if it has been added only because a subaccount matches the type - if (!d->m_typeList.contains(acc.accountType())) { + if (!m_typeList.contains(acc.accountType())) { selector->setSelectable(item, false); } item->sortChildren(1, Qt::AscendingOrder); } } return count; } bool AccountSet::includeAccount(const MyMoneyAccount& acc) { - Q_D(AccountSet); - if (d->m_typeList.contains(acc.accountType())) + if (m_typeList.contains(acc.accountType())) return true; QStringList accounts = acc.accountList(); if (accounts.size() > 0) { QStringList::ConstIterator it_acc; for (it_acc = accounts.constBegin(); it_acc != accounts.constEnd(); ++it_acc) { - MyMoneyAccount account = d->m_file->account(*it_acc); + MyMoneyAccount account = m_file->account(*it_acc); if (includeAccount(account)) return true; } } return false; } diff --git a/kmymoney/widgets/kmymoneyaccountselector.h b/kmymoney/widgets/kmymoneyaccountselector.h index b36ce3bc3..9f7a7feaf 100644 --- a/kmymoney/widgets/kmymoneyaccountselector.h +++ b/kmymoney/widgets/kmymoneyaccountselector.h @@ -1,188 +1,202 @@ /*************************************************************************** kmymoneyaccountselector.h ------------------- begin : Thu Sep 18 2003 copyright : (C) 2003 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYACCOUNTSELECTOR_H #define KMYMONEYACCOUNTSELECTOR_H // ---------------------------------------------------------------------------- // QT Includes +#include + // ---------------------------------------------------------------------------- // KDE Includes +class QPushButton; + // ---------------------------------------------------------------------------- // Project Includes -#include "kmymoneyselector.h" +#include +#include "mymoneyenums.h" +class MyMoneyFile; class MyMoneyAccount; -template class QList; - -namespace eMyMoney { enum class Account; } - /** * This class implements an account/category selector. It is based * on a tree view. Using this widget, one can select one or multiple * accounts depending on the mode of operation and the set of accounts * selected to be displayed. (see setSelectionMode() * and loadList() about the specifics of configuration). * * - Single selection mode\n * In this mode the widget allows to select a single entry out of * the set of displayed accounts. * * - Multi selection mode\n * In this mode, the widget allows to select one or more entries * out of the set of displayed accounts. Selection is performed * by marking the account in the view. */ -class kMyMoneyAccountSelectorPrivate; class kMyMoneyAccountSelector : public KMyMoneySelector { Q_OBJECT - Q_DISABLE_COPY(kMyMoneyAccountSelector) - public: friend class AccountSet; - explicit kMyMoneyAccountSelector(QWidget* parent = nullptr, Qt::WindowFlags flags = 0, const bool createButtons = true); - ~kMyMoneyAccountSelector() override; + explicit kMyMoneyAccountSelector(QWidget *parent = 0, Qt::WindowFlags flags = 0, const bool createButtons = true); + virtual ~kMyMoneyAccountSelector(); /** * This method returns a list of account ids of those accounts * currently loaded into the widget. It is possible to select * a list of specific account types only. In this case, pass * a list of account types as parameter @p list. * * @param list QList of account types to be returned. If this * list is empty (the default), then the ids of all accounts * will be returned. * @return QStringList of account ids */ - QStringList accountList(const QList& list) const; - QStringList accountList() const; + QStringList accountList(const QList& list = QList()) const; void setSelectionMode(QTreeWidget::SelectionMode mode); /** * This method checks if a given @a item matches the given regular expression @a exp. * * @param exp const reference to a regular expression object * @param item pointer to QListViewItem * * @retval true item matches * @retval false item does not match */ virtual bool match(const QRegExp& exp, QTreeWidgetItem* item) const; /** * This method returns, if any of the items in the selector contains * the text @a txt. * * @param txt const reference to string to be looked for * @retval true exact match found * @retval false no match found */ virtual bool contains(const QString& txt) const; /** * This method removes all the buttons of the widget */ void removeButtons(); public slots: /** * This slot selects all items that are currently in * the account list of the widget. */ - void slotSelectAllAccounts(); + void slotSelectAllAccounts() { + selectAllItems(true); + }; /** * This slot deselects all items that are currently in * the account list of the widget. */ - void slotDeselectAllAccounts(); + void slotDeselectAllAccounts() { + selectAllItems(false); + }; protected: /** * This method loads the list of subaccounts as found in the * @p list and attaches them to the parent widget passed as @p parent. * * @param parent pointer to parent widget * @param list QStringList containing the ids of all subaccounts to load * @return This method returns the number of accounts loaded into the list */ int loadSubAccounts(QTreeWidgetItem* parent, const QStringList& list); /** * This is a helper method for selectAllIncomeCategories() * and selectAllExpenseCategories(). */ void selectCategories(const bool income, const bool expense); protected slots: /** * This slot selects all income categories */ - void slotSelectIncomeCategories(); + void slotSelectIncomeCategories() { + selectCategories(true, false); + }; /** * This slot selects all expense categories */ - void slotSelectExpenseCategories(); + void slotSelectExpenseCategories() { + selectCategories(false, true); + }; -private: - Q_DECLARE_PRIVATE(kMyMoneyAccountSelector) +protected: + QPushButton* m_allAccountsButton; + QPushButton* m_noAccountButton; + QPushButton* m_incomeCategoriesButton; + QPushButton* m_expenseCategoriesButton; + QList m_typeList; + QStringList m_accountList; }; -class AccountSetPrivate; + class AccountSet { - Q_DISABLE_COPY(AccountSet) - public: AccountSet(); - AccountSet(AccountSet && other); - friend void swap(AccountSet& first, AccountSet& second); - ~AccountSet(); void addAccountType(eMyMoney::Account type); void addAccountGroup(eMyMoney::Account type); void removeAccountType(eMyMoney::Account type); void clear(); int load(kMyMoneyAccountSelector* selector); int load(kMyMoneyAccountSelector* selector, const QString& baseName, const QList& accountIdList, const bool clear = false); - int count() const; + int count() const { + return m_count; + } - void setHideClosedAccounts(bool _bool); - bool isHidingClosedAccounts() const; + void setHideClosedAccounts(bool _bool) { + m_hideClosedAccounts = _bool; + } + bool isHidingClosedAccounts() const { + return m_hideClosedAccounts; + } protected: int loadSubAccounts(kMyMoneyAccountSelector* selector, QTreeWidgetItem* parent, const QString& key, const QStringList& list); bool includeAccount(const MyMoneyAccount& acc); private: - AccountSetPrivate * const d_ptr; - Q_DECLARE_PRIVATE(AccountSet) + int m_count; + MyMoneyFile* m_file; + QList m_typeList; + QTreeWidgetItem* m_favorites; + bool m_hideClosedAccounts; }; - #endif diff --git a/kmymoney/widgets/kmymoneyaccounttreeview.cpp b/kmymoney/widgets/kmymoneyaccounttreeview.cpp index 673f554f5..e9e865539 100644 --- a/kmymoney/widgets/kmymoneyaccounttreeview.cpp +++ b/kmymoney/widgets/kmymoneyaccounttreeview.cpp @@ -1,257 +1,227 @@ /*************************************************************************** * Copyright 2010 Cristian Onet onet.cristian@gmail.com * * Copyright 2017 Łukasz Wojniłowicz lukasz.wojnilowicz@gmail.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ #include "kmymoneyaccounttreeview.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyinstitution.h" #include "mymoneyaccount.h" #include "models.h" #include "accountsmodel.h" #include "accountsviewproxymodel.h" #include "budgetviewproxymodel.h" #include "modelenums.h" -#include "mymoneyenums.h" -#include "viewenums.h" -class KMyMoneyAccountTreeViewPrivate -{ - Q_DISABLE_COPY(KMyMoneyAccountTreeViewPrivate) - Q_DECLARE_PUBLIC(KMyMoneyAccountTreeView) - -public: - KMyMoneyAccountTreeViewPrivate(KMyMoneyAccountTreeView *qq) : - q_ptr(qq), - m_view(View::None) - { - } - - ~KMyMoneyAccountTreeViewPrivate() - { - } - - QVector getVisibleGroups(const View view) - { - switch (view) { - case View::Institutions: - case View::Accounts: - return QVector {eMyMoney::Account::Asset, eMyMoney::Account::Liability, eMyMoney::Account::Equity}; - case View::Categories: - case View::Budget: - return QVector {eMyMoney::Account::Income, eMyMoney::Account::Expense}; - default: - return QVector (); - } - } - - QSet readVisibleColumns(const View view) - { - QSet columns; - - const auto grp = KSharedConfig::openConfig()->group(getConfGrpName(view)); - const auto cfgColumns = grp.readEntry("ColumnsSelection", QList()); - columns.insert(eAccountsModel::Column::Account); - foreach (const auto column, cfgColumns) - columns.insert(static_cast(column)); - return columns; - } - - void openIndex(const QModelIndex &index) - { - Q_Q(KMyMoneyAccountTreeView); - if (index.isValid()) { - QVariant data = q->model()->data(index, (int)eAccountsModel::Role::Account); - if (data.isValid()) { - if (data.canConvert()) { - emit q->openObject(data.value()); - } - if (data.canConvert()) { - emit q->openObject(data.value()); - } - } - } - } - - static QString getConfGrpName(const View view) - { - switch (view) { - case View::Institutions: - return QStringLiteral("KInstitutionsView"); - case View::Accounts: - return QStringLiteral("KAccountsView"); - case View::Categories: - return QStringLiteral("KCategoriesView"); - case View::Budget: - return QStringLiteral("KBudgetsView"); - default: - return QString(); - } - } - - KMyMoneyAccountTreeView *q_ptr; - AccountsViewProxyModel *m_model; - View m_view; -}; - -KMyMoneyAccountTreeView::KMyMoneyAccountTreeView(QWidget *parent) : - QTreeView(parent), - d_ptr(new KMyMoneyAccountTreeViewPrivate(this)) +KMyMoneyAccountTreeView::KMyMoneyAccountTreeView(QWidget *parent) + : QTreeView(parent), m_view(View::None) { setContextMenuPolicy(Qt::CustomContextMenu); // allow context menu to be opened on tree items header()->setContextMenuPolicy(Qt::CustomContextMenu); // allow context menu to be opened on tree header for columns selection connect(this, &QWidget::customContextMenuRequested, this, &KMyMoneyAccountTreeView::customContextMenuRequested); setAllColumnsShowFocus(true); setAlternatingRowColors(true); setIconSize(QSize(22, 22)); setSortingEnabled(true); } KMyMoneyAccountTreeView::~KMyMoneyAccountTreeView() { - Q_D(KMyMoneyAccountTreeView); - if (d->m_view != View::None) { - auto grp = KSharedConfig::openConfig()->group(d->getConfGrpName(d->m_view)); + if (m_view != View::None) { + auto grp = KSharedConfig::openConfig()->group(getConfGrpName(m_view)); const auto columns = header()->saveState(); grp.writeEntry("HeaderState", columns); QList visColumns; - foreach (const auto column, d->m_model->getVisibleColumns()) + foreach (const auto column, m_model->getVisibleColumns()) visColumns.append(static_cast(column)); grp.writeEntry("ColumnsSelection", visColumns); grp.sync(); } - delete d; } AccountsViewProxyModel *KMyMoneyAccountTreeView::init(View view) { - Q_D(KMyMoneyAccountTreeView); - d->m_view = view; + m_view = view; if (view != View::Budget) - d->m_model = new AccountsViewProxyModel(this); + m_model = new AccountsViewProxyModel(this); else - d->m_model = new BudgetViewProxyModel(this); + m_model = new BudgetViewProxyModel(this); - d->m_model->addAccountGroup(d->getVisibleGroups(view)); + m_model->addAccountGroup(getVisibleGroups(view)); const auto accountsModel = Models::instance()->accountsModel(); const auto institutionsModel = Models::instance()->institutionsModel(); AccountsModel *sourceModel; if (view != View::Institutions) sourceModel = accountsModel; else sourceModel = institutionsModel; - foreach (const auto column, d->readVisibleColumns(view)) { - d->m_model->setColumnVisibility(column, true); - accountsModel->setColumnVisibility(column, true); - institutionsModel->setColumnVisibility(column, true); + foreach (const auto column, readVisibleColumns(view)) { + m_model->setColumnVisibility(column, true); + accountsModel->setColumnVisibility(column, true); + institutionsModel->setColumnVisibility(column, true); } - d->m_model->setSourceModel(sourceModel); - d->m_model->setSourceColumns(sourceModel->getColumns()); - setModel(d->m_model); + m_model->setSourceModel(sourceModel); + m_model->setSourceColumns(sourceModel->getColumns()); + setModel(m_model); - connect(this->header(), &QWidget::customContextMenuRequested, d->m_model, &AccountsViewProxyModel::slotColumnsMenu); - connect(d->m_model, &AccountsViewProxyModel::columnToggled, this, &KMyMoneyAccountTreeView::columnToggled); + connect(this->header(), &QWidget::customContextMenuRequested, m_model, &AccountsViewProxyModel::slotColumnsMenu); + connect(m_model, &AccountsViewProxyModel::columnToggled, this, &KMyMoneyAccountTreeView::columnToggled); // restore the headers - const auto grp = KSharedConfig::openConfig()->group(d->getConfGrpName(view)); + const auto grp = KSharedConfig::openConfig()->group(getConfGrpName(view)); const auto columnNames = grp.readEntry("HeaderState", QByteArray()); header()->restoreState(columnNames); - return d->m_model; + return m_model; } void KMyMoneyAccountTreeView::mouseDoubleClickEvent(QMouseEvent *event) { - Q_D(KMyMoneyAccountTreeView); - d->openIndex(currentIndex()); + openIndex(currentIndex()); event->accept(); } void KMyMoneyAccountTreeView::keyPressEvent(QKeyEvent *event) { - Q_D(KMyMoneyAccountTreeView); if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { - d->openIndex(currentIndex()); + openIndex(currentIndex()); event->accept(); } else { QTreeView::keyPressEvent(event); } } +void KMyMoneyAccountTreeView::openIndex(const QModelIndex &index) +{ + if (index.isValid()) { + QVariant data = model()->data(index, (int)eAccountsModel::Role::Account); + if (data.isValid()) { + if (data.canConvert()) { + emit openObject(data.value()); + } + if (data.canConvert()) { + emit openObject(data.value()); + } + } + } +} + +QString KMyMoneyAccountTreeView::getConfGrpName(const View view) +{ + switch (view) { + case View::Institutions: + return QStringLiteral("KInstitutionsView"); + case View::Accounts: + return QStringLiteral("KAccountsView"); + case View::Categories: + return QStringLiteral("KCategoriesView"); + case View::Budget: + return QStringLiteral("KBudgetsView"); + default: + return QString(); + } +} + void KMyMoneyAccountTreeView::customContextMenuRequested(const QPoint) { const auto index = model()->index(currentIndex().row(), (int)eAccountsModel::Column::Account, currentIndex().parent()); if (index.isValid() && (model()->flags(index) & Qt::ItemIsSelectable)) { const auto data = model()->data(index, (int)eAccountsModel::Role::Account); if (data.isValid()) { if (data.canConvert()) { emit selectObject(data.value()); emit openContextMenu(data.value()); } if (data.canConvert()) { emit selectObject(data.value()); emit openContextMenu(data.value()); } } } } void KMyMoneyAccountTreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { QTreeView::selectionChanged(selected, deselected); if (!selected.empty()) { auto indexes = selected.front().indexes(); if (!indexes.empty()) { const auto data = model()->data(model()->index(indexes.front().row(), (int)eAccountsModel::Column::Account, indexes.front().parent()), (int)eAccountsModel::Role::Account); if (data.isValid()) { if (data.canConvert()) { emit selectObject(data.value()); } if (data.canConvert()) { emit selectObject(data.value()); } // an object was successfully selected return; } } } // since no object was selected reset the object selection emit selectObject(MyMoneyAccount()); emit selectObject(MyMoneyInstitution()); } + +QVector KMyMoneyAccountTreeView::getVisibleGroups(const View view) +{ + switch (view) { + case View::Institutions: + case View::Accounts: + return QVector {eMyMoney::Account::Asset, eMyMoney::Account::Liability, eMyMoney::Account::Equity}; + case View::Categories: + case View::Budget: + return QVector {eMyMoney::Account::Income, eMyMoney::Account::Expense}; + default: + return QVector (); + } +} + +QSet KMyMoneyAccountTreeView::readVisibleColumns(const View view) +{ + QSet columns; + + const auto grp = KSharedConfig::openConfig()->group(getConfGrpName(view)); + const auto cfgColumns = grp.readEntry("ColumnsSelection", QList()); + columns.insert(eAccountsModel::Column::Account); + foreach (const auto column, cfgColumns) + columns.insert(static_cast(column)); + return columns; +} diff --git a/kmymoney/widgets/kmymoneyaccounttreeview.h b/kmymoney/widgets/kmymoneyaccounttreeview.h index 9125fb660..ecc05fb32 100644 --- a/kmymoney/widgets/kmymoneyaccounttreeview.h +++ b/kmymoney/widgets/kmymoneyaccounttreeview.h @@ -1,96 +1,103 @@ /*************************************************************************** * Copyright 2010 Cristian Onet onet.cristian@gmail.com * * Copyright 2017 Łukasz Wojniłowicz lukasz.wojnilowicz@gmail.com * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ #ifndef KMYMONEYACCOUNTTREEVIEW_H #define KMYMONEYACCOUNTTREEVIEW_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -class MyMoneyObject; -class AccountsViewProxyModel; +#include "mymoneyenums.h" +#include "viewenums.h" + +namespace eAccountsModel { + enum class Column; +} -namespace eAccountsModel { enum class Column; } -enum class View; +class AccountsViewProxyModel; +class MyMoneyObject; /** * This view was created to handle the actions that could be performed with the accounts. */ -class KMyMoneyAccountTreeViewPrivate; class KMyMoneyAccountTreeView : public QTreeView { Q_OBJECT - Q_DISABLE_COPY(KMyMoneyAccountTreeView) public: - explicit KMyMoneyAccountTreeView(QWidget* parent = nullptr); + KMyMoneyAccountTreeView(QWidget *parent = nullptr); ~KMyMoneyAccountTreeView(); AccountsViewProxyModel *init(View view); protected: - void mouseDoubleClickEvent(QMouseEvent *event) override; - void keyPressEvent(QKeyEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event); + void keyPressEvent(QKeyEvent *event); protected slots: void customContextMenuRequested(const QPoint); - void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override; + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); signals: /** * This signal serves as proxy for KMyMoneyAccountTree::selectObject() * * @param obj const reference to object */ void selectObject(const MyMoneyObject& obj); /** * This signal serves as proxy for * KMyMoneyAccountTree::openContextMenu(const MyMoneyObject&) * * @param obj const reference to object */ void openContextMenu(const MyMoneyObject& obj); /** * This signal is emitted whenever the user requests to open an object * * @param obj reference to actual MyMoneyObject (is either * MyMoneyAccount or MyMoneyInstitution depending on selected item) */ void openObject(const MyMoneyObject& obj); void columnToggled(const eAccountsModel::Column column, const bool show); private: - KMyMoneyAccountTreeViewPrivate * const d_ptr; - Q_DECLARE_PRIVATE(KMyMoneyAccountTreeView) + void openIndex(const QModelIndex &index); + static QString getConfGrpName(const View view); + QSet readVisibleColumns(const View view); + QVector getVisibleGroups(const View view); + + AccountsViewProxyModel *m_model; + View m_view; }; #endif // KMYMONEYACCOUNTTREEVIEW_H diff --git a/kmymoney/widgets/kmymoneyactivitycombo.cpp b/kmymoney/widgets/kmymoneyactivitycombo.cpp deleted file mode 100644 index cf8d9b06d..000000000 --- a/kmymoney/widgets/kmymoneyactivitycombo.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/*************************************************************************** - kmymoneyactivitycombo.cpp - description - ------------------- - begin : Sat Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "kmymoneyactivitycombo.h" -#include "kmymoneymvccombo_p.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -#include - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "mymoneyenums.h" - -using namespace eMyMoney; - -class KMyMoneyActivityComboPrivate : public KMyMoneyMVCComboPrivate -{ - Q_DISABLE_COPY(KMyMoneyActivityComboPrivate) - -public: - KMyMoneyActivityComboPrivate() : - m_activity(Split::InvestmentTransactionType::UnknownTransactionType) - { - } - - eMyMoney::Split::InvestmentTransactionType m_activity; -}; - -KMyMoneyActivityCombo::KMyMoneyActivityCombo(QWidget* w) : - KMyMoneyMVCCombo(*new KMyMoneyActivityComboPrivate, false, w) -{ - addItem(i18n("Buy shares"), QVariant((int)Split::InvestmentTransactionType::BuyShares)); - addItem(i18n("Sell shares"), QVariant((int)Split::InvestmentTransactionType::SellShares)); - addItem(i18n("Dividend"), QVariant((int)Split::InvestmentTransactionType::Dividend)); - addItem(i18n("Reinvest dividend"), QVariant((int)Split::InvestmentTransactionType::ReinvestDividend)); - addItem(i18n("Yield"), QVariant((int)Split::InvestmentTransactionType::Yield)); - addItem(i18n("Add shares"), QVariant((int)Split::InvestmentTransactionType::AddShares)); - addItem(i18n("Remove shares"), QVariant((int)Split::InvestmentTransactionType::RemoveShares)); - addItem(i18n("Split shares"), QVariant((int)Split::InvestmentTransactionType::SplitShares)); - addItem(i18n("Interest Income"), QVariant((int)Split::InvestmentTransactionType::InterestIncome)); - - connect(this, SIGNAL(itemSelected(QString)), this, SLOT(slotSetActivity(QString))); -} - -KMyMoneyActivityCombo::~KMyMoneyActivityCombo() -{ -} - -void KMyMoneyActivityCombo::setActivity(Split::InvestmentTransactionType activity) -{ - Q_D(KMyMoneyActivityCombo); - d->m_activity = activity; - QString num; - setSelectedItem(num.setNum((int)activity)); -} - -eMyMoney::Split::InvestmentTransactionType KMyMoneyActivityCombo::activity() const -{ - Q_D(const KMyMoneyActivityCombo); - return d->m_activity; -} - -void KMyMoneyActivityCombo::slotSetActivity(const QString& id) -{ - Q_D(KMyMoneyActivityCombo); - QString num; - for (auto i = (int)Split::InvestmentTransactionType::BuyShares; i <= (int)Split::InvestmentTransactionType::InterestIncome; ++i) { - num.setNum(i); - if (num == id) { - d->m_activity = static_cast(i); - break; - } - } - emit activitySelected(d->m_activity); - update(); -} diff --git a/kmymoney/widgets/kmymoneyactivitycombo.h b/kmymoney/widgets/kmymoneyactivitycombo.h deleted file mode 100644 index d6c561588..000000000 --- a/kmymoney/widgets/kmymoneyactivitycombo.h +++ /dev/null @@ -1,68 +0,0 @@ -/*************************************************************************** - kmymoneyactivitycombo.h - description - ------------------- - begin : Mon Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KMYMONEYACTIVITYCOMBO_H -#define KMYMONEYACTIVITYCOMBO_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmymoneymvccombo.h" - -namespace eMyMoney { namespace Split { enum class InvestmentTransactionType; } } - -/** - * @author Thomas Baumgart - * This class implements a combo box with the possible activities - * for investment transactions (buy, sell, dividend, etc.) - */ -class KMyMoneyActivityComboPrivate; -class KMM_WIDGETS_EXPORT KMyMoneyActivityCombo : public KMyMoneyMVCCombo -{ - Q_OBJECT - Q_DISABLE_COPY(KMyMoneyActivityCombo) - -public: - /** - * Create a combo box that contains the entries "Buy", "Sell" etc. - */ - explicit KMyMoneyActivityCombo(QWidget *w = 0); - ~KMyMoneyActivityCombo() override; - - void setActivity(eMyMoney::Split::InvestmentTransactionType activity); - eMyMoney::Split::InvestmentTransactionType activity() const; - -protected slots: - void slotSetActivity(const QString& id); - -signals: - void activitySelected(eMyMoney::Split::InvestmentTransactionType); - -private: - Q_DECLARE_PRIVATE(KMyMoneyActivityCombo) -}; - -#endif diff --git a/kmymoney/widgets/kmymoneybriefschedule.h b/kmymoney/widgets/kmymoneybriefschedule.h index 6df9035ac..1809402a6 100644 --- a/kmymoney/widgets/kmymoneybriefschedule.h +++ b/kmymoney/widgets/kmymoneybriefschedule.h @@ -1,80 +1,80 @@ /*************************************************************************** kmymoneybriefschedule.h - description ------------------- begin : Sun Jul 6 2003 copyright : (C) 2000-2003 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYBRIEFSCHEDULE_H #define KMYMONEYBRIEFSCHEDULE_H // ---------------------------------------------------------------------------- // QT Includes #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "ui_kschedulebriefwidget.h" #include "mymoneyschedule.h" /** *@author Michael Edwardes */ class kScheduleBriefWidget : public QWidget, public Ui::kScheduleBriefWidget { public: kScheduleBriefWidget(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class KMyMoneyBriefSchedule : public kScheduleBriefWidget { Q_OBJECT public: - KMyMoneyBriefSchedule(QWidget* parent = nullptr); + KMyMoneyBriefSchedule(QWidget *parent = 0); ~KMyMoneyBriefSchedule(); void setSchedules(QList list, const QDate& date); signals: void enterClicked(const MyMoneySchedule&, const QDate&); void skipClicked(const MyMoneySchedule&, const QDate&); protected slots: void slotPrevClicked(); void slotNextClicked(); void slotEnterClicked(); void slotSkipClicked(); private: QList m_scheduleList; int m_index; QDate m_date; void loadSchedule(); }; #endif diff --git a/kmymoney/widgets/kmymoneycalculator.cpp b/kmymoney/widgets/kmymoneycalculator.cpp index 86d7228b0..022444543 100644 --- a/kmymoney/widgets/kmymoneycalculator.cpp +++ b/kmymoney/widgets/kmymoneycalculator.cpp @@ -1,453 +1,453 @@ /*************************************************************************** kmymoneycalculator.cpp - description ------------------- begin : Sat Oct 19 2002 copyright : (C) 2000-2002 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kmymoneycalculator.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes kMyMoneyCalculator::kMyMoneyCalculator(QWidget* parent) : QFrame(parent) { m_comma = QLocale().decimalPoint(); m_clearOperandOnDigit = false; QGridLayout* grid = new QGridLayout(this); display = new QLabel(this); QPalette palette; palette.setColor(display->backgroundRole(), QColor("#BDFFB4")); display->setPalette(palette); display->setFrameStyle(QFrame::Panel | QFrame::Sunken); display->setAlignment(Qt::AlignRight | Qt::AlignVCenter); grid->addWidget(display, 0, 0, 1, 5); buttons[0] = new QPushButton("0", this); buttons[1] = new QPushButton("1", this); buttons[2] = new QPushButton("2", this); buttons[3] = new QPushButton("3", this); buttons[4] = new QPushButton("4", this); buttons[5] = new QPushButton("5", this); buttons[6] = new QPushButton("6", this); buttons[7] = new QPushButton("7", this); buttons[8] = new QPushButton("8", this); buttons[9] = new QPushButton("9", this); buttons[PLUS] = new QPushButton("+", this); buttons[MINUS] = new QPushButton("-", this); buttons[STAR] = new QPushButton("X", this); buttons[COMMA] = new QPushButton(m_comma, this); buttons[EQUAL] = new QPushButton("=", this); buttons[SLASH] = new QPushButton("/", this); buttons[CLEAR] = new QPushButton("C", this); buttons[CLEARALL] = new QPushButton("AC", this); buttons[PLUSMINUS] = new QPushButton("+-", this); buttons[PERCENT] = new QPushButton("%", this); grid->addWidget(buttons[7], 1, 0); grid->addWidget(buttons[8], 1, 1); grid->addWidget(buttons[9], 1, 2); grid->addWidget(buttons[4], 2, 0); grid->addWidget(buttons[5], 2, 1); grid->addWidget(buttons[6], 2, 2); grid->addWidget(buttons[1], 3, 0); grid->addWidget(buttons[2], 3, 1); grid->addWidget(buttons[3], 3, 2); grid->addWidget(buttons[0], 4, 1); grid->addWidget(buttons[COMMA], 4, 0); grid->addWidget(buttons[PLUS], 3, 3); grid->addWidget(buttons[MINUS], 4, 3); grid->addWidget(buttons[STAR], 3, 4); grid->addWidget(buttons[SLASH], 4, 4); grid->addWidget(buttons[EQUAL], 4, 2); grid->addWidget(buttons[PLUSMINUS], 2, 3); grid->addWidget(buttons[PERCENT], 2, 4); grid->addWidget(buttons[CLEAR], 1, 3); grid->addWidget(buttons[CLEARALL], 1, 4); buttons[EQUAL]->setFocus(); op1 = 0.0; stackedOp = op = op0 = 0; operand.clear(); changeDisplay("0"); // connect the digit signals through a signal mapper QSignalMapper* mapper = new QSignalMapper(this); - for (auto i = 0; i < 10; ++i) { + for (int i = 0; i < 10; ++i) { mapper->setMapping(buttons[i], i); connect(buttons[i], SIGNAL(clicked()), mapper, SLOT(map())); } connect(mapper, SIGNAL(mapped(int)), this, SLOT(digitClicked(int))); // connect the calculation operations through another mapper mapper = new QSignalMapper(this); for (int i = PLUS; i <= EQUAL; ++i) { mapper->setMapping(buttons[i], i); connect(buttons[i], SIGNAL(clicked()), mapper, SLOT(map())); } connect(mapper, SIGNAL(mapped(int)), this, SLOT(calculationClicked(int))); // connect all remaining signals connect(buttons[COMMA], SIGNAL(clicked()), SLOT(commaClicked())); connect(buttons[PLUSMINUS], SIGNAL(clicked()), SLOT(plusminusClicked())); connect(buttons[PERCENT], SIGNAL(clicked()), SLOT(percentClicked())); connect(buttons[CLEAR], SIGNAL(clicked()), SLOT(clearClicked())); connect(buttons[CLEARALL], SIGNAL(clicked()), SLOT(clearAllClicked())); - for (auto i = 0; i < MAX_BUTTONS; ++i) { + for (int i = 0; i < MAX_BUTTONS; ++i) { buttons[i]->setMinimumSize(40, 30); buttons[i]->setMaximumSize(40, 30); } // keep the size determined by the size of the contained buttons no matter what setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } kMyMoneyCalculator::~kMyMoneyCalculator() { } void kMyMoneyCalculator::digitClicked(int button) { if (m_clearOperandOnDigit) { operand.clear(); m_clearOperandOnDigit = false; } operand += QChar(button + 0x30); if (operand.length() > 16) operand = operand.left(16); changeDisplay(operand); } void kMyMoneyCalculator::commaClicked() { if (operand.length() == 0) operand = '0'; if (operand.contains('.', Qt::CaseInsensitive) == 0) operand.append('.'); if (operand.length() > 16) operand = operand.left(16); changeDisplay(operand); } void kMyMoneyCalculator::plusminusClicked() { if (operand.length() == 0 && m_result.length() > 0) operand = m_result; if (operand.length() > 0) { if (operand.indexOf('-') != -1) operand.remove('-'); else operand.prepend('-'); changeDisplay(operand); } } void kMyMoneyCalculator::calculationClicked(int button) { if (operand.length() == 0 && op != 0 && button == EQUAL) { op = 0; m_result = normalizeString(op1); changeDisplay(m_result); } else if (operand.length() > 0 && op != 0) { // perform operation double op2 = operand.toDouble(); bool error = false; // if the pending operation is addition and we now do multiplication // we just stack op1 and remember the operation in if ((op == PLUS || op == MINUS) && (button == STAR || button == SLASH)) { op0 = op1; stackedOp = op; op = 0; } switch (op) { case PLUS: op2 = op1 + op2; break; case MINUS: op2 = op1 - op2; break; case STAR: op2 = op1 * op2; break; case SLASH: if (op2 == 0.0) error = true; else op2 = op1 / op2; break; } // if we have a pending addition operation, and the next operation is // not multiplication, we calculate the stacked operation if (stackedOp && button != STAR && button != SLASH) { switch (stackedOp) { case PLUS: op2 = op0 + op2; break; case MINUS: op2 = op0 - op2; break; } stackedOp = 0; } if (error) { op = 0; changeDisplay("Error"); operand.clear(); } else { op1 = op2; m_result = normalizeString(op1); changeDisplay(m_result); } } else if (operand.length() > 0 && op == 0) { op1 = operand.toDouble(); m_result = normalizeString(op1); changeDisplay(m_result); } if (button != EQUAL) { op = button; } else { op = 0; emit signalResultAvailable(); } operand.clear(); } QString kMyMoneyCalculator::normalizeString(const double& val) { QString str; str.setNum(val, 'f'); int i = str.length(); while (i > 1 && str[i-1] == '0') { --i; } // cut off trailing 0's str.remove(i, str.length()); if (str.length() > 0) { // possibly remove trailing period if (str[str.length()-1] == '.') { str.remove(str.length() - 1, 1); } } return str; } void kMyMoneyCalculator::clearClicked() { if (operand.length() > 0) { operand = operand.left(operand.length() - 1); } if (operand.length() == 0) changeDisplay("0"); else changeDisplay(operand); } void kMyMoneyCalculator::clearAllClicked() { operand.clear(); op = 0; changeDisplay("0"); } void kMyMoneyCalculator::percentClicked() { if (op != 0) { double op2 = operand.toDouble(); switch (op) { case PLUS: case MINUS: op2 = (op1 * op2) / 100; break; case STAR: case SLASH: op2 /= 100; break; } operand = normalizeString(op2); changeDisplay(operand); } } const QString kMyMoneyCalculator::result() const { - auto txt = m_result; + QString txt = m_result; txt.replace(QRegExp("\\."), m_comma); if (txt[0] == '-') { txt = txt.mid(1); // get rid of the minus sign QString mask; // TODO: port this to kf5 #if 0 switch (KLocale::global()->negativeMonetarySignPosition()) { case KLocale::ParensAround: mask = "(%1)"; break; case KLocale::AfterQuantityMoney: mask = "%1-"; break; case KLocale::AfterMoney: case KLocale::BeforeMoney: mask = "%1 -"; break; case KLocale::BeforeQuantityMoney: mask = "-%1"; break; } #else mask = "-%1"; #endif txt = QString(mask).arg(txt); } return txt; } void kMyMoneyCalculator::changeDisplay(const QString& str) { - auto txt = str; + QString txt = str; txt.replace(QRegExp("\\."), m_comma); display->setText("" + txt + ""); } void kMyMoneyCalculator::keyPressEvent(QKeyEvent* ev) { int button = -1; switch (ev->key()) { case Qt::Key_0: case Qt::Key_1: case Qt::Key_2: case Qt::Key_3: case Qt::Key_4: case Qt::Key_5: case Qt::Key_6: case Qt::Key_7: case Qt::Key_8: case Qt::Key_9: if (m_clearOperandOnDigit) { operand.clear(); m_clearOperandOnDigit = false; } button = ev->key() - Qt::Key_0; break; case Qt::Key_Plus: button = PLUS; break; case Qt::Key_Minus: button = MINUS; break; case Qt::Key_Comma: case Qt::Key_Period: if (m_clearOperandOnDigit) { operand.clear(); m_clearOperandOnDigit = false; } button = COMMA; break; case Qt::Key_Slash: button = SLASH; break; case Qt::Key_Backspace: button = CLEAR; if(ev->modifiers() & Qt::ShiftModifier) { button = CLEARALL; } break; case Qt::Key_Asterisk: button = STAR; break; case Qt::Key_Return: case Qt::Key_Enter: case Qt::Key_Equal: button = EQUAL; break; case Qt::Key_Escape: emit signalQuit(); break; case Qt::Key_Percent: button = PERCENT; break; default: ev->ignore(); break; } if (button != -1) buttons[button]->animateClick(); m_clearOperandOnDigit = false; } void kMyMoneyCalculator::setInitialValues(const QString& value, QKeyEvent* ev) { bool negative = false; // setup operand operand = value; // TODO: port this to kf5 - //operand.replace(QRegExp(QString('\\') + QString()/* TODO: port to kf5 - KLocale::global()->thousandsSeparator()*/), QChar()); + //operand.replace(QRegExp(QString('\\') + ""/* TODO: port to kf5 - KLocale::global()->thousandsSeparator()*/), QChar()); operand.replace(QRegExp(QString('\\') + m_comma), "."); if (operand.contains('(')) { negative = true; operand.remove('('); operand.remove(')'); } if (operand.contains('-')) { negative = true; operand.remove('-'); } if (operand.isEmpty()) operand = '0'; else if (negative) operand = QString("-%1").arg(operand); changeDisplay(operand); // and operation op = 0; if (ev) keyPressEvent(ev); else m_clearOperandOnDigit = true; } diff --git a/kmymoney/widgets/kmymoneycalculator.h b/kmymoney/widgets/kmymoneycalculator.h index 91679e37d..24fb4da41 100644 --- a/kmymoney/widgets/kmymoneycalculator.h +++ b/kmymoney/widgets/kmymoneycalculator.h @@ -1,253 +1,253 @@ /*************************************************************************** kmymoneycalculator.h - description ------------------- begin : Sat Oct 19 2002 copyright : (C) 2000-2002 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYCALCULATOR_H #define KMYMONEYCALCULATOR_H // ---------------------------------------------------------------------------- // QT Includes #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmm_widgets_export.h" /** *@author Thomas Baumgart */ /** * This class implements a simple electronic calculator with the * ability of addition, subtraction, multiplication and division * and percentage calculation. Memory locations are not available. * * The first operand and operation can be loaded from an external * source to switch from an edit-widget to the calculator widget * without having the user to re-type the data. See setInitialValues() * for details. */ class KMM_WIDGETS_EXPORT kMyMoneyCalculator : public QFrame { Q_OBJECT public: - kMyMoneyCalculator(QWidget* parent = nullptr); + kMyMoneyCalculator(QWidget* parent = 0); ~kMyMoneyCalculator(); /** * This methods is used to extract the result of the last * calculation. The fractional part is separated from the * integral part by the character setup using setComma(). * * @return QString representing the result of the * last operation */ const QString result() const; /** * This method is used to set the character to be used * as the separator between the integer and fractional part * of an operand. Upon creation of the object, m_comma is * set to the current locale setting of KDE's decimalSymbol. * * @param ch QChar representing the character to be used */ void setComma(const QChar ch) { m_comma = ch; }; /** * This method is used to preset the first operand and start * execution of an operation. This method is currently used * by kMyMoneyEdit. If @p ev is 0, then no operation will be * started. * * @param value reference to QString representing the operands value * @param ev pointer to QKeyEvent representing the operation's key */ void setInitialValues(const QString& value, QKeyEvent* ev); signals: /** * This signal is emitted, when a new result is available */ void signalResultAvailable(); /** * This signal is emitted, when the user pressed the ESC key */ void signalQuit(); protected: void keyPressEvent(QKeyEvent* ev); /** * This method is used to transform a double into a QString * and removing any trailing 0's and decimal separators * * @param val reference to double value to be converted * @return QString object containing the converted value */ QString normalizeString(const double& val); protected slots: /** * This method appends the digit represented by the parameter * to the current operand * * @param button integer value of the digit to be added in the * range [0..9] */ void digitClicked(int button); /** * This methods starts the operation contained in the parameter * * @param button The Qt::Keycode for the button pressed or clicked */ void calculationClicked(int button); /** * This method appends a period (comma) to initialize the fractional * part of an operand. The period is only appended once. */ void commaClicked(); /** * This method reverses the sign of the current operand */ void plusminusClicked(); /** * This method clears the current operand */ void clearClicked(); /** * This method clears all registers */ void clearAllClicked(); /** * This method executes the percent operation */ void percentClicked(); /** * This method updates the display of the calculator with * the text passed as argument * * @param str reference to QString containing the new display contents */ void changeDisplay(const QString& str); private: /** * This member variable stores the current (second) operand */ QString operand; /** * This member variable stores the last result */ QString m_result; /** * This member variable stores the representation of the * character to be used to separate the integer and fractional * part of numbers. The internal representation is always a period. */ QChar m_comma; /** * The numeric representation of a stacked first operand */ double op0; /** * The numeric representation of the first operand */ double op1; /** * This member stores the operation to be performed between * the first and the second operand. */ int op; /** * This member stores a pending addition operation */ int stackedOp; /** * This member stores a pointer to the display area */ QLabel *display; /** * This member array stores the pointers to the various * buttons of the calculator. It is setup during the * constructor of this object */ QPushButton *buttons[20]; /** * This enumeration type stores the values used for the * various keys internally */ enum { /* 0-9 are used by digits */ COMMA = 10, /* * make sure, that PLUS through EQUAL remain in * the order they are. Otherwise, check the calculation * signal mapper */ PLUS, MINUS, SLASH, STAR, EQUAL, PLUSMINUS, PERCENT, CLEAR, CLEARALL, /* insert new buttons before this line */ MAX_BUTTONS }; /** * This flag signals, if the operand should be replaced upon * a digit key pressure. Defaults to false and will be set, if * setInitialValues() is called without an operation. */ bool m_clearOperandOnDigit; }; #endif diff --git a/kmymoney/widgets/kmymoneycashflowcombo.cpp b/kmymoney/widgets/kmymoneycashflowcombo.cpp deleted file mode 100644 index 6b4b947e1..000000000 --- a/kmymoney/widgets/kmymoneycashflowcombo.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/*************************************************************************** - kmymoneycashflowcombo.cpp - description - ------------------- - begin : Sat Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "kmymoneycashflowcombo.h" -#include "kmymoneymvccombo_p.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -#include - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "registeritem.h" - -#include "mymoneyenums.h" - -using namespace eMyMoney; - -class KMyMoneyCashFlowComboPrivate : public KMyMoneyMVCComboPrivate -{ - Q_DISABLE_COPY(KMyMoneyCashFlowComboPrivate) - -public: - KMyMoneyCashFlowComboPrivate() : - m_dir(KMyMoneyRegister::Unknown) - { - } - - KMyMoneyRegister::CashFlowDirection m_dir; -}; - -KMyMoneyCashFlowCombo::KMyMoneyCashFlowCombo(Account accountType, QWidget* parent) : - KMyMoneyMVCCombo(*new KMyMoneyCashFlowComboPrivate, false, parent) -{ - addItem(" ", QVariant(KMyMoneyRegister::Unknown)); - if (accountType == Account::Income || accountType == Account::Expense) { - // this is used for income/expense accounts to just show the reverse sense - addItem(i18nc("Activity for income categories", "Received"), QVariant(KMyMoneyRegister::Payment)); - addItem(i18nc("Activity for expense categories", "Paid"), QVariant(KMyMoneyRegister::Deposit)); - } else { - addItem(i18n("Pay to"), QVariant(KMyMoneyRegister::Payment)); - addItem(i18n("From"), QVariant(KMyMoneyRegister::Deposit)); - } - - connect(this, SIGNAL(itemSelected(QString)), this, SLOT(slotSetDirection(QString))); -} - -KMyMoneyCashFlowCombo::~KMyMoneyCashFlowCombo() -{ -} - -void KMyMoneyCashFlowCombo::setDirection(KMyMoneyRegister::CashFlowDirection dir) -{ - Q_D(KMyMoneyCashFlowCombo); - d->m_dir = dir; - QString num; - setSelectedItem(num.setNum(dir)); -} - -KMyMoneyRegister::CashFlowDirection KMyMoneyCashFlowCombo::direction() const -{ - Q_D(const KMyMoneyCashFlowCombo); - return d->m_dir; -} - -void KMyMoneyCashFlowCombo::slotSetDirection(const QString& id) -{ - Q_D(KMyMoneyCashFlowCombo); - QString num; - for (int i = KMyMoneyRegister::Deposit; i <= KMyMoneyRegister::Unknown; ++i) { - num.setNum(i); - if (num == id) { - d->m_dir = static_cast(i); - break; - } - } - emit directionSelected(d->m_dir); - update(); -} - -void KMyMoneyCashFlowCombo::removeDontCare() -{ - removeItem(findData(QVariant(KMyMoneyRegister::Unknown), Qt::UserRole, Qt::MatchExactly)); -} diff --git a/kmymoney/widgets/kmymoneycashflowcombo.h b/kmymoney/widgets/kmymoneycashflowcombo.h deleted file mode 100644 index a07f9a595..000000000 --- a/kmymoney/widgets/kmymoneycashflowcombo.h +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************** - kmymoneycashflowcombo.h - description - ------------------- - begin : Mon Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KMYMONEYCASHFLOWCOMBO_H -#define KMYMONEYCASHFLOWCOMBO_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmymoneymvccombo.h" - -namespace eMyMoney { enum class Account;} -namespace KMyMoneyRegister { enum CashFlowDirection : int; } - -/** - * @author Thomas Baumgart - * This class implements a combo box with the possible states for - * actions (Deposit, Withdrawal, etc.). - */ -class KMyMoneyCashFlowComboPrivate; -class KMM_WIDGETS_EXPORT KMyMoneyCashFlowCombo : public KMyMoneyMVCCombo -{ - Q_OBJECT - Q_DISABLE_COPY(KMyMoneyCashFlowCombo) - -public: - /** - * Create a combo box that contains the entries "Pay to", "From" and - * " " for don't care. - */ - explicit KMyMoneyCashFlowCombo(eMyMoney::Account type, QWidget *parent = nullptr); - ~KMyMoneyCashFlowCombo() override; - - void setDirection(KMyMoneyRegister::CashFlowDirection dir); - KMyMoneyRegister::CashFlowDirection direction() const; - - void removeDontCare(); - -protected slots: - void slotSetDirection(const QString& id); - -signals: - void directionSelected(KMyMoneyRegister::CashFlowDirection); - -private: - Q_DECLARE_PRIVATE(KMyMoneyCashFlowCombo) -}; - -#endif diff --git a/kmymoney/widgets/kmymoneycategory.cpp b/kmymoney/widgets/kmymoneycategory.cpp index 0a42d3078..d4fae5612 100644 --- a/kmymoney/widgets/kmymoneycategory.cpp +++ b/kmymoney/widgets/kmymoneycategory.cpp @@ -1,258 +1,224 @@ /*************************************************************************** kmymoneycategory.cpp - description ------------------- begin : Mon Jul 10 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kmymoneycategory.h" -#include "kmymoneycombo_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include -#include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "kmymoneyaccountcompletion.h" #include "icons/icons.h" using namespace Icons; -class KMyMoneyCategoryPrivate : public KMyMoneyComboPrivate +class KMyMoneyCategory::Private { public: - KMyMoneyCategoryPrivate() : + Private() : splitButton(0), frame(0), recursive(false), - isSplit(false) - { - } - - ~KMyMoneyCategoryPrivate() - { - } + isSplit(false) {} QPushButton* splitButton; QFrame* frame; bool recursive; bool isSplit; }; -KMyMoneyCategory::KMyMoneyCategory(bool splitButton, QWidget* parent) : - KMyMoneyCombo(*new KMyMoneyCategoryPrivate, true, parent) +KMyMoneyCategory::KMyMoneyCategory(QWidget* parent, bool splitButton) : + KMyMoneyCombo(true, parent), + d(new Private) { - Q_D(KMyMoneyCategory); if (splitButton) { - d->frame = new QFrame(nullptr); + d->frame = new QFrame(0); // don't change the following name unless you want to break TransactionEditor::setup() d->frame->setObjectName("KMyMoneyCategoryFrame"); d->frame->setFocusProxy(this); QHBoxLayout* layout = new QHBoxLayout(d->frame); layout->setContentsMargins(0, 0, 0, 0); // make sure not to use our own overridden version of reparent() here KMyMoneyCombo::setParent(d->frame, windowFlags() & ~Qt::WindowType_Mask); KMyMoneyCombo::show(); if (parent) { d->frame->setParent(parent); d->frame->show(); } // create button KGuiItem splitButtonItem(QString(), QIcon::fromTheme(g_Icons[Icon::Split]), QString(), QString()); d->splitButton = new QPushButton(d->frame); d->splitButton->setObjectName("splitButton"); KGuiItem::assign(d->splitButton, splitButtonItem); layout->addWidget(this, 5); layout->addWidget(d->splitButton); installEventFilter(this); } - d->m_completion = new kMyMoneyAccountCompletion(this); - connect(d->m_completion, SIGNAL(itemSelected(QString)), this, SLOT(slotItemSelected(QString))); - connect(this, SIGNAL(editTextChanged(QString)), d->m_completion, SLOT(slotMakeCompletion(QString))); + m_completion = new kMyMoneyAccountCompletion(this); + connect(m_completion, SIGNAL(itemSelected(QString)), this, SLOT(slotItemSelected(QString))); + connect(this, SIGNAL(editTextChanged(QString)), m_completion, SLOT(slotMakeCompletion(QString))); } KMyMoneyCategory::~KMyMoneyCategory() { - Q_D(KMyMoneyCategory); // make sure to wipe out the frame, button and layout if (d->frame && !d->frame->parentWidget()) d->frame->deleteLater(); + + delete d; } QPushButton* KMyMoneyCategory::splitButton() const { - Q_D(const KMyMoneyCategory); return d->splitButton; } void KMyMoneyCategory::setPalette(const QPalette& palette) { - Q_D(KMyMoneyCategory); if (d->frame) d->frame->setPalette(palette); KMyMoneyCombo::setPalette(palette); } void KMyMoneyCategory::reparent(QWidget *parent, Qt::WindowFlags w, const QPoint&, bool showIt) { - Q_D(KMyMoneyCategory); if (d->frame) { d->frame->setParent(parent, w); if (showIt) d->frame->show(); } else { KMyMoneyCombo::setParent(parent, w); if (showIt) KMyMoneyCombo::show(); } } kMyMoneyAccountSelector* KMyMoneyCategory::selector() const { return dynamic_cast(KMyMoneyCombo::selector()); } void KMyMoneyCategory::setCurrentTextById(const QString& id) { if (!id.isEmpty()) { QString category = MyMoneyFile::instance()->accountToCategory(id); setCompletedText(category); setEditText(category); } else { setCompletedText(QString()); clearEditText(); } setSuppressObjectCreation(false); } void KMyMoneyCategory::slotItemSelected(const QString& id) { - Q_D(KMyMoneyCategory); setCurrentTextById(id); - d->m_completion->hide(); + m_completion->hide(); - if (d->m_id != id) { - d->m_id = id; + if (m_id != id) { + m_id = id; emit itemSelected(id); } } void KMyMoneyCategory::focusOutEvent(QFocusEvent *ev) { if (isSplitTransaction()) { KComboBox::focusOutEvent(ev); } else { KMyMoneyCombo::focusOutEvent(ev); } } void KMyMoneyCategory::focusInEvent(QFocusEvent *ev) { KMyMoneyCombo::focusInEvent(ev); // make sure, we get a clean state before we automagically move the focus to // some other widget (like for 'split transaction'). We do this by delaying // the emission of the focusIn signal until the next run of the event loop. QTimer::singleShot(0, this, SIGNAL(focusIn())); } void KMyMoneyCategory::setSplitTransaction() { - Q_D(KMyMoneyCategory); d->isSplit = true; setEditText(i18nc("Split transaction (category replacement)", "Split transaction")); setSuppressObjectCreation(true); } bool KMyMoneyCategory::isSplitTransaction() const { - Q_D(const KMyMoneyCategory); return d->isSplit; } -void KMyMoneyCategory::setCurrentText(const QString& txt) -{ - KMyMoneyCombo::setCurrentText(txt); -} - -void KMyMoneyCategory::setCurrentText() -{ - KMyMoneyCombo::setCurrentText(QString()); -} - bool KMyMoneyCategory::eventFilter(QObject *o, QEvent *ev) { - Q_D(KMyMoneyCategory); // forward enable/disable state to split button if (o == this && ev->type() == QEvent::EnabledChange) { if (d->splitButton) { d->splitButton->setEnabled(isEnabled()); } } return KMyMoneyCombo::eventFilter(o, ev); } KMyMoneySecurity::KMyMoneySecurity(QWidget* parent) : - KMyMoneyCategory(false, parent) + KMyMoneyCategory(parent, false) { } KMyMoneySecurity::~KMyMoneySecurity() { } -void KMyMoneySecurity::setCurrentText(const QString& txt) -{ - KMyMoneyCategory::setCurrentText(txt); -} - -void KMyMoneySecurity::setCurrentText() -{ - KMyMoneyCategory::setCurrentText(QString()); -} - void KMyMoneySecurity::setCurrentTextById(const QString& id) { if (!id.isEmpty()) { QString security = MyMoneyFile::instance()->account(id).name(); setCompletedText(security); setEditText(security); } else { setCompletedText(QString()); clearEditText(); } } diff --git a/kmymoney/widgets/kmymoneycategory.h b/kmymoney/widgets/kmymoneycategory.h index 460f2eac4..e764d4827 100644 --- a/kmymoney/widgets/kmymoneycategory.h +++ b/kmymoney/widgets/kmymoneycategory.h @@ -1,192 +1,194 @@ /*************************************************************************** kmymoneycategory.h ------------------- begin : Mon Jul 10 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYCATEGORY_H #define KMYMONEYCATEGORY_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes +class QPushButton; + // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneycombo.h" -class QPushButton; class kMyMoneyAccountSelector; /** * This class implements a text based account/category selector. * When initially used, the widget has the functionality of a KComboBox object. * Whenever a key is pressed, the set of loaded accounts is searched for * accounts which match the currently entered text. * * If any match is found a list selection box is opened and the user can use * the up/down, page-up/page-down keys or the mouse to navigate in the list. If * an account is selected, the selection box is closed. Other key-strokes are * directed to the parent object to manipulate the text. The visible contents of * the selection box is updated with every key-stroke. * * This object is a replacement of the kMyMoneyCategory object and should be used * for new code. * * @author Thomas Baumgart */ -class KMyMoneyCategoryPrivate; class KMyMoneyCategory : public KMyMoneyCombo { Q_OBJECT - Q_DISABLE_COPY(KMyMoneyCategory) - public: /** * Standard constructor for the account selection object. * * If parameter @a splitButton is @a true, the widget * will construct a surrounding QFrame and reparent itself to be a child of this * QFrame. It also adds a QPushButton with the "Split" icon to the right of the * input field. In this case it is important not to use the pointer to this widget * but it's parent when placing the object in a QLayout, QTable or some such. The * parent widget (the QFrame in this case) can be extracted with the parentWidget() * method. * * Reparenting is handled by the object transparently for both cases. * * Standard usage example (no split button): * * @code * KMyMoneyCategory* category = new KMyMoneyCategory; * category->reparent(newParent); * layout->addWidget(category); * table->setCellWidget(category); * @endcode * * Enhanced usage example (with split button): * * @code * KMyMoneyCategory* category = new KMyMoneyCategory(0, true); * category->reparent(newParent); * layout->addWidget(category->parentWidget()); * table->setCellWidget(category->parentWidget()); * @endcode */ - explicit KMyMoneyCategory(bool splitButton = false, QWidget* parent = nullptr); - ~KMyMoneyCategory() override; + explicit KMyMoneyCategory(QWidget* parent = 0, bool splitButton = false); + + virtual ~KMyMoneyCategory(); /** * This member returns a pointer to the completion object. * * @return pointer to completion's selector object */ kMyMoneyAccountSelector* selector() const; /** * This member returns a pointer to the split button. In case the @a splitButton parameter * of the constructor was @a false, this method prints a warning to stderr and returns 0. */ QPushButton* splitButton() const; /** * Reimplemented for internal reasons. No API change */ virtual void reparent(QWidget *parent, Qt::WindowFlags, const QPoint &, bool showIt = false); /** * Reimplemented for internal reasons. No API change. */ virtual void setPalette(const QPalette& palette); /** * Force the text field to show the text for split transaction. */ void setSplitTransaction(); /** * Check if the text field contains the text for a split transaction */ bool isSplitTransaction() const; /** * overridden for internal reasons, no API change */ - void setCurrentText(const QString& txt); - void setCurrentText(); + void setCurrentText(const QString& txt = QString()) { + KMyMoneyCombo::setCurrentText(txt); + } - bool eventFilter(QObject *o, QEvent *ev) override; + bool eventFilter(QObject *o, QEvent *ev); protected: /** * Reimplemented to support protected category text ("split transactions") * * @sa focusIn() */ - void focusInEvent(QFocusEvent* ev) override; + virtual void focusInEvent(QFocusEvent* ev); /** * Reimplemented to support protected category text ("split transactions") */ - void focusOutEvent(QFocusEvent* ev) override; + virtual void focusOutEvent(QFocusEvent* ev); /** * set the widgets text area based on the item with the given @a id. */ - void setCurrentTextById(const QString& id) override; + virtual void setCurrentTextById(const QString& id); public slots: - void slotItemSelected(const QString& id) override; + virtual void slotItemSelected(const QString& id); signals: /** * Signal to inform other objects that this object has reached focus. * Used for e.g. to open the split dialog when the focus reaches this * object and it contains the text 'Split transaction'. * * @sa focusInEvent() */ void focusIn(); private: - Q_DECLARE_PRIVATE(KMyMoneyCategory) + /// \internal d-pointer class. + class Private; + /// \internal d-pointer instance. + Private* const d; }; class KMyMoneySecurity : public KMyMoneyCategory { Q_OBJECT - Q_DISABLE_COPY(KMyMoneySecurity) - public: - explicit KMyMoneySecurity(QWidget* parent = nullptr); - ~KMyMoneySecurity() override; + KMyMoneySecurity(QWidget* parent = 0); + virtual ~KMyMoneySecurity(); /** * overridden for internal reasons, no API change */ - void setCurrentText(const QString& txt); - void setCurrentText(); + void setCurrentText(const QString& txt = QString()) { + KMyMoneyCategory::setCurrentText(txt); + } protected: /** * set the widgets text area based on the item with the given @a id. */ - void setCurrentTextById(const QString& id) override; + virtual void setCurrentTextById(const QString& id); }; #endif diff --git a/kmymoney/widgets/kmymoneycombo.cpp b/kmymoney/widgets/kmymoneycombo.cpp index ecf7a8047..d0949c314 100644 --- a/kmymoney/widgets/kmymoneycombo.cpp +++ b/kmymoney/widgets/kmymoneycombo.cpp @@ -1,381 +1,332 @@ /*************************************************************************** kmymoneycombo.cpp - description ------------------- begin : Mon Mar 12 2007 copyright : (C) 2007 by Thomas Baumgart email : ipwizard@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kmymoneycombo.h" -#include "kmymoneycombo_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyselector.h" #include "kmymoneycompletion.h" #include "kmymoneylineedit.h" -KMyMoneyCombo::KMyMoneyCombo(QWidget *parent) : - KComboBox(parent), - d_ptr(new KMyMoneyComboPrivate) +KMyMoneyCombo::KMyMoneyCombo(QWidget *w) : + KComboBox(w), + m_completion(0), + m_edit(0), + m_canCreateObjects(false), + m_inFocusOutEvent(false) { } -KMyMoneyCombo::KMyMoneyCombo(bool rw, QWidget *parent) : - KComboBox(rw, parent), - d_ptr(new KMyMoneyComboPrivate) +KMyMoneyCombo::KMyMoneyCombo(bool rw, QWidget *w) : + KComboBox(rw, w), + m_completion(0), + m_edit(0), + m_canCreateObjects(false), + m_inFocusOutEvent(false) { - Q_D(KMyMoneyCombo); if (rw) { - d->m_edit = new kMyMoneyLineEdit(this, "combo edit"); - setLineEdit(d->m_edit); + m_edit = new kMyMoneyLineEdit(this, "combo edit"); + setLineEdit(m_edit); } } -KMyMoneyCombo::KMyMoneyCombo(KMyMoneyComboPrivate &dd, bool rw, QWidget *parent) : - KComboBox(rw, parent), - d_ptr(&dd) -{ - Q_D(KMyMoneyCombo); - if (rw) { - d->m_edit = new kMyMoneyLineEdit(this, "combo edit"); - setLineEdit(d->m_edit); - } -} - -KMyMoneyCombo::~KMyMoneyCombo() -{ - Q_D(KMyMoneyCombo); - delete d; -} - void KMyMoneyCombo::setCurrentTextById(const QString& id) { clearEditText(); if (!id.isEmpty()) { QTreeWidgetItem* item = selector()->item(id); if (item) { setCompletedText(item->text(0)); setEditText(item->text(0)); } } } void KMyMoneyCombo::slotItemSelected(const QString& id) { - Q_D(KMyMoneyCombo); if (isEditable()) { bool blocked = signalsBlocked(); blockSignals(true); setCurrentTextById(id); blockSignals(blocked); } - d->m_completion->hide(); + m_completion->hide(); - if (d->m_id != id) { - d->m_id = id; + if (m_id != id) { + m_id = id; emit itemSelected(id); } } void KMyMoneyCombo::setEditable(bool y) { - Q_D(KMyMoneyCombo); if (y == isEditable()) return; KComboBox::setEditable(y); // make sure we use our own line edit style if (y) { - d->m_edit = new kMyMoneyLineEdit(this, "combo edit"); - setLineEdit(d->m_edit); - d->m_edit->setPalette(palette()); + m_edit = new kMyMoneyLineEdit(this, "combo edit"); + setLineEdit(m_edit); + m_edit->setPalette(palette()); } else { - d->m_edit = 0; + m_edit = 0; } } void KMyMoneyCombo::setPlaceholderText(const QString& hint) const { - Q_D(const KMyMoneyCombo); - if (d->m_edit) - d->m_edit->setPlaceholderText(hint); + if (m_edit) + m_edit->setPlaceholderText(hint); } void KMyMoneyCombo::paintEvent(QPaintEvent* ev) { - Q_D(KMyMoneyCombo); KComboBox::paintEvent(ev); // if we don't have an edit field, we need to paint the text onto the button - if (!d->m_edit) { - if (d->m_completion) { + if (!m_edit) { + if (m_completion) { QStringList list; selector()->selectedItems(list); if (!list.isEmpty()) { QString str = selector()->item(list[0])->text(0); // we only paint, if the text is longer than 1 char. Assumption // is that length 1 is the blank case so no need to do painting if (str.length() > 1) { QPainter p(this); p.setPen(palette().text().color()); QStyleOptionComboBox opt; initStyleOption(&opt); QRect re = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this); p.setClipRect(re); p.save(); p.setFont(font()); QFontMetrics fm(font()); int x = re.x(), y = re.y() + fm.ascent(); p.drawText(x, y, str); p.restore(); } } } } } void KMyMoneyCombo::mousePressEvent(QMouseEvent *e) { - Q_D(KMyMoneyCombo); // mostly copied from QCombo::mousePressEvent() and adjusted for our needs if (e->button() != Qt::LeftButton) return; - if (((!isEditable() || isInArrowArea(e->globalPos())) && selector()->itemList().count()) && !d->m_completion->isVisible()) { - d->m_completion->setVisible(true); + if (((!isEditable() || isInArrowArea(e->globalPos())) && selector()->itemList().count()) && !m_completion->isVisible()) { + m_completion->setVisible(true); } - if (d->m_timer.isActive()) { - d->m_timer.stop(); - d->m_completion->slotMakeCompletion(QString()); + if (m_timer.isActive()) { + m_timer.stop(); + m_completion->slotMakeCompletion(""); // the above call clears the selection in the selector but maintains the current index, use that index to restore the selection QTreeWidget* listView = selector()->listView(); QModelIndex currentIndex = listView->currentIndex(); if (currentIndex.isValid()) { listView->selectionModel()->select(currentIndex, QItemSelectionModel::Select); listView->scrollToItem(listView->currentItem()); } } else { KConfig config("kcminputrc"); KConfigGroup grp = config.group("KDE"); - d->m_timer.setSingleShot(true); - d->m_timer.start(grp.readEntry("DoubleClickInterval", 400)); + m_timer.setSingleShot(true); + m_timer.start(grp.readEntry("DoubleClickInterval", 400)); } } bool KMyMoneyCombo::isInArrowArea(const QPoint& pos) const { QStyleOptionComboBox opt; initStyleOption(&opt); QRect arrowRect = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxArrow, this); // Correction for motif style, where arrow is smaller // and thus has a rect that doesn't fit the button. arrowRect.setHeight(qMax(height() - (2 * arrowRect.y()), arrowRect.height())); // if the button is not isEditable, it covers the whole widget if (!isEditable()) arrowRect = rect(); return arrowRect.contains(mapFromGlobal(pos)); } - -void KMyMoneyCombo::setSuppressObjectCreation(bool suppress) -{ - Q_D(KMyMoneyCombo); - d->m_canCreateObjects = !suppress; -} - -void KMyMoneyCombo::setCurrentText(const QString& txt) -{ - KComboBox::setItemText(KComboBox::currentIndex(), txt); -} - -void KMyMoneyCombo::setCurrentText() -{ - KComboBox::setItemText(KComboBox::currentIndex(), QString()); -} - void KMyMoneyCombo::keyPressEvent(QKeyEvent* e) { - Q_D(KMyMoneyCombo); if ((e->key() == Qt::Key_F4 && e->modifiers() == 0) || (e->key() == Qt::Key_Down && (e->modifiers() & Qt::AltModifier)) || (!isEditable() && e->key() == Qt::Key_Space)) { // if we have at least one item in the list, we open the dropdown if (selector()->listView()->itemAt(0, 0)) - d->m_completion->setVisible(true); + m_completion->setVisible(true); e->ignore(); return; } KComboBox::keyPressEvent(e); } void KMyMoneyCombo::connectNotify(const QMetaMethod & signal) { - Q_D(KMyMoneyCombo); if (signal != QMetaMethod::fromSignal(&KMyMoneyCombo::createItem)) { - d->m_canCreateObjects = true; + m_canCreateObjects = true; } } void KMyMoneyCombo::disconnectNotify(const QMetaMethod & signal) { - Q_D(KMyMoneyCombo); if (signal != QMetaMethod::fromSignal(&KMyMoneyCombo::createItem)) { - d->m_canCreateObjects = false; + m_canCreateObjects = false; } } void KMyMoneyCombo::focusOutEvent(QFocusEvent* e) { - Q_D(KMyMoneyCombo); // don't do anything if the focus is lost due to window activation, this way switching // windows while typing a category will not popup the category creation dialog // also ignore the fact that the focus is lost because of Qt::PopupFocusReason (context menu) if (e->reason() == Qt::ActiveWindowFocusReason || e->reason() == Qt::PopupFocusReason) return; - if (d->m_inFocusOutEvent) { + if (m_inFocusOutEvent) { KComboBox::focusOutEvent(e); return; } - d->m_inFocusOutEvent = true; + m_inFocusOutEvent = true; if (isEditable() && !currentText().isEmpty()) { - if (d->m_canCreateObjects) { - if (!d->m_completion->selector()->contains(currentText())) { + if (m_canCreateObjects) { + if (!m_completion->selector()->contains(currentText())) { QString id; // annouce that we go into a possible dialog to create an object // This can be used by upstream widgets to disable filters etc. emit objectCreation(true); emit createItem(currentText(), id); // Announce that we return from object creation emit objectCreation(false); // update the field to a possibly created object - d->m_id = id; + m_id = id; setCurrentTextById(id); // make sure the completion does not show through - d->m_completion->hide(); + m_completion->hide(); } // else if we cannot create objects, and the current text is not // in the list, then we clear the text and the selection. - } else if (!d->m_completion->selector()->contains(currentText())) { + } else if (!m_completion->selector()->contains(currentText())) { clearEditText(); } } KComboBox::focusOutEvent(e); // force update of hint and id if there is no text in the widget if (isEditable() && currentText().isEmpty()) { - QString id = d->m_id; - d->m_id.clear(); + QString id = m_id; + m_id.clear(); if (!id.isEmpty()) - emit itemSelected(d->m_id); + emit itemSelected(m_id); update(); } - d->m_inFocusOutEvent = false; + m_inFocusOutEvent = false; } KMyMoneySelector* KMyMoneyCombo::selector() const { - Q_D(const KMyMoneyCombo); - return d->m_completion->selector(); + return m_completion->selector(); } kMyMoneyCompletion* KMyMoneyCombo::completion() const { - Q_D(const KMyMoneyCombo); - return d->m_completion; + return m_completion; } void KMyMoneyCombo::selectedItems(QStringList& list) const { - Q_D(const KMyMoneyCombo); if (lineEdit() && lineEdit()->text().length() == 0) { list.clear(); } else { - d->m_completion->selector()->selectedItems(list); + m_completion->selector()->selectedItems(list); } } -const QString& KMyMoneyCombo::selectedItem() const -{ - Q_D(const KMyMoneyCombo); - return d->m_id; -} - void KMyMoneyCombo::setSelectedItem(const QString& id) { - Q_D(KMyMoneyCombo); - d->m_completion->selector()->setSelected(id, true); + m_completion->selector()->setSelected(id, true); blockSignals(true); slotItemSelected(id); blockSignals(false); update(); } QSize KMyMoneyCombo::sizeHint() const { return KComboBox::sizeHint(); // I wanted to use the code below to adjust the size of the combo box // according to the largest item in the selector list. Apparently that // does not work too well in the enter and edit schedule dialog for // the category combo box. So we just use the standard implementation for now. #if 0 constPolish(); int i, w; QFontMetrics fm = fontMetrics(); int maxW = count() ? 18 : 7 * fm.width(QChar('x')) + 18; int maxH = qMax(fm.lineSpacing(), 14) + 2; w = selector()->optimizedWidth(); if (w > maxW) maxW = w; QSize sizeHint = (style().sizeFromContents(QStyle::CT_ComboBox, this, QSize(maxW, maxH)). expandedTo(QApplication::globalStrut())); return sizeHint; #endif } diff --git a/kmymoney/widgets/kmymoneycombo.h b/kmymoney/widgets/kmymoneycombo.h index e467536e1..9eb52c4c5 100644 --- a/kmymoney/widgets/kmymoneycombo.h +++ b/kmymoney/widgets/kmymoneycombo.h @@ -1,175 +1,207 @@ /*************************************************************************** kmymoneycombo.h - description ------------------- begin : Mon Mar 12 2007 copyright : (C) 2007 by Thomas Baumgart email : ipwizard@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYCOMBO_H #define KMYMONEYCOMBO_H // ---------------------------------------------------------------------------- // QT Includes +#include +#include +#include + // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes class kMyMoneyCompletion; class KMyMoneySelector; +class kMyMoneyLineEdit; /** * @author Thomas Baumgart */ -class KMyMoneyComboPrivate; class KMyMoneyCombo : public KComboBox { Q_OBJECT - Q_DISABLE_COPY(KMyMoneyCombo) Q_PROPERTY(QString selectedItem READ selectedItem WRITE setSelectedItem STORED false) - public: - explicit KMyMoneyCombo(QWidget *parent = nullptr); - explicit KMyMoneyCombo(bool rw = false, QWidget *parent = nullptr); - virtual ~KMyMoneyCombo(); + KMyMoneyCombo(QWidget *w = 0); + explicit KMyMoneyCombo(bool rw, QWidget *w = 0); /** * This method is used to turn on/off the hint display and to setup the appropriate text. * The hint text is shown in a lighter color if the field is otherwise empty and does * not have the keyboard focus. * * @param hint reference to text. If @a hint is empty, no hint will be shown. */ void setPlaceholderText(const QString& hint) const; /** * overridden for internal reasons. * * @param editable make combo box editable (@a true) or selectable only (@a false). */ void setEditable(bool editable); /** * This method returns a pointer to the completion object of the combo box. * * @return pointer to kMyMoneyCompletion or derivative. */ kMyMoneyCompletion* completion() const; /** * This method returns a pointer to the completion object's selector. * * @return pointer to KMyMoneySelector or derivative. */ KMyMoneySelector* selector() const; /** * This method returns the ids of the currently selected items */ void selectedItems(QStringList& list) const; /** * This method returns the id of the first selected item. * Usage makes usually only sense when the selection mode * of the associated KMyMoneySelector is QListView::Single. * * @sa KMyMoneySelector::setSelectionMode() * * @return reference to QString containing the id. If no item * is selected the QString will be empty. */ - const QString& selectedItem() const; + const QString& selectedItem() const { + return m_id; + } /** * This method selects the item with the respective @a id. * * @param id reference to QString containing the id */ void setSelectedItem(const QString& id); /** * This method checks if the position @a pos is part of the * area of the drop down arrow. */ bool isInArrowArea(const QPoint& pos) const; - void setSuppressObjectCreation(bool suppress); + void setSuppressObjectCreation(bool suppress) { + m_canCreateObjects = !suppress; + } /** * overridden for internal reasons, no API change */ - void setCurrentText(const QString& txt); - void setCurrentText(); + void setCurrentText(const QString& txt = QString()) { + KComboBox::setItemText(KComboBox::currentIndex(), txt); + } /** * Overridden to support our own completion box */ - QSize sizeHint() const override; + QSize sizeHint() const; protected slots: virtual void slotItemSelected(const QString& id); protected: /** * reimplemented to support our own popup widget */ - void mousePressEvent(QMouseEvent *e) override; + void mousePressEvent(QMouseEvent *e); /** * reimplemented to support our own popup widget */ - void keyPressEvent(QKeyEvent *e) override; + void keyPressEvent(QKeyEvent *e); /** * reimplemented to support our own popup widget */ - void paintEvent(QPaintEvent *) override; + void paintEvent(QPaintEvent *); /** * reimplemented to support detection of new items */ - void focusOutEvent(QFocusEvent*) override; + void focusOutEvent(QFocusEvent*); /** * set the widgets text area based on the item with the given @a id. */ virtual void setCurrentTextById(const QString& id); /** * Overridden for internal reasons, no API change */ - void connectNotify(const QMetaMethod & signal) override; + void connectNotify(const QMetaMethod & signal); /** * Overridden for internal reasons, no API change */ - void disconnectNotify(const QMetaMethod & signal) override; + void disconnectNotify(const QMetaMethod & signal); protected: - KMyMoneyComboPrivate * const d_ptr; - KMyMoneyCombo(KMyMoneyComboPrivate &dd, bool rw = false, QWidget *parent = 0); + /** + * This member keeps a pointer to the object's completion object + */ + kMyMoneyCompletion* m_completion; + + /** + * Use our own line edit to provide hint functionality + */ + kMyMoneyLineEdit* m_edit; + + /** + * The currently selected item + */ + QString m_id; signals: void itemSelected(const QString& id); void objectCreation(bool); void createItem(const QString&, QString&); private: - Q_DECLARE_PRIVATE(KMyMoneyCombo) + QTimer m_timer; + QMutex m_focusMutex; + /** + * Flag to control object creation. Use setSuppressObjectCreation() + * to modify it's setting. Defaults to @a false. + */ + bool m_canCreateObjects; + + /** + * Flag to check whether a focusOutEvent processing is underway or not + */ + bool m_inFocusOutEvent; }; + + + #endif diff --git a/kmymoney/widgets/kmymoneycombo_p.h b/kmymoney/widgets/kmymoneycombo_p.h deleted file mode 100644 index 8e3b68861..000000000 --- a/kmymoney/widgets/kmymoneycombo_p.h +++ /dev/null @@ -1,82 +0,0 @@ -/*************************************************************************** - kmymoneycombo_p.h - description - ------------------- - begin : Mon Mar 12 2007 - copyright : (C) 2007 by Thomas Baumgart - email : ipwizard@users.sourceforge.net - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KMYMONEYCOMBO_P_H -#define KMYMONEYCOMBO_P_H - -#include "kmymoneycombo.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -class kMyMoneyCompletion; -class kMyMoneyLineEdit; - -class KMyMoneyComboPrivate -{ -public: - KMyMoneyComboPrivate() : - m_completion(nullptr), - m_edit(nullptr), - m_canCreateObjects(false), - m_inFocusOutEvent(false) - { - } - - virtual ~KMyMoneyComboPrivate() - { - } - - /** - * This member keeps a pointer to the object's completion object - */ - kMyMoneyCompletion* m_completion; - - /** - * Use our own line edit to provide hint functionality - */ - kMyMoneyLineEdit* m_edit; - - /** - * The currently selected item - */ - QString m_id; - - QTimer m_timer; - QMutex m_focusMutex; - /** - * Flag to control object creation. Use setSuppressObjectCreation() - * to modify it's setting. Defaults to @a false. - */ - bool m_canCreateObjects; - - /** - * Flag to check whether a focusOutEvent processing is underway or not - */ - bool m_inFocusOutEvent; -}; - -#endif diff --git a/kmymoney/widgets/kmymoneycompletion.h b/kmymoney/widgets/kmymoneycompletion.h index bfdcec064..d88cae575 100644 --- a/kmymoney/widgets/kmymoneycompletion.h +++ b/kmymoney/widgets/kmymoneycompletion.h @@ -1,122 +1,122 @@ /*************************************************************************** kmymoneycompletion.h - description ------------------- begin : Mon Apr 26 2004 copyright : (C) 2000-2004 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYCOMPLETION_H #define KMYMONEYCOMPLETION_H // ---------------------------------------------------------------------------- // QT Includes #include #include class QTreeWidgetItem; class QTreeWidget; // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes class KMyMoneySelector; /** * @author Thomas Baumgart */ class kMyMoneyCompletion : public QWidget { Q_OBJECT public: - explicit kMyMoneyCompletion(QWidget* parent = nullptr); + kMyMoneyCompletion(QWidget *parent = 0); virtual ~kMyMoneyCompletion(); /** * Re-implemented for internal reasons. API is unaffected. */ virtual void hide(); /** * This method sets the current account with id @p id as * the current selection. * * @param id id of account to be selected */ void setSelected(const QString& id); virtual KMyMoneySelector* selector() const { return m_selector; } public slots: void slotMakeCompletion(const QString& txt); void slotItemSelected(QTreeWidgetItem *item, int col); protected: /** * Reimplemented from kMyMoneyAccountSelector to get events from the viewport (to hide * this widget on mouse-click, Escape-presses, etc. */ - bool eventFilter(QObject *, QEvent *) override; + virtual bool eventFilter(QObject *, QEvent *); /** * Re-implemented for internal reasons. API is unaffected. */ - void showEvent(QShowEvent*) override; + virtual void showEvent(QShowEvent*); /** * This method resizes the widget to show a maximum of @p count * or @a MAX_ITEMS items. * * @param count maximum number to be shown if < MAX_ITEMS */ void adjustSize(const int count); /** * This method counts the number of items currently visible and * calls adjustSize(count). */ void adjustSize(); void connectSignals(QWidget *widget, QTreeWidget* lv); void show(bool presetSelected); signals: void itemSelected(const QString& id); protected: QWidget* m_parent; QWidget* m_widget; QString m_id; QTreeWidget* m_lv; KMyMoneySelector* m_selector; QRegExp m_lastCompletion; static const int MAX_ITEMS; }; #endif diff --git a/kmymoney/widgets/kmymoneycurrencyselector.cpp b/kmymoney/widgets/kmymoneycurrencyselector.cpp index ed942cb1f..55327949b 100644 --- a/kmymoney/widgets/kmymoneycurrencyselector.cpp +++ b/kmymoney/widgets/kmymoneycurrencyselector.cpp @@ -1,200 +1,171 @@ /*************************************************************************** kmymoneycurrencyselector.cpp - description ------------------- begin : Tue Apr 6 2004 copyright : (C) 2000-2004 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kmymoneycurrencyselector.h" // ---------------------------------------------------------------------------- // QT Includes #include #include // ---------------------------------------------------------------------------- // KDE Includes + // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyfile.h" -#include "mymoneysecurity.h" #include "icons/icons.h" using namespace Icons; -class KMyMoneySecuritySelectorPrivate -{ - Q_DISABLE_COPY(KMyMoneySecuritySelectorPrivate) - -public: - KMyMoneySecuritySelectorPrivate(): - m_displayItem(KMyMoneySecuritySelector::FullName), +KMyMoneySecuritySelector::KMyMoneySecuritySelector(QWidget *parent) : + KComboBox(parent), + m_displayItem(FullName), m_selectedItemId(0), m_displayOnly(false), - m_displayType(KMyMoneySecuritySelector::TypeAll) - { - } - - MyMoneySecurity m_currency; - KMyMoneySecuritySelector::displayItemE m_displayItem; - int m_selectedItemId; - bool m_displayOnly; - KMyMoneySecuritySelector::displayTypeE m_displayType; - QList m_list; -}; - -KMyMoneySecuritySelector::KMyMoneySecuritySelector(QWidget *parent) : - KComboBox(parent), - d_ptr(new KMyMoneySecuritySelectorPrivate) + m_displayType(TypeAll) { // update(QString()); } KMyMoneySecuritySelector::~KMyMoneySecuritySelector() { - Q_D(KMyMoneySecuritySelector); - delete d; } void KMyMoneySecuritySelector::selectDisplayItem(KMyMoneySecuritySelector::displayItemE item) { - Q_D(KMyMoneySecuritySelector); - d->m_displayItem = item; + m_displayItem = item; update(QString()); } void KMyMoneySecuritySelector::setDisplayType(displayTypeE type) { - Q_D(KMyMoneySecuritySelector); - d->m_displayType = type; + m_displayType = type; } void KMyMoneySecuritySelector::update(const QString& id) { - Q_D(KMyMoneySecuritySelector); MyMoneySecurity curr = MyMoneyFile::instance()->baseCurrency(); QString baseCurrency = curr.id(); if (!id.isEmpty()) - curr = d->m_currency; + curr = m_currency; this->clear(); - d->m_list.clear(); - if (d->m_displayType & TypeCurrencies) - d->m_list += MyMoneyFile::instance()->currencyList(); - if (d->m_displayType & TypeSecurities) - d->m_list += MyMoneyFile::instance()->securityList(); + m_list.clear(); + if (m_displayType & TypeCurrencies) + m_list += MyMoneyFile::instance()->currencyList(); + if (m_displayType & TypeSecurities) + m_list += MyMoneyFile::instance()->securityList(); // sort - qSort(d->m_list); + qSort(m_list); QList::ConstIterator it; // construct a transparent 16x16 pixmap static unsigned char empty_png[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1F, 0xF3, 0xFF, 0x61, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, 0x00, 0x06, 0x62, 0x4B, 0x47, 0x44, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xA0, 0xBD, 0xA7, 0x93, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0B, 0x13, 0x00, 0x00, 0x0B, 0x13, 0x01, 0x00, 0x9A, 0x9C, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4D, 0x45, 0x07, 0xDB, 0x07, 0x08, 0x0B, 0x16, 0x09, 0xAA, 0xA8, 0x50, 0x21, 0x00, 0x00, 0x00, 0x12, 0x49, 0x44, 0x41, 0x54, 0x38, 0xCB, 0x63, 0x60, 0x18, 0x05, 0xA3, 0x60, 0x14, 0x8C, 0x02, 0x08, 0x00, 0x00, 0x04, 0x10, 0x00, 0x01, 0x85, 0x3F, 0xAA, 0x72, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 }; QPixmap empty; empty.loadFromData(empty_png, sizeof(empty_png), 0, Qt::AutoColor); QIcon emptyIcon(empty); int itemId = 0; int m_selectedItemId = 0; - for (it = d->m_list.constBegin(); it != d->m_list.constEnd(); ++it) { + for (it = m_list.constBegin(); it != m_list.constEnd(); ++it) { QString display; - switch (d->m_displayItem) { + switch (m_displayItem) { default: case FullName: if ((*it).isCurrency()) { display = QString("%2 (%1)").arg((*it).id()).arg((*it).name()); } else display = QString("%2 (%1)").arg((*it).tradingSymbol()).arg((*it).name()); break; break; case Symbol: if ((*it).isCurrency()) display = (*it).id(); else display = (*it).tradingSymbol(); break; } if ((*it).id() == baseCurrency) { insertItem(itemId, QIcon::fromTheme(g_Icons[Icon::ViewBankAccount]), display); } else { insertItem(itemId, emptyIcon, display); } if (curr.id() == (*it).id()) { m_selectedItemId = itemId; - d->m_currency = (*it); + m_currency = (*it); } itemId++; } setCurrentIndex(m_selectedItemId); } const MyMoneySecurity& KMyMoneySecuritySelector::security() const { - Q_D(const KMyMoneySecuritySelector); int index = currentIndex(); - if ((0 <= index) && (index < d->m_list.size())) - return d->m_list[index]; + if ((0 <= index) && (index < m_list.size())) + return m_list[index]; else - return d->m_currency; + return m_currency; } void KMyMoneySecuritySelector::setSecurity(const MyMoneySecurity& currency) { - Q_D(KMyMoneySecuritySelector); - d->m_currency = currency; + m_currency = currency; update(QString("x")); } KMyMoneyCurrencySelector::KMyMoneyCurrencySelector(QWidget *parent) : - KMyMoneySecuritySelector(parent) + KMyMoneySecuritySelector(parent) { setDisplayType(TypeCurrencies); } - -KMyMoneyCurrencySelector::~KMyMoneyCurrencySelector() -{ -} diff --git a/kmymoney/widgets/kmymoneycurrencyselector.h b/kmymoney/widgets/kmymoneycurrencyselector.h index 665675b62..26e78601e 100644 --- a/kmymoney/widgets/kmymoneycurrencyselector.h +++ b/kmymoney/widgets/kmymoneycurrencyselector.h @@ -1,91 +1,89 @@ /*************************************************************************** kmymoneycurrencyselector.h - description ------------------- begin : Tue Apr 6 2004 copyright : (C) 2000-2004 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYCURRENCYSELECTOR_H #define KMYMONEYCURRENCYSELECTOR_H // ---------------------------------------------------------------------------- // QT Includes +#include + // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneysecurity.h" -class MyMoneySecurity; - /** * @author Thomas Baumgart */ -class KMyMoneySecuritySelectorPrivate; + class KMyMoneySecuritySelector : public KComboBox { - Q_OBJECT - Q_DISABLE_COPY(KMyMoneySecuritySelector) + Q_OBJECT Q_PROPERTY(MyMoneySecurity security READ security WRITE setSecurity DESIGNABLE false STORED false) - public: enum displayItemE { Symbol = 0, FullName }; enum displayTypeE { TypeCurrencies = 0x01, TypeSecurities = 0x02, TypeAll = 0x03 }; - explicit KMyMoneySecuritySelector(QWidget* parent = nullptr); + explicit KMyMoneySecuritySelector(QWidget *parent = 0); virtual ~KMyMoneySecuritySelector(); const MyMoneySecurity& security() const; void setSecurity(const MyMoneySecurity& currency); void selectDisplayItem(KMyMoneySecuritySelector::displayItemE item); void setDisplayType(displayTypeE type); void update(const QString& id); -protected: - KMyMoneySecuritySelectorPrivate * const d_ptr; - private: - Q_DECLARE_PRIVATE(KMyMoneySecuritySelector) + MyMoneySecurity m_currency; + displayItemE m_displayItem; + int m_selectedItemId; + bool m_displayOnly; + displayTypeE m_displayType; + QList m_list; }; class KMyMoneyCurrencySelector : public KMyMoneySecuritySelector { Q_OBJECT - Q_DISABLE_COPY(KMyMoneyCurrencySelector) - public: - explicit KMyMoneyCurrencySelector(QWidget* parent = nullptr); - ~KMyMoneyCurrencySelector() override; + KMyMoneyCurrencySelector(QWidget *parent = 0); + virtual ~KMyMoneyCurrencySelector() {} }; #endif diff --git a/kmymoney/widgets/kmymoneydateedit.h b/kmymoney/widgets/kmymoneydateedit.h index c47eb2697..b3789de7f 100644 --- a/kmymoney/widgets/kmymoneydateedit.h +++ b/kmymoney/widgets/kmymoneydateedit.h @@ -1,34 +1,34 @@ /*************************************************************************** kmymoneydateedit.h ------------------- copyright : (C) 2016 by Thomas Baumgart email : tbaumgart@kde.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYDATEEDIT_H #define KMYMONEYDATEEDIT_H // ---------------------------------------------------------------------------- // QT Includes #include // TODO: check if this class is really necessary class KMyMoneyDateEdit : public QDateEdit { Q_OBJECT public: - explicit KMyMoneyDateEdit(QWidget* parent = nullptr); + explicit KMyMoneyDateEdit(QWidget* parent = 0); }; #endif // KMYMONEYDATEEDIT_H diff --git a/kmymoney/widgets/kmymoneydateinput.h b/kmymoney/widgets/kmymoneydateinput.h index 0b9e6376b..0e815c346 100644 --- a/kmymoney/widgets/kmymoneydateinput.h +++ b/kmymoney/widgets/kmymoneydateinput.h @@ -1,150 +1,150 @@ /*************************************************************************** kmymoneydateinput.h ------------------- copyright : (C) 2000 by Michael Edwardes email : mte@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYDATEINPUT_H #define KMYMONEYDATEINPUT_H // ---------------------------------------------------------------------------- // QT Includes #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmm_widgets_export.h" // Ideas neatly taken from korganizer // Respective authors are credited. // Some ideas/code have been borrowed from Calendar-0.13 (phoenix.bmedesign.com/~qt) namespace KMyMoney { /** * Provided to be able to catch the focusOut events before the contents gets changed */ class OldDateEdit : public QDateEdit { Q_OBJECT public: - explicit OldDateEdit(const QDate& date, QWidget* parent = nullptr) : QDateEdit(date, parent) {} + explicit OldDateEdit(const QDate& date, QWidget *parent = 0) : QDateEdit(date, parent) {} protected: /** if the date was cleared (a state which is not supported by QDateEdit) * make sure that a date can be entered again */ virtual void keyPressEvent(QKeyEvent* k); /** reimplemented for internal reasons */ virtual bool event(QEvent* e); /** reimplemented for internal reasons */ virtual bool focusNextPrevChild(bool next); /** reimplemented for internal reasons */ virtual void focusInEvent(QFocusEvent *event); }; }; // namespace /** * This class provides the general widget used for date selection * throughout the KMyMoney project. It provides an QDateEdit widget * which is based on an edit field with spin boxes and adds a QPushButton * to open a KDatePicker. */ class KMM_WIDGETS_EXPORT kMyMoneyDateInput : public QWidget { Q_OBJECT Q_PROPERTY(QDate date READ date WRITE setDate STORED false) public: - explicit kMyMoneyDateInput(QWidget* parent = nullptr, Qt::AlignmentFlag flags = Qt::AlignLeft); + explicit kMyMoneyDateInput(QWidget *parent = 0, Qt::AlignmentFlag flags = Qt::AlignLeft); ~kMyMoneyDateInput(); /** * Returns the selected date in the widget. If the widget is not * showing a date, a QDate() object is returned which has an invalid date. */ QDate date() const; /** * Set the date shown in the widget to @a date. If @a date is invalid, * no text will be shown. The internal widget will use 1.1.1800 for this * special case, as the standard QDateEdit widget does not support an * invalid date as of Qt4 anymore, but we need it anyway for multi transaction * edit. */ void setDate(QDate date); void setMaximumDate(const QDate& max); /** * Setup the widget with @a date. This date is stored internally and * can be reloaded using resetDate(). * * @sa setDate, resetDate */ void loadDate(const QDate& date); /** * Setup the widget with the date loaded using loadDate(). * * @sa loadDate */ void resetDate(); QWidget* focusWidget() const; void setRange(const QDate & min, const QDate & max); void markAsBadDate(bool bad = false, const QColor& = QColor()); signals: void dateChanged(const QDate& date); protected: /** * - increments/decrements the date upon +/- key input * - increments/decrements the date upon Up/Down key input * - sets the date to current date when the 'T' key is pressed. * The actual key for this to happen might be overridden through * an i18n package. The 'T'-key is always possible. */ - void keyPressEvent(QKeyEvent* k) override; - void showEvent(QShowEvent* event) override; + void keyPressEvent(QKeyEvent* k); + void showEvent(QShowEvent* event); /** To intercept events sent to focusWidget() */ - bool eventFilter(QObject *o, QEvent *e) override; + bool eventFilter(QObject *o, QEvent *e); protected slots: void slotDateChosen(QDate date); void toggleDatePicker(); private slots: void slotDateChosenRef(const QDate& date); void fixSize(); private: struct Private; Private * const d; }; #endif diff --git a/kmymoney/widgets/kmymoneyedit.cpp b/kmymoney/widgets/kmymoneyedit.cpp index d97d7ef7f..f46a623ed 100644 --- a/kmymoney/widgets/kmymoneyedit.cpp +++ b/kmymoney/widgets/kmymoneyedit.cpp @@ -1,693 +1,588 @@ /*************************************************************************** kmymoneyedit.cpp ------------------- copyright : (C) 2000 by Michael Edwardes 2004 by Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kmymoneyedit.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneylineedit.h" #include "kmymoneycalculator.h" #include "mymoneymoney.h" #include "mymoneysecurity.h" #include "icons.h" using namespace Icons; kMyMoneyMoneyValidator::kMyMoneyMoneyValidator(QObject * parent) : - QDoubleValidator(parent) + QDoubleValidator(parent) { setLocale(QLocale::c()); } kMyMoneyMoneyValidator::kMyMoneyMoneyValidator(double bottom, double top, int decimals, - QObject * parent) : - QDoubleValidator(bottom, top, decimals, parent) + QObject * parent) : + QDoubleValidator(bottom, top, decimals, parent) { setLocale(QLocale::c()); } -kMyMoneyMoneyValidator::~kMyMoneyMoneyValidator() -{ -} - /* * The code of the following function is taken from kdeui/knumvalidator.cpp * and adjusted to always use the monetary symbols defined in the KDE System Settings */ QValidator::State kMyMoneyMoneyValidator::validate(QString & input, int & _p) const { Q_UNUSED(_p) QString s = input; // TODO: port this to kf5 #if 0 KLocale * l = KLocale::global(); // ok, we have to re-format the number to have: // 1. decimalSymbol == '.' // 2. negativeSign == '-' // 3. positiveSign == // 4. thousandsSeparator() == (we don't check that there // are exactly three decimals between each separator): QString d = l->monetaryDecimalSymbol(), - n = l->negativeSign(), - p = l->positiveSign(), - t = l->monetaryThousandsSeparator(); + n = l->negativeSign(), + p = l->positiveSign(), + t = l->monetaryThousandsSeparator(); // first, delete p's and t's: if (!p.isEmpty()) for (int idx = s.indexOf(p) ; idx >= 0 ; idx = s.indexOf(p, idx)) s.remove(idx, p.length()); if (!t.isEmpty()) for (int idx = s.indexOf(t) ; idx >= 0 ; idx = s.indexOf(t, idx)) s.remove(idx, t.length()); // then, replace the d's and n's if ((!n.isEmpty() && n.indexOf('.') != -1) || (!d.isEmpty() && d.indexOf('-') != -1)) { // make sure we don't replace something twice: qWarning() << "KDoubleValidator: decimal symbol contains '-' or " - "negative sign contains '.' -> improve algorithm" << endl; + "negative sign contains '.' -> improve algorithm" << endl; return Invalid; } if (!d.isEmpty() && d != ".") for (int idx = s.indexOf(d) ; idx >= 0 ; idx = s.indexOf(d, idx + 1)) s.replace(idx, d.length(), "."); if (!n.isEmpty() && n != "-") for (int idx = s.indexOf(n) ; idx >= 0 ; idx = s.indexOf(n, idx + 1)) s.replace(idx, n.length(), "-"); // Take care of monetary parens around the value if selected via // the locale settings. // If the lead-in or lead-out paren is present, remove it // before passing the string to the QDoubleValidator if (l->negativeMonetarySignPosition() == KLocale::ParensAround || l->positiveMonetarySignPosition() == KLocale::ParensAround) { QRegExp regExp("^(\\()?([\\d-\\.]*)(\\))?$"); if (s.indexOf(regExp) != -1) { s = regExp.cap(2); } } // check for non numeric values (QDoubleValidator allows an 'e', we don't) QRegExp nonNumeric("[^\\d-\\.]+"); if (s.indexOf(nonNumeric) != -1) return Invalid; // check for minus sign trailing the number QRegExp trailingMinus("^([^-]*)\\w*-$"); if (s.indexOf(trailingMinus) != -1) { s = QString("-%1").arg(trailingMinus.cap(1)); } // check for the maximum allowed number of decimal places int decPos = s.indexOf('.'); if (decPos != -1) { if (decimals() == 0) return Invalid; if (((int)(s.length()) - decPos) > decimals()) return Invalid; } // If we have just a single minus sign, we are done if (s == QString("-")) return Acceptable; QValidator::State rc = QDoubleValidator::validate(s, _p); if (rc == Acceptable) { // If the numeric value is acceptable, we check if the parens // are ok. If only the lead-in is present, the return value // is intermediate, if only the lead-out is present then it // definitely is invalid. Nevertheless, we check for parens // only, if the locale settings have it enabled. if (l->negativeMonetarySignPosition() == KLocale::ParensAround || l->positiveMonetarySignPosition() == KLocale::ParensAround) { int tmp = input.count('(') - input.count(')'); if (tmp > 0) rc = Intermediate; else if (tmp < 0) rc = Invalid; } } return rc; #else return Acceptable; #endif } + +int kMyMoneyEdit::standardPrecision = 2; + + +kMyMoneyEdit::kMyMoneyEdit(QWidget *parent, const int prec) + : QWidget(parent) +{ + m_prec = prec; + if (prec < -1 || prec > 20) + m_prec = standardPrecision; + init(); +} + +kMyMoneyEdit::kMyMoneyEdit(const MyMoneySecurity& sec, QWidget *parent) + : QWidget(parent) +{ + m_prec = MyMoneyMoney::denomToPrec(sec.smallestAccountFraction()); + init(); +} + +void kMyMoneyEdit::setStandardPrecision(int prec) +{ + if (prec >= 0 && prec < 20) { + standardPrecision = prec; + } +} + // converted image from kde3.5.1/share/apps/kdevdesignerpart/pics/designer_resetproperty.png static const uchar resetButtonImage[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x08, 0x06, 0x00, 0x00, 0x00, 0x0F, 0x0E, 0x84, 0x76, 0x00, 0x00, 0x00, 0x06, 0x62, 0x4B, 0x47, 0x44, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xA0, 0xBD, 0xA7, 0x93, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0B, 0x13, 0x00, 0x00, 0x0B, 0x13, 0x01, 0x00, 0x9A, 0x9C, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4D, 0x45, 0x07, 0xD6, 0x06, 0x10, 0x09, 0x36, 0x0C, 0x58, 0x91, 0x11, 0x7C, 0x00, 0x00, 0x00, 0x64, 0x49, 0x44, 0x41, 0x54, 0x78, 0xDA, 0x65, 0xC9, 0xA1, 0x0D, 0x02, 0x41, 0x18, 0x84, 0xD1, 0xF7, 0x5F, 0x13, 0x04, 0x9A, 0x39, 0x43, 0x68, 0x81, 0x02, 0x10, 0xB8, 0x13, 0x74, 0x80, 0xC1, 0x21, 0x76, 0x1D, 0xDD, 0xD0, 0x01, 0x65, 0x10, 0x34, 0x9A, 0x0C, 0x66, 0x83, 0x61, 0x92, 0x2F, 0x23, 0x5E, 0x25, 0x01, 0xBD, 0x6A, 0xC6, 0x1D, 0x9B, 0x25, 0x79, 0xC2, 0x34, 0xE0, 0x30, 0x00, 0x56, 0xBD, 0x6A, 0x0D, 0xD5, 0x38, 0xE1, 0xEA, 0x7F, 0xE7, 0x4A, 0xA2, 0x57, 0x1D, 0x71, 0xC1, 0x07, 0xBB, 0x81, 0x8F, 0x09, 0x96, 0xE4, 0x86, 0x3D, 0xDE, 0x78, 0x8D, 0x48, 0xF2, 0xAB, 0xB1, 0x1D, 0x9F, 0xC6, 0xFC, 0x05, 0x46, 0x68, 0x28, 0x6B, 0x58, 0xEE, 0x72, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 }; -class kMyMoneyEditPrivate +void kMyMoneyEdit::init() { - Q_DISABLE_COPY(kMyMoneyEditPrivate) - Q_DECLARE_PUBLIC(kMyMoneyEdit) - -public: - kMyMoneyEditPrivate(kMyMoneyEdit *qq) : - q_ptr(qq) - { - } - - ~kMyMoneyEditPrivate() - { - } - - void init() - { - Q_Q(kMyMoneyEdit); - QHBoxLayout *editLayout = new QHBoxLayout(q); - editLayout->setSpacing(0); - editLayout->setContentsMargins(0, 0, 0, 0); - - allowEmpty = false; - m_edit = new kMyMoneyLineEdit(q, true); - m_edit->installEventFilter(q); - q->setFocusProxy(m_edit); - editLayout->addWidget(m_edit); - - // Yes, just a simple double validator ! - kMyMoneyMoneyValidator *validator = new kMyMoneyMoneyValidator(q); - m_edit->setValidator(validator); - m_edit->setAlignment(Qt::AlignRight | Qt::AlignVCenter); - - m_calculatorFrame = new QWidget; - QVBoxLayout *calculatorFrameVBoxLayout = new QVBoxLayout(m_calculatorFrame); - calculatorFrameVBoxLayout->setMargin(0); - m_calculatorFrame->setWindowFlags(Qt::Popup); - - m_calculator = new kMyMoneyCalculator(m_calculatorFrame); - calculatorFrameVBoxLayout->addWidget(m_calculator); - m_calculatorFrame->hide(); + QHBoxLayout *editLayout = new QHBoxLayout(this); + editLayout->setSpacing(0); + editLayout->setContentsMargins(0, 0, 0, 0); - m_calcButton = new QPushButton(QIcon::fromTheme(g_Icons[Icon::AccessoriesCalculator]), QString(), q); - m_calcButton->setFocusProxy(m_edit); - editLayout->addWidget(m_calcButton); - - QPixmap pixmap; - pixmap.loadFromData(resetButtonImage, sizeof(resetButtonImage), "PNG", 0); - m_resetButton = new QPushButton(pixmap, QString(QString()), q); - m_resetButton->setEnabled(false); - m_resetButton->setFocusProxy(m_edit); - editLayout->addWidget(m_resetButton); - - KSharedConfigPtr kconfig = KSharedConfig::openConfig(); - KConfigGroup grp = kconfig->group("General Options"); - if (grp.readEntry("DontShowCalculatorButton", false) == true) - q->setCalculatorButtonVisible(false); - - q->connect(m_edit, SIGNAL(textChanged(QString)), q, SLOT(theTextChanged(QString))); - q->connect(m_calculator, SIGNAL(signalResultAvailable()), q, SLOT(slotCalculatorResult())); - q->connect(m_calcButton, SIGNAL(clicked()), q, SLOT(slotCalculatorOpen())); - q->connect(m_resetButton, SIGNAL(clicked()), q, SLOT(resetText())); - } + allowEmpty = false; + m_edit = new kMyMoneyLineEdit(this, true); + m_edit->installEventFilter(this); + setFocusProxy(m_edit); + editLayout->addWidget(m_edit); - /** - * This method ensures that the text version contains a - * fractional part. - */ - void ensureFractionalPart() - { - QString s(m_edit->text()); - ensureFractionalPart(s); - // by setting the text only when it's different then the one that it is already there - // we preserve the edit widget's state (like the selection for example) during a - // call to ensureFractionalPart() that does not change anything - if (s != m_edit->text()) - m_edit->setText(s); - } + // Yes, just a simple double validator ! + kMyMoneyMoneyValidator *validator = new kMyMoneyMoneyValidator(this); + m_edit->setValidator(validator); + m_edit->setAlignment(Qt::AlignRight | Qt::AlignVCenter); - /** - * Internal helper function for value() and ensureFractionalPart(). - */ - void ensureFractionalPart(QString& s) const - { - QString decimalSymbol = QLocale().decimalPoint(); - if (decimalSymbol.isEmpty()) - decimalSymbol = '.'; - - // If text contains no 'monetaryDecimalSymbol' then add it - // followed by the required number of 0s - if (!s.isEmpty()) { - if (m_prec > 0) { - if (!s.contains(decimalSymbol)) { - s += decimalSymbol; - for (auto i = 0; i < m_prec; i++) - s += '0'; - } - } else if (m_prec == 0) { - while (s.contains(decimalSymbol)) { - int pos = s.lastIndexOf(decimalSymbol); - if (pos != -1) { - s.truncate(pos); - } - } - } else if (s.contains(decimalSymbol)) { // m_prec == -1 && fraction - // no trailing zeroes - while (s.endsWith('0')) { - s.truncate(s.length() - 1); - } - // no trailing decimalSymbol - if (s.endsWith(decimalSymbol)) - s.truncate(s.length() - 1); - } - } - } + m_calculatorFrame = new QWidget; + QVBoxLayout *calculatorFrameVBoxLayout = new QVBoxLayout(m_calculatorFrame); + calculatorFrameVBoxLayout->setMargin(0); + m_calculatorFrame->setWindowFlags(Qt::Popup); - /** - * This method opens the calculator and replays the key - * event pointed to by @p ev. If @p ev is 0, then no key - * event is replayed. - * - * @param ev pointer to QKeyEvent that started the calculator. - */ - void calculatorOpen(QKeyEvent* k) - { - Q_Q(kMyMoneyEdit); - m_calculator->setInitialValues(m_edit->text(), k); - - int h = m_calculatorFrame->height(); - int w = m_calculatorFrame->width(); - - // usually, the calculator widget is shown underneath the MoneyEdit widget - // if it does not fit on the screen, we show it above this widget - QPoint p = q->mapToGlobal(QPoint(0, 0)); - if (p.y() + q->height() + h > QApplication::desktop()->height()) - p.setY(p.y() - h); - else - p.setY(p.y() + q->height()); - - // usually, it is shown left aligned. If it does not fit, we align it - // to the right edge of the widget - if (p.x() + w > QApplication::desktop()->width()) - p.setX(p.x() + q->width() - w); - - QRect r = m_calculator->geometry(); - r.moveTopLeft(p); - m_calculatorFrame->setGeometry(r); - m_calculatorFrame->show(); - m_calculator->setFocus(); - } + m_calculator = new kMyMoneyCalculator(m_calculatorFrame); + calculatorFrameVBoxLayout->addWidget(m_calculator); + m_calculatorFrame->hide(); - kMyMoneyEdit *q_ptr; - QString previousText; // keep track of what has been typed - QString m_text; // keep track of what was the original value - kMyMoneyCalculator* m_calculator; - QWidget* m_calculatorFrame; - kMyMoneyLineEdit* m_edit; - QPushButton* m_calcButton; - QPushButton* m_resetButton; - int m_prec; - bool allowEmpty; - - /** - * This holds the number of precision to be used - * when no other information (e.g. from account) - * is available. - * - * @sa setStandardPrecision() - */ - static int standardPrecision; -}; + m_calcButton = new QPushButton(QIcon::fromTheme(g_Icons[Icon::AccessoriesCalculator]), QString(), this); + m_calcButton->setFocusProxy(m_edit); + editLayout->addWidget(m_calcButton); -int kMyMoneyEditPrivate::standardPrecision = 2; + QPixmap pixmap; + pixmap.loadFromData(resetButtonImage, sizeof(resetButtonImage), "PNG", 0); + m_resetButton = new QPushButton(pixmap, QString(""), this); + m_resetButton->setEnabled(false); + m_resetButton->setFocusProxy(m_edit); + editLayout->addWidget(m_resetButton); -kMyMoneyEdit::kMyMoneyEdit(QWidget *parent, const int prec) : - QWidget(parent), - d_ptr(new kMyMoneyEditPrivate(this)) -{ - Q_D(kMyMoneyEdit); - d->m_prec = prec; - if (prec < -1 || prec > 20) - d->m_prec = kMyMoneyEditPrivate::standardPrecision; - d->init(); -} + KSharedConfigPtr kconfig = KSharedConfig::openConfig(); + KConfigGroup grp = kconfig->group("General Options"); + if (grp.readEntry("DontShowCalculatorButton", false) == true) + setCalculatorButtonVisible(false); -kMyMoneyEdit::kMyMoneyEdit(const MyMoneySecurity& sec, QWidget *parent) : - QWidget(parent), - d_ptr(new kMyMoneyEditPrivate(this)) -{ - Q_D(kMyMoneyEdit); - d->m_prec = MyMoneyMoney::denomToPrec(sec.smallestAccountFraction()); - d->init(); -} - -void kMyMoneyEdit::setStandardPrecision(int prec) -{ - if (prec >= 0 && prec < 20) { - kMyMoneyEditPrivate::standardPrecision = prec; - } + connect(m_edit, SIGNAL(textChanged(QString)), this, SLOT(theTextChanged(QString))); + connect(m_calculator, SIGNAL(signalResultAvailable()), this, SLOT(slotCalculatorResult())); + connect(m_calcButton, SIGNAL(clicked()), this, SLOT(slotCalculatorOpen())); + connect(m_resetButton, SIGNAL(clicked()), this, SLOT(resetText())); } void kMyMoneyEdit::setValidator(const QValidator* v) { - Q_D(kMyMoneyEdit); - d->m_edit->setValidator(v); + m_edit->setValidator(v); } kMyMoneyEdit::~kMyMoneyEdit() { - Q_D(kMyMoneyEdit); - delete d; } KLineEdit* kMyMoneyEdit::lineedit() const { - Q_D(const kMyMoneyEdit); - return d->m_edit; -} - -QString kMyMoneyEdit::text() const -{ - return value().toString(); -} - -void kMyMoneyEdit::setMinimumWidth(int w) -{ - Q_D(kMyMoneyEdit); - d->m_edit->setMinimumWidth(w); + return m_edit; } void kMyMoneyEdit::setPrecision(const int prec) { - Q_D(kMyMoneyEdit); if (prec >= -1 && prec <= 20) { - if (prec != d->m_prec) { - d->m_prec = prec; + if (prec != m_prec) { + m_prec = prec; // update current display setValue(value()); } } } -int kMyMoneyEdit::precision() const -{ - Q_D(const kMyMoneyEdit); - return d->m_prec; -} - bool kMyMoneyEdit::isValid() const { - Q_D(const kMyMoneyEdit); - return !(d->m_edit->text().isEmpty()); + return !(m_edit->text().isEmpty()); } MyMoneyMoney kMyMoneyEdit::value() const { - Q_D(const kMyMoneyEdit); - auto txt = d->m_edit->text(); - d->ensureFractionalPart(txt); + QString txt = m_edit->text(); + ensureFractionalPart(txt); MyMoneyMoney money(txt); - if (d->m_prec != -1) - money = money.convert(MyMoneyMoney::precToDenom(d->m_prec)); + if (m_prec != -1) + money = money.convert(MyMoneyMoney::precToDenom(m_prec)); return money; } void kMyMoneyEdit::setValue(const MyMoneyMoney& value) { - Q_D(kMyMoneyEdit); // load the value into the widget but don't use thousandsSeparators - auto txt = value.formatMoney(QString(), d->m_prec, false); + QString txt = value.formatMoney("", m_prec, false); loadText(txt); } void kMyMoneyEdit::loadText(const QString& txt) { - Q_D(kMyMoneyEdit); - d->m_edit->setText(txt); + m_edit->setText(txt); if (isEnabled() && !txt.isEmpty()) - d->ensureFractionalPart(); - d->m_text = d->m_edit->text(); - d->m_resetButton->setEnabled(false); + ensureFractionalPart(); + m_text = m_edit->text(); + m_resetButton->setEnabled(false); } void kMyMoneyEdit::clearText() { - Q_D(kMyMoneyEdit); - d->m_text.clear(); - d->m_edit->setText(d->m_text); -} - -void kMyMoneyEdit::setText(const QString& txt) -{ - setValue(MyMoneyMoney(txt)); + m_text.clear(); + m_edit->setText(m_text); } void kMyMoneyEdit::resetText() { - Q_D(kMyMoneyEdit); - d->m_edit->setText(d->m_text); - d->m_resetButton->setEnabled(false); + m_edit->setText(m_text); + m_resetButton->setEnabled(false); } void kMyMoneyEdit::theTextChanged(const QString & theText) { - Q_D(kMyMoneyEdit); - QString txt = QLocale().decimalPoint(); + QString d = QLocale().decimalPoint(); QString l_text = theText; QString nsign, psign; #if 0 KLocale * l = KLocale::global(); if (l->negativeMonetarySignPosition() == KLocale::ParensAround || l->positiveMonetarySignPosition() == KLocale::ParensAround) { nsign = psign = '('; } else { nsign = l->negativeSign(); psign = l->positiveSign(); } #else nsign = "-"; - psign = QString(); + psign = ""; #endif - auto i = 0; + int i = 0; if (isEnabled()) { - QValidator::State state = d->m_edit->validator()->validate(l_text, i); + QValidator::State state = m_edit->validator()->validate(l_text, i); if (state == QValidator::Intermediate) { if (l_text.length() == 1) { - if (l_text != txt && l_text != nsign && l_text != psign) + if (l_text != d && l_text != nsign && l_text != psign) state = QValidator::Invalid; } } if (state == QValidator::Invalid) - d->m_edit->setText(d->previousText); + m_edit->setText(previousText); else { - d->previousText = l_text; - emit textChanged(d->m_edit->text()); - d->m_resetButton->setEnabled(true); + previousText = l_text; + emit textChanged(m_edit->text()); + m_resetButton->setEnabled(true); } } } -bool kMyMoneyEdit::eventFilter(QObject * /* o */ , QEvent *event) +void kMyMoneyEdit::ensureFractionalPart() { - Q_D(kMyMoneyEdit); - auto rc = false; + QString s(m_edit->text()); + ensureFractionalPart(s); + // by setting the text only when it's different then the one that it is already there + // we preserve the edit widget's state (like the selection for example) during a + // call to ensureFractionalPart() that does not change anything + if (s != m_edit->text()) + m_edit->setText(s); +} + +void kMyMoneyEdit::ensureFractionalPart(QString& s) const +{ + QString decimalSymbol = QLocale().decimalPoint(); + if (decimalSymbol.isEmpty()) + decimalSymbol = '.'; + + // If text contains no 'monetaryDecimalSymbol' then add it + // followed by the required number of 0s + if (!s.isEmpty()) { + if (m_prec > 0) { + if (!s.contains(decimalSymbol)) { + s += decimalSymbol; + for (int i = 0; i < m_prec; i++) + s += '0'; + } + } else if (m_prec == 0) { + while (s.contains(decimalSymbol)) { + int pos = s.lastIndexOf(decimalSymbol); + if (pos != -1) { + s.truncate(pos); + } + } + } else if (s.contains(decimalSymbol)) { // m_prec == -1 && fraction + // no trailing zeroes + while (s.endsWith('0')) { + s.truncate(s.length() - 1); + } + // no trailing decimalSymbol + if (s.endsWith(decimalSymbol)) + s.truncate(s.length() - 1); + } + } +} + +bool kMyMoneyEdit::eventFilter(QObject * /* o */ , QEvent *e) +{ + bool rc = false; // we want to catch some keys that are usually handled by // the base class (e.g. '+', '-', etc.) - if (event->type() == QEvent::KeyPress) { - QKeyEvent *k = static_cast(event); + if (e->type() == QEvent::KeyPress) { + QKeyEvent *k = static_cast(e); rc = true; switch (k->key()) { case Qt::Key_Plus: case Qt::Key_Minus: - if (d->m_edit->hasSelectedText()) { - d->m_edit->cut(); + if (m_edit->hasSelectedText()) { + m_edit->cut(); } - if (d->m_edit->text().length() == 0) { + if (m_edit->text().length() == 0) { rc = false; break; } // in case of '-' we do not enter the calculator when // the current position is the beginning and there is // no '-' sign at the first position. if (k->key() == Qt::Key_Minus) { - if (d->m_edit->cursorPosition() == 0 && d->m_edit->text()[0] != '-') { + if (m_edit->cursorPosition() == 0 && m_edit->text()[0] != '-') { rc = false; break; } } // intentional fall through case Qt::Key_Slash: case Qt::Key_Asterisk: case Qt::Key_Percent: - if (d->m_edit->hasSelectedText()) { + if (m_edit->hasSelectedText()) { // remove the selected text - d->m_edit->cut(); + m_edit->cut(); } - d->calculatorOpen(k); + calculatorOpen(k); break; default: rc = false; break; } - } else if (event->type() == QEvent::FocusOut) { - if (!d->m_edit->text().isEmpty() || !d->allowEmpty) - d->ensureFractionalPart(); + } else if (e->type() == QEvent::FocusOut) { + if (!m_edit->text().isEmpty() || !allowEmpty) + ensureFractionalPart(); - if (MyMoneyMoney(d->m_edit->text()) != MyMoneyMoney(d->m_text) - && !d->m_calculator->isVisible()) { - emit valueChanged(d->m_edit->text()); + if (MyMoneyMoney(m_edit->text()) != MyMoneyMoney(m_text) + && !m_calculator->isVisible()) { + emit valueChanged(m_edit->text()); } - d->m_text = d->m_edit->text(); + m_text = m_edit->text(); } return rc; } void kMyMoneyEdit::slotCalculatorOpen() { - Q_D(kMyMoneyEdit); - d->calculatorOpen(0); + calculatorOpen(0); +} + +void kMyMoneyEdit::calculatorOpen(QKeyEvent* k) +{ + m_calculator->setInitialValues(m_edit->text(), k); + + int h = m_calculatorFrame->height(); + int w = m_calculatorFrame->width(); + + // usually, the calculator widget is shown underneath the MoneyEdit widget + // if it does not fit on the screen, we show it above this widget + QPoint p = mapToGlobal(QPoint(0, 0)); + if (p.y() + height() + h > QApplication::desktop()->height()) + p.setY(p.y() - h); + else + p.setY(p.y() + height()); + + // usually, it is shown left aligned. If it does not fit, we align it + // to the right edge of the widget + if (p.x() + w > QApplication::desktop()->width()) + p.setX(p.x() + width() - w); + + QRect r = m_calculator->geometry(); + r.moveTopLeft(p); + m_calculatorFrame->setGeometry(r); + m_calculatorFrame->show(); + m_calculator->setFocus(); } void kMyMoneyEdit::slotCalculatorResult() { - Q_D(kMyMoneyEdit); QString result; - if (d->m_calculator != 0) { - d->m_calculatorFrame->hide(); - d->m_edit->setText(d->m_calculator->result()); - d->ensureFractionalPart(); - emit valueChanged(d->m_edit->text()); - d->m_text = d->m_edit->text(); + if (m_calculator != 0) { + m_calculatorFrame->hide(); + m_edit->setText(m_calculator->result()); + ensureFractionalPart(); + emit valueChanged(m_edit->text()); + m_text = m_edit->text(); } } QWidget* kMyMoneyEdit::focusWidget() const { - Q_D(const kMyMoneyEdit); - QWidget* w = d->m_edit; + QWidget* w = m_edit; while (w->focusProxy()) w = w->focusProxy(); return w; } void kMyMoneyEdit::setCalculatorButtonVisible(const bool show) { - Q_D(kMyMoneyEdit); - d->m_calcButton->setVisible(show); + m_calcButton->setVisible(show); } void kMyMoneyEdit::setResetButtonVisible(const bool show) { - Q_D(kMyMoneyEdit); - d->m_resetButton->setVisible(show); + m_resetButton->setVisible(show); } void kMyMoneyEdit::setAllowEmpty(bool allowed) { - Q_D(kMyMoneyEdit); - d->allowEmpty = allowed; + allowEmpty = allowed; } bool kMyMoneyEdit::isCalculatorButtonVisible() const { - Q_D(const kMyMoneyEdit); - return d->m_calcButton->isVisible(); + return m_calcButton->isVisible(); } bool kMyMoneyEdit::isResetButtonVisible() const { - Q_D(const kMyMoneyEdit); - return d->m_resetButton->isVisible(); + return m_resetButton->isVisible(); } bool kMyMoneyEdit::isEmptyAllowed() const { - Q_D(const kMyMoneyEdit); - return d->allowEmpty; + return allowEmpty; } void kMyMoneyEdit::setPlaceholderText(const QString& hint) const { - Q_D(const kMyMoneyEdit); - if (d->m_edit) - d->m_edit->setPlaceholderText(hint); + if (m_edit) + m_edit->setPlaceholderText(hint); } bool kMyMoneyEdit::isReadOnly() const { - Q_D(const kMyMoneyEdit); - if (d->m_edit) - return d->m_edit->isReadOnly(); + if (m_edit) + return m_edit->isReadOnly(); return false; } void kMyMoneyEdit::setReadOnly(bool readOnly) { - Q_D(kMyMoneyEdit); // we use the QLineEdit::setReadOnly() method directly to avoid // changing the background between readonly and read/write mode // as it is done by the KLineEdit code. - if (d->m_edit) - d->m_edit->QLineEdit::setReadOnly(readOnly); //krazy:exclude=qclasses + if (m_edit) + m_edit->QLineEdit::setReadOnly(readOnly); //krazy:exclude=qclasses } diff --git a/kmymoney/widgets/kmymoneyedit.h b/kmymoney/widgets/kmymoneyedit.h index 523020b9d..37a585ff1 100644 --- a/kmymoney/widgets/kmymoneyedit.h +++ b/kmymoney/widgets/kmymoneyedit.h @@ -1,206 +1,257 @@ /*************************************************************************** kmymoneyedit.h ------------------- copyright : (C) 2000 by Michael Edwardes ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYEDIT_H #define KMYMONEYEDIT_H + + // ---------------------------------------------------------------------------- // QT Includes -#include -#include +#include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +#include "kmymoneylineedit.h" #include "mymoneymoney.h" #include "kmm_widgets_export.h" class kMyMoneyCalculator; class QPushButton; class QWidget; class KLineEdit; class MyMoneySecurity; /** * This class is derived from KDoubleValidator and uses * the monetary symbols instead of the numeric symbols. * Also, it always accepts localized input. * * @author Thomas Baumgart */ class KMM_WIDGETS_EXPORT kMyMoneyMoneyValidator : public QDoubleValidator { Q_OBJECT - Q_DISABLE_COPY(kMyMoneyMoneyValidator) public: /** * Constuct a locale-aware KDoubleValidator with default range * (whatever QDoubleValidator uses for that) and parent @p * parent */ - explicit kMyMoneyMoneyValidator(QObject * parent); + kMyMoneyMoneyValidator(QObject * parent); /** * Constuct a locale-aware KDoubleValidator for range [@p bottom,@p * top] and a precision of @p digits after the decimal * point. */ - explicit kMyMoneyMoneyValidator(double bottom, double top, int decimals, - QObject * parent); + kMyMoneyMoneyValidator(double bottom, double top, int decimals, + QObject * parent); /** * Destructs the validator. */ - ~kMyMoneyMoneyValidator(); + virtual ~kMyMoneyMoneyValidator() {} /** Overloaded for internal reasons. The API is not affected. */ - QValidator::State validate(QString & input, int & pos) const override; + virtual QValidator::State validate(QString & input, int & pos) const; }; /** * This class represents a widget to enter monetary values. * It has an edit field and a button to select a popup * calculator. The result of the calculator (if used) is * stored in the edit field. * * @author Michael Edwardes, Thomas Baumgart */ -class kMyMoneyEditPrivate; class KMM_WIDGETS_EXPORT kMyMoneyEdit : public QWidget { Q_OBJECT - Q_DISABLE_COPY(kMyMoneyEdit) Q_PROPERTY(bool calculatorButtonVisibility READ isCalculatorButtonVisible WRITE setCalculatorButtonVisible) Q_PROPERTY(bool resetButtonVisibility READ isResetButtonVisible WRITE setResetButtonVisible) Q_PROPERTY(bool allowEmpty READ isEmptyAllowed WRITE setAllowEmpty) Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) Q_PROPERTY(MyMoneyMoney value READ value WRITE setValue DESIGNABLE false STORED false USER true) Q_PROPERTY(bool valid READ isValid DESIGNABLE false STORED false) +private: + QString previousText; // keep track of what has been typed + QString m_text; // keep track of what was the original value + kMyMoneyCalculator* m_calculator; + QWidget* m_calculatorFrame; + kMyMoneyLineEdit* m_edit; + QPushButton* m_calcButton; + QPushButton* m_resetButton; + int m_prec; + bool allowEmpty; + + /** + * This holds the number of precision to be used + * when no other information (e.g. from account) + * is available. + * + * @sa setStandardPrecision() + */ + static int standardPrecision; + +private: + /** + * Internal helper function for value() and ensureFractionalPart(). + */ + void ensureFractionalPart(QString& txt) const; + +protected: + /** + * This method ensures that the text version contains a + * fractional part. + */ + void ensureFractionalPart(); + + /** + * This method opens the calculator and replays the key + * event pointed to by @p ev. If @p ev is 0, then no key + * event is replayed. + * + * @param ev pointer to QKeyEvent that started the calculator. + */ + void calculatorOpen(QKeyEvent* ev); + + /** + * Helper method for constructors. + */ + void init(); + +protected slots: + void theTextChanged(const QString & text); + void slotCalculatorResult(); + void slotCalculatorOpen(); + public: - explicit kMyMoneyEdit(QWidget* parent = nullptr, const int prec = -2); - explicit kMyMoneyEdit(const MyMoneySecurity& eq, QWidget* parent = nullptr); + explicit kMyMoneyEdit(QWidget *parent = 0, const int prec = -2); + explicit kMyMoneyEdit(const MyMoneySecurity& eq, QWidget *parent = 0); ~kMyMoneyEdit(); MyMoneyMoney value() const; void setValue(const MyMoneyMoney& value); bool isValid() const; - virtual bool eventFilter(QObject *watched, QEvent *event) override; + virtual bool eventFilter(QObject * , QEvent *); /** * This method returns the value of the edit field in "numerator/denominator" format. * If you want to get the text of the edit field, use lineedit()->text() instead. */ - QString text() const; - void setMinimumWidth(int w); + QString text() const { + return value().toString(); + }; + + void setMinimumWidth(int w) { + m_edit->setMinimumWidth(w); + }; /** * Set the number of fractional digits that should be shown * * @param prec number of fractional digits. * * @note should be used prior to calling loadText() * @sa precision */ void setPrecision(const int prec); /** * return the number of fractional digits * @sa setPrecision */ - int precision() const; + int precision() { + return m_prec; + }; QWidget* focusWidget() const; /** * This method allows to modify the behavior of the widget * such that it accepts an empty value (all blank) or not. * The default is to not accept an emtpy input and to * convert an empty field into 0.00 upon loss of focus. * * @param allowed if @a true, empty input is allowed, if @a false * emtpy input will be converted to 0.00 */ void setAllowEmpty(bool allowed = true); /** Overloaded for internal reasons. The API is not affected. */ void setValidator(const QValidator* v); bool isCalculatorButtonVisible() const; bool isResetButtonVisible() const; bool isEmptyAllowed() const; KLineEdit* lineedit() const; void setPlaceholderText(const QString& hint) const; bool isReadOnly() const; /** * This allows to setup the standard precision (number of decimal places) * to be used when no other information is available. @a prec must be in * the range of 0..19. If never set, the default precision is 2. */ static void setStandardPrecision(int prec); public slots: void loadText(const QString& text); void resetText(); void clearText(); - void setText(const QString& txt); + void setText(const QString& txt) { + setValue(MyMoneyMoney(txt)); + }; /** * This method allows to show/hide the calculator button of the widget. * The parameter @p show controls the behavior. Default is to show the * button. * * @param show if true, button is shown, if false it is hidden */ void setCalculatorButtonVisible(const bool show); void setResetButtonVisible(const bool show); void setReadOnly(bool readOnly); signals: // Signals /** * This signal is sent, when the focus leaves this widget and * the amount has been changed by user during this session. */ void valueChanged(const QString& text); void textChanged(const QString& text); - -protected slots: - void theTextChanged(const QString & text); - void slotCalculatorResult(); - void slotCalculatorOpen(); - -private: - kMyMoneyEditPrivate * const d_ptr; - Q_DECLARE_PRIVATE(kMyMoneyEdit) }; #endif diff --git a/kmymoney/widgets/kmymoneyfrequencycombo.cpp b/kmymoney/widgets/kmymoneyfrequencycombo.cpp deleted file mode 100644 index 3f67b7d05..000000000 --- a/kmymoney/widgets/kmymoneyfrequencycombo.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/*************************************************************************** - kmymoneyfrequencycombo.cpp - description - ------------------- - begin : Sat Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "kmymoneyfrequencycombo.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -#include - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "mymoneyschedule.h" -#include "mymoneyenums.h" - -using namespace eMyMoney; - -KMyMoneyFrequencyCombo::KMyMoneyFrequencyCombo(QWidget* parent) : - KMyMoneyOccurrenceCombo(parent) -{ - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::Once).toLatin1()), (int)Schedule::Occurrence::Once); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::Daily).toLatin1()), (int)Schedule::Occurrence::Daily); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::Weekly).toLatin1()), (int)Schedule::Occurrence::Weekly); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryOtherWeek).toLatin1()), (int)Schedule::Occurrence::EveryOtherWeek); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryHalfMonth).toLatin1()), (int)Schedule::Occurrence::EveryHalfMonth); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryThreeWeeks).toLatin1()), (int)Schedule::Occurrence::EveryThreeWeeks); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryThirtyDays).toLatin1()), (int)Schedule::Occurrence::EveryThirtyDays); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryFourWeeks).toLatin1()), (int)Schedule::Occurrence::EveryFourWeeks); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::Monthly).toLatin1()), (int)Schedule::Occurrence::Monthly); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryEightWeeks).toLatin1()), (int)Schedule::Occurrence::EveryEightWeeks); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryOtherMonth).toLatin1()), (int)Schedule::Occurrence::EveryOtherMonth); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryThreeMonths).toLatin1()), (int)Schedule::Occurrence::EveryThreeMonths); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryFourMonths).toLatin1()), (int)Schedule::Occurrence::EveryFourMonths); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::TwiceYearly).toLatin1()), (int)Schedule::Occurrence::TwiceYearly); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::Yearly).toLatin1()), (int)Schedule::Occurrence::Yearly); - addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryOtherYear).toLatin1()), (int)Schedule::Occurrence::EveryOtherYear); - - connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentDataChanged())); -} - -KMyMoneyFrequencyCombo::~KMyMoneyFrequencyCombo() -{ -} - -int KMyMoneyFrequencyCombo::daysBetweenEvents() const -{ - return MyMoneySchedule::daysBetweenEvents(currentItem()); -} - -int KMyMoneyFrequencyCombo::eventsPerYear() const -{ - return MyMoneySchedule::eventsPerYear(currentItem()); -} - -QVariant KMyMoneyFrequencyCombo::currentData() const -{ - return itemData(currentIndex(), Qt::UserRole); -} - -void KMyMoneyFrequencyCombo::setCurrentData(QVariant data) -{ - setItemData(currentIndex(), data, Qt::UserRole); -} - -void KMyMoneyFrequencyCombo::slotCurrentDataChanged() -{ - emit currentDataChanged(currentData()); -} diff --git a/kmymoney/widgets/kmymoneyfrequencycombo.h b/kmymoney/widgets/kmymoneyfrequencycombo.h deleted file mode 100644 index 5ad74a3d6..000000000 --- a/kmymoney/widgets/kmymoneyfrequencycombo.h +++ /dev/null @@ -1,78 +0,0 @@ -/*************************************************************************** - kmymoneyfrequencycombo.h - description - ------------------- - begin : Mon Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KMYMONEYFREQUENCYCOMBO_H -#define KMYMONEYFREQUENCYCOMBO_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmymoneyoccurrencecombo.h" - -/** - * This class implements a payment frequency selector - * @author Thomas Baumgart - */ -class KMM_WIDGETS_EXPORT KMyMoneyFrequencyCombo : public KMyMoneyOccurrenceCombo -{ - Q_OBJECT - Q_DISABLE_COPY(KMyMoneyFrequencyCombo) - Q_PROPERTY(QVariant data READ currentData WRITE setCurrentData STORED false) - -public: - explicit KMyMoneyFrequencyCombo(QWidget* parent = nullptr); - ~KMyMoneyFrequencyCombo() override; - - /** - * This method returns the number of events for the selected payment - * frequency (eg for yearly the return value is 1 and for monthly it - * is 12). In case, the frequency cannot be converted (once, every other year, etc.) - * the method returns 0. - */ - int eventsPerYear() const; - /** - * This method returns the number of days between two events of - * the selected frequency. The return value for months is based - * on 30 days and the year is 360 days long. - */ - int daysBetweenEvents() const; - - QVariant currentData() const; - - void setCurrentData(QVariant data); - -Q_SIGNALS: - void currentDataChanged(QVariant data); - -protected slots: - void slotCurrentDataChanged(); - -private: - QVariant data; - -}; - -#endif diff --git a/kmymoney/widgets/kmymoneygeneralcombo.cpp b/kmymoney/widgets/kmymoneygeneralcombo.cpp deleted file mode 100644 index b0186c413..000000000 --- a/kmymoney/widgets/kmymoneygeneralcombo.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/*************************************************************************** - kmymoneygeneralcombo.cpp - description - ------------------- - begin : Sat Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "kmymoneygeneralcombo.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -KMyMoneyGeneralCombo::KMyMoneyGeneralCombo(QWidget* w) : - KComboBox(w) -{ - connect(this, SIGNAL(highlighted(int)), this, SLOT(slotChangeItem(int))); -} - -KMyMoneyGeneralCombo::~KMyMoneyGeneralCombo() -{ -} - -void KMyMoneyGeneralCombo::setCurrentItem(int id) -{ - setCurrentIndex(findData(QVariant(id), Qt::UserRole, Qt::MatchExactly)); -} - -int KMyMoneyGeneralCombo::currentItem() const -{ - return itemData(currentIndex()).toInt(); -} - -void KMyMoneyGeneralCombo::clear() -{ - KComboBox::clear(); -} - -void KMyMoneyGeneralCombo::insertItem(const QString& txt, int id, int idx) -{ - KComboBox::insertItem(idx, txt, QVariant(id)); -} - -void KMyMoneyGeneralCombo::removeItem(int id) -{ - KComboBox::removeItem(findData(QVariant(id), Qt::UserRole, Qt::MatchExactly)); -} - -void KMyMoneyGeneralCombo::slotChangeItem(int idx) -{ - emit itemSelected(itemData(idx).toInt()); -} diff --git a/kmymoney/widgets/kmymoneygeneralcombo.h b/kmymoney/widgets/kmymoneygeneralcombo.h deleted file mode 100644 index f07168665..000000000 --- a/kmymoney/widgets/kmymoneygeneralcombo.h +++ /dev/null @@ -1,69 +0,0 @@ -/*************************************************************************** - kmymoneygeneralcombo.h - description - ------------------- - begin : Mon Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KMYMONEYGENERALCOMBO_H -#define KMYMONEYGENERALCOMBO_H - -#include "kmm_widgets_export.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -class KMM_WIDGETS_EXPORT KMyMoneyGeneralCombo : public KComboBox -{ - Q_OBJECT - Q_DISABLE_COPY(KMyMoneyGeneralCombo) - Q_PROPERTY(int currentItem READ currentItem WRITE setCurrentItem STORED false) - -public: - explicit KMyMoneyGeneralCombo(QWidget* parent = nullptr); - virtual ~KMyMoneyGeneralCombo(); - - void insertItem(const QString& txt, int id, int idx = -1); - - void setCurrentItem(int id); - int currentItem() const; - - void removeItem(int id); - -public slots: - void clear(); - -signals: - void itemSelected(int id); - -protected: - // prevent the caller to use the standard KComboBox insertItem function with a default idx - void insertItem(const QString&); - -protected slots: - void slotChangeItem(int idx); - -}; - -#endif diff --git a/kmymoney/widgets/kmymoneymvccombo.cpp b/kmymoney/widgets/kmymoneymvccombo.cpp index b94a906fb..88e882900 100644 --- a/kmymoney/widgets/kmymoneymvccombo.cpp +++ b/kmymoney/widgets/kmymoneymvccombo.cpp @@ -1,311 +1,820 @@ /*************************************************************************** kmymoneymvccombo.cpp - description ------------------- begin : Sat Jan 09 2010 copyright : (C) 2010 by Thomas Baumgart Cristian Onet Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kmymoneymvccombo.h" -#include "kmymoneymvccombo_p.h" // ---------------------------------------------------------------------------- // QT Includes +#include +#include #include +#include #include +#include +#include +#include +#include +#include +#include #include #include -#include -#include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes +#include "icons/icons.h" +#include "mymoneyschedule.h" +#include "mymoneypayee.h" +#include "mymoneytag.h" +#include "mymoneytransactionfilter.h" -KMyMoneyMVCCombo::KMyMoneyMVCCombo(QWidget* parent) : - KComboBox(parent), - d_ptr(new KMyMoneyMVCComboPrivate) -{ - view()->setAlternatingRowColors(true); - connect(this, SIGNAL(activated(int)), SLOT(activated(int))); -} +using namespace Icons; +using namespace eMyMoney; -KMyMoneyMVCCombo::KMyMoneyMVCCombo(bool editable, QWidget* parent) : - KComboBox(editable, parent), - d_ptr(new KMyMoneyMVCComboPrivate) +class KMyMoneyMVCCombo::Private { - Q_D(KMyMoneyMVCCombo); - d->m_completer = new QCompleter(this); - d->m_completer->setCaseSensitivity(Qt::CaseInsensitive); - d->m_completer->setModel(model()); - setCompleter(d->m_completer); +public: + Private() : + m_canCreateObjects(false), + m_inFocusOutEvent(false), + m_completer(0) {} - // setSubstringSearch(!KMyMoneySettings::stringMatchFromStart()); + /** + * Flag to control object creation. Use + * KMyMoneyMVCCombo::setSuppressObjectCreation() + * to modify it's setting. Defaults to @a false. + */ + bool m_canCreateObjects; - view()->setAlternatingRowColors(true); - setInsertPolicy(QComboBox::NoInsert); // don't insert new objects due to object creation - connect(this, SIGNAL(activated(int)), SLOT(activated(int))); -} + /** + * Flag to check whether a focusOutEvent processing is underway or not + */ + bool m_inFocusOutEvent; -KMyMoneyMVCCombo::KMyMoneyMVCCombo(KMyMoneyMVCComboPrivate &dd, QWidget* parent) : - KComboBox(parent), - d_ptr(&dd) + QCompleter *m_completer; +}; + + +KMyMoneyMVCCombo::KMyMoneyMVCCombo(QWidget* parent) : + KComboBox(parent), + d(new Private) { view()->setAlternatingRowColors(true); connect(this, SIGNAL(activated(int)), SLOT(activated(int))); } -KMyMoneyMVCCombo::KMyMoneyMVCCombo(KMyMoneyMVCComboPrivate &dd, bool editable, QWidget* parent) : - KComboBox(editable, parent), - d_ptr(&dd) +KMyMoneyMVCCombo::KMyMoneyMVCCombo(bool editable, QWidget* parent) : + KComboBox(editable, parent), + d(new Private) { - Q_D(KMyMoneyMVCCombo); d->m_completer = new QCompleter(this); d->m_completer->setCaseSensitivity(Qt::CaseInsensitive); d->m_completer->setModel(model()); setCompleter(d->m_completer); // setSubstringSearch(!KMyMoneySettings::stringMatchFromStart()); view()->setAlternatingRowColors(true); setInsertPolicy(QComboBox::NoInsert); // don't insert new objects due to object creation connect(this, SIGNAL(activated(int)), SLOT(activated(int))); } KMyMoneyMVCCombo::~KMyMoneyMVCCombo() { - Q_D(KMyMoneyMVCCombo); delete d; } void KMyMoneyMVCCombo::setEditable(bool editable) { - Q_D(KMyMoneyMVCCombo); KComboBox::setEditable(editable); if(editable) { if(!d->m_completer) { d->m_completer = new QCompleter(this); d->m_completer->setCaseSensitivity(Qt::CaseInsensitive); d->m_completer->setModel(model()); } setCompleter(d->m_completer); } } void KMyMoneyMVCCombo::setSubstringSearch(bool enabled) { - Q_D(KMyMoneyMVCCombo); d->m_completer->setCompletionMode(QCompleter::PopupCompletion); d->m_completer->setModel(model()); d->m_completer->setFilterMode(enabled ? Qt::MatchContains : Qt::MatchStartsWith); } void KMyMoneyMVCCombo::setSubstringSearchForChildren(QWidget*const widget, bool enabled) { Q_CHECK_PTR(widget); QList comboList; comboList = widget->findChildren(); foreach (KMyMoneyMVCCombo *combo, comboList) { combo->setSubstringSearch(enabled); } } void KMyMoneyMVCCombo::setPlaceholderText(const QString& hint) const { KLineEdit* le = qobject_cast(lineEdit()); if (le) { le->setPlaceholderText(hint); } } const QString& KMyMoneyMVCCombo::selectedItem() const { - Q_D(const KMyMoneyMVCCombo); QVariant data = itemData(currentIndex()); if (data.isValid()) - d->m_id = data.toString(); + m_id = data.toString(); else - d->m_id.clear(); - return d->m_id; + m_id.clear(); + return m_id; } void KMyMoneyMVCCombo::setSelectedItem(const QString& id) { - Q_D(KMyMoneyMVCCombo); - d->m_id = id; - setCurrentIndex(findData(QVariant(d->m_id))); + m_id = id; + setCurrentIndex(findData(QVariant(m_id))); } void KMyMoneyMVCCombo::activated(int index) { - Q_D(KMyMoneyMVCCombo); QVariant data = itemData(index); if (data.isValid()) { - d->m_id = data.toString(); - emit itemSelected(d->m_id); + m_id = data.toString(); + emit itemSelected(m_id); } } void KMyMoneyMVCCombo::connectNotify(const QMetaMethod & signal) { - Q_D(KMyMoneyMVCCombo); if (signal != QMetaMethod::fromSignal(&KMyMoneyMVCCombo::createItem)) { d->m_canCreateObjects = true; } } void KMyMoneyMVCCombo::disconnectNotify(const QMetaMethod & signal) { - Q_D(KMyMoneyMVCCombo); if (signal != QMetaMethod::fromSignal(&KMyMoneyMVCCombo::createItem)) { d->m_canCreateObjects = false; } } -void KMyMoneyMVCCombo::setCurrentText(const QString& txt) -{ - KComboBox::setItemText(KComboBox::currentIndex(), txt); -} - -void KMyMoneyMVCCombo::setCurrentText() -{ - KComboBox::setItemText(KComboBox::currentIndex(), QString()); -} - - void KMyMoneyMVCCombo::focusOutEvent(QFocusEvent* e) { - Q_D(KMyMoneyMVCCombo); // when showing m_completion we'll receive a focus out event even if the focus // will still remain at this widget since this widget is the completion's focus proxy // so ignore the focus out event caused by showin a widget of type Qt::Popup if (e->reason() == Qt::PopupFocusReason) return; if (d->m_inFocusOutEvent) { KComboBox::focusOutEvent(e); return; } //check if the focus went to a widget in TransactionFrom or in the Register if (e->reason() == Qt::MouseFocusReason) { QObject *w = this->parent(); QObject *q = qApp->focusWidget()->parent(); // KMyMoneyTagCombo is inside KTagContainer, KMyMoneyPayeeCombo isn't it if (w->inherits("KTagContainer")) w = w->parent(); while (q && q->objectName() != "qt_scrollarea_viewport") q = q->parent(); if (q != w && qApp->focusWidget()->objectName() != "register") { KComboBox::focusOutEvent(e); return; } } d->m_inFocusOutEvent = true; if (isEditable() && !currentText().isEmpty() && e->reason() != Qt::ActiveWindowFocusReason) { if (d->m_canCreateObjects) { // in case we tab out, we make sure that if the current completion // contains the current text that we set the current text to // the full completion text but only if the completion box is visible. // BUG 254984 is resolved with the visbility check if (e->reason() != Qt::MouseFocusReason) { if (d->m_completer->popup() && d->m_completer->popup()->isVisible() && d->m_completer->currentCompletion().contains(currentText(), Qt::CaseInsensitive)) { lineEdit()->setText(d->m_completer->currentCompletion()); } } //check if the current text is contained in the internal list, if not ask the user if want to create a new item. checkCurrentText(); // else if we cannot create objects, and the current text is not // in the list, then we clear the text and the selection. } else if (!contains(currentText())) { clearEditText(); } //this is to cover the case when you highlight an item but don't activate it with Enter if (currentText() != itemText(currentIndex())) { setCurrentIndex(findText(currentText(), Qt::MatchExactly)); emit activated(currentIndex()); } } KComboBox::focusOutEvent(e); // force update of hint and id if there is no text in the widget if (isEditable() && currentText().isEmpty()) { - QString id = d->m_id; - d->m_id.clear(); + QString id = m_id; + m_id.clear(); if (!id.isEmpty()) - emit itemSelected(d->m_id); + emit itemSelected(m_id); update(); } d->m_inFocusOutEvent = false; // This is used only be KMyMoneyTagCombo at this time emit lostFocus(); } void KMyMoneyMVCCombo::checkCurrentText() { - Q_D(KMyMoneyMVCCombo); if (!contains(currentText())) { QString id; // annouce that we go into a possible dialog to create an object // This can be used by upstream widgets to disable filters etc. emit objectCreation(true); emit createItem(currentText(), id); // Announce that we return from object creation emit objectCreation(false); // update the field to a possibly created object - d->m_id = id; + m_id = id; setCurrentTextById(id); } } void KMyMoneyMVCCombo::setCurrentTextById(const QString& id) { clearEditText(); if (!id.isEmpty()) { int index = findData(QVariant(id), Qt::UserRole, Qt::MatchExactly); if (index > -1) { setCompletedText(itemText(index)); setEditText(itemText(index)); setCurrentIndex(index); } } } void KMyMoneyMVCCombo::protectItem(int id, bool protect) { QStandardItemModel* standardModel = qobject_cast (model()); QStandardItem* standardItem = standardModel->item(id); standardItem->setSelectable(!protect); } +KMyMoneyPayeeCombo::KMyMoneyPayeeCombo(QWidget* parent) : + KMyMoneyMVCCombo(true, parent) +{ +} + +void KMyMoneyPayeeCombo::loadPayees(const QList& list) +{ + clear(); + + //add a blank item, since the field is optional + addItem(QString(), QVariant(QString())); + + //add all payees + QList::const_iterator it; + for (it = list.constBegin(); it != list.constEnd(); ++it) { + addItem((*it).name(), QVariant((*it).id())); + } + + //sort the model, which will sort the list in the combo + model()->sort(Qt::DisplayRole, Qt::AscendingOrder); + + //set the text to empty and the index to the first item on the list + setCurrentIndex(0); + clearEditText(); +} + +KMyMoneyTagCombo::KMyMoneyTagCombo(QWidget* parent) : + KMyMoneyMVCCombo(true, parent) +{ +} + +void KMyMoneyTagCombo::loadTags(const QList& list) +{ + clear(); + + //add a blank item, since the field is optional + addItem(QString(), QVariant(QString())); + + //add all not closed tags + QList::const_iterator it; + for (it = list.constBegin(); it != list.constEnd(); ++it) { + if (!(*it).isClosed()) + addItem((*it).name(), QVariant((*it).id())); + else { + m_closedIdList.append((*it).id()); + m_closedTagNameList.append((*it).name()); + } + } + + //sort the model, which will sort the list in the combo + model()->sort(Qt::DisplayRole, Qt::AscendingOrder); + + //set the text to empty and the index to the first item on the list + setCurrentIndex(0); + clearEditText(); +} + +void KMyMoneyTagCombo::setUsedTagList(QList& usedIdList, QList& usedTagNameList) +{ + m_usedIdList = usedIdList; + m_usedTagNameList = usedTagNameList; + for (int i = 0; i < m_usedIdList.size(); ++i) { + int index = findData(QVariant(m_usedIdList.at(i)), Qt::UserRole, Qt::MatchExactly); + if (index != -1) removeItem(index); + } +} + +void KMyMoneyTagCombo::checkCurrentText() +{ + if (!contains(currentText())) { + if (m_closedTagNameList.contains(currentText())) { + // Tell the user what's happened + QString msg = QString("") + i18n("Closed tags cannot be used.") + QString(""); + KMessageBox::information(this, msg, i18n("Closed tag"), "Closed tag"); + setCurrentText(); + return; + } else if (m_usedTagNameList.contains(currentText())) { + // Tell the user what's happened + QString msg = QString("") + i18n("The tag is already present.") + QString(""); + KMessageBox::information(this, msg, i18n("Duplicate tag"), "Duplicate tag"); + setCurrentText(); + return; + } + QString id; + // annouce that we go into a possible dialog to create an object + // This can be used by upstream widgets to disable filters etc. + emit objectCreation(true); + + emit createItem(currentText(), id); + + // Announce that we return from object creation + emit objectCreation(false); + + // update the field to a possibly created object + //m_id = id; + setCurrentTextById(id); + } +} + +KTagLabel::KTagLabel(const QString& id, const QString& name, QWidget* parent) : + QFrame(parent) +{ + QToolButton *t = new QToolButton(this); + t->setIcon(QIcon::fromTheme(g_Icons[Icon::DialogClose])); + t->setAutoRaise(true); + QLabel *l = new QLabel(name, this); + m_tagId = id; + QHBoxLayout *layout = new QHBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + this->setLayout(layout); + layout->addWidget(t); + layout->addWidget(l); + connect(t, SIGNAL(clicked(bool)), this, SIGNAL(clicked(bool))); + //this->setFrameStyle(QFrame::Panel | QFrame::Plain); +} + +KTagContainer::KTagContainer(QWidget* parent) : + QWidget(parent) +{ + m_tagCombo = new KMyMoneyTagCombo; + QHBoxLayout *layout = new QHBoxLayout; + layout->setContentsMargins(0, 0, 5, 0); + layout->setSpacing(0); + layout->addWidget(m_tagCombo, 100); + this->setLayout(layout); + this->setFocusProxy(m_tagCombo); + connect(m_tagCombo, SIGNAL(lostFocus()), this, SLOT(slotAddTagWidget())); +} + +void KTagContainer::loadTags(const QList& list) +{ + m_list = list; + m_tagCombo->loadTags(list); +} + +const QList KTagContainer::selectedTags() +{ + return m_tagIdList; +} + +void KTagContainer::addTagWidget(const QString& id) +{ + if (id.isNull() || m_tagIdList.contains(id)) + return; + const QString tagName = m_tagCombo->itemText(m_tagCombo->findData(QVariant(id), Qt::UserRole, Qt::MatchExactly)); + KTagLabel *t = new KTagLabel(id, tagName, this); + connect(t, SIGNAL(clicked(bool)), this, SLOT(slotRemoveTagWidget())); + m_tagLabelList.append(t); + m_tagNameList.append(tagName); + m_tagIdList.append(id); + this->layout()->addWidget(t); + m_tagCombo->loadTags(m_list); + m_tagCombo->setUsedTagList(m_tagIdList, m_tagNameList); + m_tagCombo->setCurrentIndex(0); + m_tagCombo->setFocus(); +} + +void KTagContainer::RemoveAllTagWidgets() +{ + m_tagIdList.clear(); + m_tagNameList.clear(); + while (!m_tagLabelList.isEmpty()) + delete m_tagLabelList.takeLast(); + m_tagCombo->loadTags(m_list); + m_tagCombo->setUsedTagList(m_tagIdList, m_tagNameList); + m_tagCombo->setCurrentIndex(0); +} + +void KTagContainer::slotAddTagWidget() +{ + addTagWidget(m_tagCombo->selectedItem()); +} + +void KTagContainer::slotRemoveTagWidget() +{ + this->tagCombo()->setFocus(); + KTagLabel *t = (KTagLabel *)sender(); + int index = m_tagLabelList.indexOf(t); + m_tagLabelList.removeAt(index); + m_tagIdList.removeAt(index); + m_tagNameList.removeAt(index); + delete t; + m_tagCombo->loadTags(m_list); + m_tagCombo->setUsedTagList(m_tagIdList, m_tagNameList); + m_tagCombo->setCurrentIndex(0); +} + +KMyMoneyReconcileCombo::KMyMoneyReconcileCombo(QWidget* w) : + KMyMoneyMVCCombo(false, w) +{ + // add the items in reverse order of appearance (see KMyMoneySelector::newItem() for details) + addItem(i18n("Reconciled"), QVariant("R")); + addItem(i18nc("Reconciliation state 'Cleared'", "Cleared"), QVariant("C")); + addItem(i18n("Not reconciled"), QVariant(" ")); + addItem(" ", QVariant("U")); + + connect(this, SIGNAL(itemSelected(QString)), this, SLOT(slotSetState(QString))); +} + +void KMyMoneyReconcileCombo::slotSetState(const QString& state) +{ + setSelectedItem(state); +} + +void KMyMoneyReconcileCombo::removeDontCare() +{ + //Remove unknown state + removeItem(3); +} + +void KMyMoneyReconcileCombo::setState(Split::State state) +{ + QString id; + + switch (state) { + case Split::State::NotReconciled: + id = ' '; + break; + case Split::State::Cleared: + id = 'C'; + break; + case Split::State::Reconciled: + id = 'R'; + break; + case Split::State::Frozen: + id = 'F'; + break; + case Split::State::Unknown: + id = 'U'; + break; + default: + qDebug() << "Unknown reconcile state '" << (int)state << "' in KMyMoneyReconcileCombo::setState()\n"; + break; + } + setSelectedItem(id); +} + +Split::State KMyMoneyReconcileCombo::state() const +{ + Split::State state = Split::State::NotReconciled; + + QVariant data = itemData(currentIndex()); + QString dataVal; + if (data.isValid()) + dataVal = data.toString(); + else + return state; + + if (!dataVal.isEmpty()) { + if (dataVal == "C") + state = Split::State::Cleared; + if (dataVal == "R") + state = Split::State::Reconciled; + if (dataVal == "F") + state = Split::State::Frozen; + if (dataVal == "U") + state = Split::State::Unknown; + } + return state; +} + +KMyMoneyCashFlowCombo::KMyMoneyCashFlowCombo(QWidget* w, Account accountType) : + KMyMoneyMVCCombo(false, w), + m_dir(KMyMoneyRegister::Unknown) +{ + addItem(" ", QVariant(KMyMoneyRegister::Unknown)); + if (accountType == Account::Income || accountType == Account::Expense) { + // this is used for income/expense accounts to just show the reverse sense + addItem(i18nc("Activity for income categories", "Received"), QVariant(KMyMoneyRegister::Payment)); + addItem(i18nc("Activity for expense categories", "Paid"), QVariant(KMyMoneyRegister::Deposit)); + } else { + addItem(i18n("Pay to"), QVariant(KMyMoneyRegister::Payment)); + addItem(i18n("From"), QVariant(KMyMoneyRegister::Deposit)); + } + + connect(this, SIGNAL(itemSelected(QString)), this, SLOT(slotSetDirection(QString))); +} + +void KMyMoneyCashFlowCombo::setDirection(KMyMoneyRegister::CashFlowDirection dir) +{ + m_dir = dir; + QString num; + setSelectedItem(num.setNum(dir)); +} + +void KMyMoneyCashFlowCombo::slotSetDirection(const QString& id) +{ + QString num; + for (int i = KMyMoneyRegister::Deposit; i <= KMyMoneyRegister::Unknown; ++i) { + num.setNum(i); + if (num == id) { + m_dir = static_cast(i); + break; + } + } + emit directionSelected(m_dir); + update(); +} + +void KMyMoneyCashFlowCombo::removeDontCare() +{ + removeItem(findData(QVariant(KMyMoneyRegister::Unknown), Qt::UserRole, Qt::MatchExactly)); +} + + +KMyMoneyActivityCombo::KMyMoneyActivityCombo(QWidget* w) : + KMyMoneyMVCCombo(false, w), + m_activity(Split::InvestmentTransactionType::UnknownTransactionType) +{ + addItem(i18n("Buy shares"), QVariant((int)Split::InvestmentTransactionType::BuyShares)); + addItem(i18n("Sell shares"), QVariant((int)Split::InvestmentTransactionType::SellShares)); + addItem(i18n("Dividend"), QVariant((int)Split::InvestmentTransactionType::Dividend)); + addItem(i18n("Reinvest dividend"), QVariant((int)Split::InvestmentTransactionType::ReinvestDividend)); + addItem(i18n("Yield"), QVariant((int)Split::InvestmentTransactionType::Yield)); + addItem(i18n("Add shares"), QVariant((int)Split::InvestmentTransactionType::AddShares)); + addItem(i18n("Remove shares"), QVariant((int)Split::InvestmentTransactionType::RemoveShares)); + addItem(i18n("Split shares"), QVariant((int)Split::InvestmentTransactionType::SplitShares)); + addItem(i18n("Interest Income"), QVariant((int)Split::InvestmentTransactionType::InterestIncome)); + + connect(this, SIGNAL(itemSelected(QString)), this, SLOT(slotSetActivity(QString))); +} + +void KMyMoneyActivityCombo::setActivity(Split::InvestmentTransactionType activity) +{ + m_activity = activity; + QString num; + setSelectedItem(num.setNum((int)activity)); +} + +void KMyMoneyActivityCombo::slotSetActivity(const QString& id) +{ + QString num; + for (auto i = (int)Split::InvestmentTransactionType::BuyShares; i <= (int)Split::InvestmentTransactionType::InterestIncome; ++i) { + num.setNum(i); + if (num == id) { + m_activity = static_cast(i); + break; + } + } + emit activitySelected(m_activity); + update(); +} + +KMyMoneyGeneralCombo::KMyMoneyGeneralCombo(QWidget* w) : + KComboBox(w) +{ + connect(this, SIGNAL(highlighted(int)), this, SLOT(slotChangeItem(int))); +} + +KMyMoneyGeneralCombo::~KMyMoneyGeneralCombo() +{ +} + +void KMyMoneyGeneralCombo::setCurrentItem(int id) +{ + setCurrentIndex(findData(QVariant(id), Qt::UserRole, Qt::MatchExactly)); +} + +int KMyMoneyGeneralCombo::currentItem() const +{ + return itemData(currentIndex()).toInt(); +} + +void KMyMoneyGeneralCombo::clear() +{ + KComboBox::clear(); +} + +void KMyMoneyGeneralCombo::insertItem(const QString& txt, int id, int idx) +{ + KComboBox::insertItem(idx, txt, QVariant(id)); +} + +void KMyMoneyGeneralCombo::removeItem(int id) +{ + KComboBox::removeItem(findData(QVariant(id), Qt::UserRole, Qt::MatchExactly)); +} + +void KMyMoneyGeneralCombo::slotChangeItem(int idx) +{ + emit itemSelected(itemData(idx).toInt()); +} + +KMyMoneyPeriodCombo::KMyMoneyPeriodCombo(QWidget* parent) : + KMyMoneyGeneralCombo(parent) +{ + insertItem(i18n("All dates"), (int)TransactionFilter::Date::All); + insertItem(i18n("As of today"), (int)TransactionFilter::Date::AsOfToday); + insertItem(i18n("Today"), (int)TransactionFilter::Date::Today); + insertItem(i18n("Current month"), (int)TransactionFilter::Date::CurrentMonth); + insertItem(i18n("Current quarter"), (int)TransactionFilter::Date::CurrentQuarter); + insertItem(i18n("Current year"), (int)TransactionFilter::Date::CurrentYear); + insertItem(i18n("Current fiscal year"), (int)TransactionFilter::Date::CurrentFiscalYear); + insertItem(i18n("Month to date"), (int)TransactionFilter::Date::MonthToDate); + insertItem(i18n("Year to date"), (int)TransactionFilter::Date::YearToDate); + insertItem(i18n("Year to month"), (int)TransactionFilter::Date::YearToMonth); + insertItem(i18n("Last month"), (int)TransactionFilter::Date::LastMonth); + insertItem(i18n("Last year"), (int)TransactionFilter::Date::LastYear); + insertItem(i18n("Last fiscal year"), (int)TransactionFilter::Date::LastFiscalYear); + insertItem(i18n("Last 7 days"), (int)TransactionFilter::Date::Last7Days); + insertItem(i18n("Last 30 days"), (int)TransactionFilter::Date::Last30Days); + insertItem(i18n("Last 3 months"), (int)TransactionFilter::Date::Last3Months); + insertItem(i18n("Last quarter"), (int)TransactionFilter::Date::LastQuarter); + insertItem(i18n("Last 6 months"), (int)TransactionFilter::Date::Last6Months); + insertItem(i18n("Last 11 months"), (int)TransactionFilter::Date::Last11Months); + insertItem(i18n("Last 12 months"), (int)TransactionFilter::Date::Last12Months); + insertItem(i18n("Next 7 days"), (int)TransactionFilter::Date::Next7Days); + insertItem(i18n("Next 30 days"), (int)TransactionFilter::Date::Next30Days); + insertItem(i18n("Next 3 months"), (int)TransactionFilter::Date::Next3Months); + insertItem(i18n("Next quarter"), (int)TransactionFilter::Date::NextQuarter); + insertItem(i18n("Next 6 months"), (int)TransactionFilter::Date::Next6Months); + insertItem(i18n("Next 12 months"), (int)TransactionFilter::Date::Next12Months); + insertItem(i18n("Next 18 months"), (int)TransactionFilter::Date::Next18Months); + insertItem(i18n("Last 3 months to next 3 months"), (int)TransactionFilter::Date::Last3ToNext3Months); + insertItem(i18n("User defined"), (int)TransactionFilter::Date::UserDefined); +} + +void KMyMoneyPeriodCombo::setCurrentItem(TransactionFilter::Date id) +{ + if (id >= TransactionFilter::Date::LastDateItem) + id = TransactionFilter::Date::UserDefined; + + KMyMoneyGeneralCombo::setCurrentItem((int)id); +} + +TransactionFilter::Date KMyMoneyPeriodCombo::currentItem() const +{ + return static_cast(KMyMoneyGeneralCombo::currentItem()); +} + +QDate KMyMoneyPeriodCombo::start(TransactionFilter::Date id) +{ + QDate start, end; + MyMoneyTransactionFilter::translateDateRange(id, start, end); + return start; +} + +QDate KMyMoneyPeriodCombo::end(TransactionFilter::Date id) +{ + QDate start, end; + MyMoneyTransactionFilter::translateDateRange(id, start, end); + return end; +} + +#if 0 +void KMyMoneyPeriodCombo::dates(QDate& start, QDate& end, MyMoneyTransactionFilter::dateOptionE id) +{ +} +#endif + +KMyMoneyOccurrenceCombo::KMyMoneyOccurrenceCombo(QWidget* parent) : + KMyMoneyGeneralCombo(parent) +{ +} + +Schedule::Occurrence KMyMoneyOccurrenceCombo::currentItem() const +{ + return static_cast(KMyMoneyGeneralCombo::currentItem()); +} + +KMyMoneyOccurrencePeriodCombo::KMyMoneyOccurrencePeriodCombo(QWidget* parent) : + KMyMoneyOccurrenceCombo(parent) +{ + addItem(i18nc("Schedule occurrence period", MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence::Once).toLatin1()), (int)Schedule::Occurrence::Once); + addItem(i18nc("Schedule occurrence period", MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence::Daily).toLatin1()), (int)Schedule::Occurrence::Daily); + addItem(i18nc("Schedule occurrence period", MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence::Weekly).toLatin1()), (int)Schedule::Occurrence::Weekly); + addItem(i18nc("Schedule occurrence period", MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence::EveryHalfMonth).toLatin1()), (int)Schedule::Occurrence::EveryHalfMonth); + addItem(i18nc("Schedule occurrence period", MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence::Monthly).toLatin1()), (int)Schedule::Occurrence::Monthly); + addItem(i18nc("Schedule occurrence period", MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence::Yearly).toLatin1()), (int)Schedule::Occurrence::Yearly); +} + +KMyMoneyFrequencyCombo::KMyMoneyFrequencyCombo(QWidget* parent) : + KMyMoneyOccurrenceCombo(parent) +{ + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::Once).toLatin1()), (int)Schedule::Occurrence::Once); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::Daily).toLatin1()), (int)Schedule::Occurrence::Daily); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::Weekly).toLatin1()), (int)Schedule::Occurrence::Weekly); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryOtherWeek).toLatin1()), (int)Schedule::Occurrence::EveryOtherWeek); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryHalfMonth).toLatin1()), (int)Schedule::Occurrence::EveryHalfMonth); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryThreeWeeks).toLatin1()), (int)Schedule::Occurrence::EveryThreeWeeks); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryThirtyDays).toLatin1()), (int)Schedule::Occurrence::EveryThirtyDays); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryFourWeeks).toLatin1()), (int)Schedule::Occurrence::EveryFourWeeks); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::Monthly).toLatin1()), (int)Schedule::Occurrence::Monthly); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryEightWeeks).toLatin1()), (int)Schedule::Occurrence::EveryEightWeeks); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryOtherMonth).toLatin1()), (int)Schedule::Occurrence::EveryOtherMonth); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryThreeMonths).toLatin1()), (int)Schedule::Occurrence::EveryThreeMonths); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryFourMonths).toLatin1()), (int)Schedule::Occurrence::EveryFourMonths); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::TwiceYearly).toLatin1()), (int)Schedule::Occurrence::TwiceYearly); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::Yearly).toLatin1()), (int)Schedule::Occurrence::Yearly); + addItem(i18nc("Frequency of schedule", MyMoneySchedule::occurrenceToString(Schedule::Occurrence::EveryOtherYear).toLatin1()), (int)Schedule::Occurrence::EveryOtherYear); + + connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentDataChanged())); +} + +int KMyMoneyFrequencyCombo::daysBetweenEvents() const +{ + return MyMoneySchedule::daysBetweenEvents(currentItem()); +} + +int KMyMoneyFrequencyCombo::eventsPerYear() const +{ + return MyMoneySchedule::eventsPerYear(currentItem()); +} + +QVariant KMyMoneyFrequencyCombo::currentData() const +{ + return itemData(currentIndex(), Qt::UserRole); +} + +void KMyMoneyFrequencyCombo::setCurrentData(QVariant data) +{ + setItemData(currentIndex(), data, Qt::UserRole); +} + +void KMyMoneyFrequencyCombo::slotCurrentDataChanged() +{ + emit currentDataChanged(currentData()); +} #include "moc_kmymoneymvccombo.cpp" diff --git a/kmymoney/widgets/kmymoneymvccombo.h b/kmymoney/widgets/kmymoneymvccombo.h index d37f72ad8..1f28ffcb7 100644 --- a/kmymoney/widgets/kmymoneymvccombo.h +++ b/kmymoney/widgets/kmymoneymvccombo.h @@ -1,156 +1,486 @@ /*************************************************************************** kmymoneymvccombo.h - description ------------------- begin : Mon Jan 09 2010 copyright : (C) 2010 by Thomas Baumgart Cristian Onet Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYMVCCOMBO_H #define KMYMONEYMVCCOMBO_H -#include "kmm_widgets_export.h" - // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes +#include "kmm_widgets_export.h" +#include "register.h" +#include "mymoneytransactionfilter.h" + +class QSortFilterProxyModel; +class MyMoneyPayee; +class MyMoneySchedule; +class MyMoneyTag; +class MyMoneyTransactionFilter; /** * @author Cristian Onet * This class will replace the KMyMoneyCombo class when all widgets will use the MVC */ -class KMyMoneyMVCComboPrivate; class KMM_WIDGETS_EXPORT KMyMoneyMVCCombo : public KComboBox { Q_OBJECT - Q_DISABLE_COPY(KMyMoneyMVCCombo) Q_PROPERTY(QString selectedItem READ selectedItem WRITE setSelectedItem STORED false) public: - explicit KMyMoneyMVCCombo(QWidget* parent = nullptr); - explicit KMyMoneyMVCCombo(bool editable, QWidget* parent = nullptr); - virtual ~KMyMoneyMVCCombo(); + KMyMoneyMVCCombo(QWidget* parent = 0); + explicit KMyMoneyMVCCombo(bool editable, QWidget* parent = 0); + ~KMyMoneyMVCCombo(); /** * @sa KLineEdit::setPlaceholderText() */ void setPlaceholderText(const QString& hint) const; /** * This method returns the id of the first selected item. * * @return reference to QString containing the id. If no item * is selected the QString will be empty. */ const QString& selectedItem() const; /** * This method selects the item with the respective @a id. * * @param id reference to QString containing the id */ void setSelectedItem(const QString& id); /** * Protect an entry from selection. Protection is controlled by * the parameter @p protect. * * @param id id of item for which to modify the protection * @param protect if true, the entry specified by @p accId cannot be * selected. If false, it can be selected. * Defaults to @p true. */ void protectItem(int id, bool protect); /** * Make the completion match on any substring or only * from the start of an entry. * * @param enabled @a true turns on substring match, @a false turns it off. * The default is @a false. */ void setSubstringSearch(bool enabled); /** * set setSubstringSearch(enabled) of all children of widget * * @param widget a valid pointer (not 0) */ static void setSubstringSearchForChildren(QWidget *const widget, bool enabled = false); /** * Reimplemented for internal reasons, no API change */ void setEditable(bool editable); protected slots: void activated(int index); protected: /** * reimplemented to support detection of new items */ - void focusOutEvent(QFocusEvent*) override; + void focusOutEvent(QFocusEvent*); /** * check if the current text is contained in the internal list, if not ask the user if want to create a new item. */ virtual void checkCurrentText(); /** * set the widgets text area based on the item with the given @a id. */ virtual void setCurrentTextById(const QString& id); /** * Overridden for internal reasons, no API change */ - void connectNotify(const QMetaMethod & signal) override; + void connectNotify(const QMetaMethod & signal); /** * Overridden for internal reasons, no API change */ - void disconnectNotify(const QMetaMethod & signal) override; + void disconnectNotify(const QMetaMethod & signal); /** * overridden for internal reasons, no API change */ - void setCurrentText(const QString& txt); - void setCurrentText(); + void setCurrentText(const QString& txt = QString()) { + KComboBox::setItemText(KComboBox::currentIndex(), txt); + } signals: void itemSelected(const QString& id); void objectCreation(bool); void createItem(const QString&, QString&); void lostFocus(); +private: + /// \internal d-pointer class. + class Private; + /// \internal d-pointer instance. + Private* const d; + + /** + * This is just a cache to be able to implement the old interface. + */ + mutable QString m_id; +}; + +/** + * This class implements a text based payee selector. + * When initially used, the widget has the functionality of a KComboBox object. + * Whenever a key is pressed, the set of loaded payees is searched for + * payees names which match the currently entered text. + * + * If any match is found a list selection box is opened and the user can use + * the up/down, page-up/page-down keys or the mouse to navigate in the list. If + * a payee is selected, the selection box is closed. Other key-strokes are + * directed to the parent object to manipulate the text. The visible contents of + * the selection box is updated with every key-stroke. + * + * This object is a replacement of the kMyMoneyPayee object and should be used + * for new code. + * + * @author Thomas Baumgart + */ +class KMM_WIDGETS_EXPORT KMyMoneyPayeeCombo : public KMyMoneyMVCCombo + +{ + Q_OBJECT +public: + KMyMoneyPayeeCombo(QWidget* parent = 0); + + void loadPayees(const QList& list); +}; + +/** + * This class implements a text based tag selector. + * The widget has the functionality of a KMyMoneyPayeeCombo object. + * Whenever a key is pressed, the set of loaded tags is searched for + * tags names which match the currently entered text. + * + * @author Alessandro Russo + */ +class KMM_WIDGETS_EXPORT KMyMoneyTagCombo : public KMyMoneyMVCCombo +{ + Q_OBJECT +public: + KMyMoneyTagCombo(QWidget* parent = 0); + + void loadTags(const QList& list); + /** ids in usedIdList are escluded from the internal list + * you should call loadTags before calling setUsedTagList because it doesn't readd + * tag removed in previous call*/ + void setUsedTagList(QList& usedIdList, QList& usedTagNameList); + protected: - KMyMoneyMVCComboPrivate * const d_ptr; - KMyMoneyMVCCombo(KMyMoneyMVCComboPrivate &dd, QWidget* parent = nullptr); - KMyMoneyMVCCombo(KMyMoneyMVCComboPrivate &dd, bool editable, QWidget* parent = nullptr); + /** + * check if the current text is contained in the internal list, if not ask the user if want to create a new item. + */ + virtual void checkCurrentText(); + +private: + QList m_usedIdList; + QList m_usedTagNameList; + QList m_closedIdList; + QList m_closedTagNameList; +}; + +/** + * This class implements a tag label. It create a QFrame and inside it a QToolButton + * with a 'X' Icon and a QLabel with the name of the Tag + * + * @author Alessandro Russo + */ +class KTagLabel : public QFrame +{ + Q_OBJECT +public: + KTagLabel(const QString& id, const QString& name, QWidget* parent = 0); + +signals: + void clicked(bool); private: - Q_DECLARE_PRIVATE(KMyMoneyMVCCombo) + QString m_tagId; }; +/** + * This widget contain a KMyMoneyTagCombo widget and 0 or more KTagLabel widgets + * call KMyMoneyTagCombo.loadTags with the correct list whenever a new KTagLabel is created or + * deleted by removing or adding the relative tag + * + * @author Alessandro Russo + */ +class KMM_WIDGETS_EXPORT KTagContainer : public QWidget +{ + Q_OBJECT +public: + KTagContainer(QWidget* parent = 0); + + void loadTags(const QList& list); + KMyMoneyTagCombo* tagCombo() { + return m_tagCombo; + } + const QList selectedTags(); + void addTagWidget(const QString& id); + void RemoveAllTagWidgets(); + +protected slots: + void slotRemoveTagWidget(); + void slotAddTagWidget(); + +private: + KMyMoneyTagCombo *m_tagCombo; + QList m_tagLabelList; + QList m_tagIdList; + QList m_tagNameList; + + // A local cache of the list of all Tags, it's updated when loadTags is called + QList m_list; +}; + +/** + * @author Thomas Baumgart + * This class implements a combo box with the possible states for + * reconciliation. + */ +class KMM_WIDGETS_EXPORT KMyMoneyReconcileCombo : public KMyMoneyMVCCombo +{ + Q_OBJECT +public: + KMyMoneyReconcileCombo(QWidget *w = 0); + + void setState(eMyMoney::Split::State state); + eMyMoney::Split::State state() const; + void removeDontCare(); + +protected slots: + void slotSetState(const QString&); +}; + +/** + * @author Thomas Baumgart + * This class implements a combo box with the possible states for + * actions (Deposit, Withdrawal, etc.). + */ +class KMM_WIDGETS_EXPORT KMyMoneyCashFlowCombo : public KMyMoneyMVCCombo +{ + Q_OBJECT +public: + /** + * Create a combo box that contains the entries "Pay to", "From" and + * " " for don't care. + */ + explicit KMyMoneyCashFlowCombo(QWidget *w = 0, eMyMoney::Account type = eMyMoney::Account::Asset); + + void setDirection(KMyMoneyRegister::CashFlowDirection dir); + KMyMoneyRegister::CashFlowDirection direction() const { + return m_dir; + } + void removeDontCare(); + +protected slots: + void slotSetDirection(const QString& id); + +signals: + void directionSelected(KMyMoneyRegister::CashFlowDirection); + +private: + KMyMoneyRegister::CashFlowDirection m_dir; +}; + +/** + * @author Thomas Baumgart + * This class implements a combo box with the possible activities + * for investment transactions (buy, sell, dividend, etc.) + */ +class KMM_WIDGETS_EXPORT KMyMoneyActivityCombo : public KMyMoneyMVCCombo +{ + Q_OBJECT +public: + /** + * Create a combo box that contains the entries "Buy", "Sell" etc. + */ + KMyMoneyActivityCombo(QWidget *w = 0); + + void setActivity(eMyMoney::Split::InvestmentTransactionType activity); + eMyMoney::Split::InvestmentTransactionType activity() const { + return m_activity; + } + +protected slots: + void slotSetActivity(const QString& id); + +signals: + void activitySelected(eMyMoney::Split::InvestmentTransactionType); + +private: + eMyMoney::Split::InvestmentTransactionType m_activity; +}; + +class KMM_WIDGETS_EXPORT KMyMoneyGeneralCombo : public KComboBox +{ + Q_OBJECT + Q_PROPERTY(int currentItem READ currentItem WRITE setCurrentItem STORED false) +public: + KMyMoneyGeneralCombo(QWidget* parent = 0); + virtual ~KMyMoneyGeneralCombo(); + + void insertItem(const QString& txt, int id, int idx = -1); + + void setCurrentItem(int id); + int currentItem() const; + + void removeItem(int id); + +public slots: + void clear(); + +signals: + void itemSelected(int id); + +protected: + // prevent the caller to use the standard KComboBox insertItem function with a default idx + void insertItem(const QString&); + +protected slots: + void slotChangeItem(int idx); + +}; + +/** + * This class implements a time period selector + * @author Thomas Baumgart + */ +class KMM_WIDGETS_EXPORT KMyMoneyPeriodCombo : public KMyMoneyGeneralCombo +{ + Q_OBJECT +public: + KMyMoneyPeriodCombo(QWidget* parent = 0); + + eMyMoney::TransactionFilter::Date currentItem() const; + void setCurrentItem(eMyMoney::TransactionFilter::Date id); + + /** + * This function returns the actual start date for the given + * period definition given by @p id. For user defined periods + * the returned value is QDate() + */ + static QDate start(eMyMoney::TransactionFilter::Date id); + + /** + * This function returns the actual end date for the given + * period definition given by @p id. For user defined periods + * the returned value is QDate() + */ + static QDate end(eMyMoney::TransactionFilter::Date id); + + // static void dates(QDate& start, QDate& end, MyMoneyTransactionFilter::dateOptionE id); +}; + +/** + * This class implements an occurrence selector + * as a parent class for both OccurrencePeriod and Frequency combos + * + * @author Colin Wright + */ +class KMM_WIDGETS_EXPORT KMyMoneyOccurrenceCombo : public KMyMoneyGeneralCombo +{ + Q_OBJECT +public: + KMyMoneyOccurrenceCombo(QWidget* parent = 0); + + eMyMoney::Schedule::Occurrence currentItem() const; +}; + +/** + * This class implements an occurrence period selector + * + * @author Colin Wright + */ +class KMM_WIDGETS_EXPORT KMyMoneyOccurrencePeriodCombo : public KMyMoneyOccurrenceCombo +{ + Q_OBJECT +public: + KMyMoneyOccurrencePeriodCombo(QWidget* parent = 0); + +}; + +/** + * This class implements a payment frequency selector + * @author Thomas Baumgart + */ +class KMM_WIDGETS_EXPORT KMyMoneyFrequencyCombo : public KMyMoneyOccurrenceCombo +{ + Q_OBJECT + + Q_PROPERTY(QVariant data READ currentData WRITE setCurrentData STORED false) + +public: + KMyMoneyFrequencyCombo(QWidget* parent = 0); + + /** + * This method returns the number of events for the selected payment + * frequency (eg for yearly the return value is 1 and for monthly it + * is 12). In case, the frequency cannot be converted (once, every other year, etc.) + * the method returns 0. + */ + int eventsPerYear() const; + /** + * This method returns the number of days between two events of + * the selected frequency. The return value for months is based + * on 30 days and the year is 360 days long. + */ + int daysBetweenEvents() const; + + QVariant currentData() const; + + void setCurrentData(QVariant data); + +Q_SIGNALS: + void currentDataChanged(QVariant data); + +protected slots: + void slotCurrentDataChanged(); + +private: + QVariant data; + +}; #endif diff --git a/kmymoney/widgets/kmymoneymvccombo_p.h b/kmymoney/widgets/kmymoneymvccombo_p.h deleted file mode 100644 index 4f12198e6..000000000 --- a/kmymoney/widgets/kmymoneymvccombo_p.h +++ /dev/null @@ -1,66 +0,0 @@ -/*************************************************************************** - kmymoneymvccombo_p.h - description - ------------------- - begin : Sat Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KMYMONEYMVCCOMBO_P_H -#define KMYMONEYMVCCOMBO_P_H - - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -class QCompleter; - -class KMyMoneyMVCComboPrivate -{ -public: - KMyMoneyMVCComboPrivate() : - m_canCreateObjects(false), - m_inFocusOutEvent(false), - m_completer(nullptr) - { - } - - /** - * Flag to control object creation. Use - * KMyMoneyMVCCombo::setSuppressObjectCreation() - * to modify it's setting. Defaults to @a false. - */ - bool m_canCreateObjects; - - /** - * Flag to check whether a focusOutEvent processing is underway or not - */ - bool m_inFocusOutEvent; - - QCompleter *m_completer; - /** - * This is just a cache to be able to implement the old interface. - */ - mutable QString m_id; -}; - -#endif diff --git a/kmymoney/widgets/kmymoneyoccurrencecombo.cpp b/kmymoney/widgets/kmymoneyoccurrencecombo.cpp deleted file mode 100644 index 1baa8844c..000000000 --- a/kmymoney/widgets/kmymoneyoccurrencecombo.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/*************************************************************************** - kmymoneyoccurrencecombo.cpp - description - ------------------- - begin : Sat Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "kmymoneyoccurrencecombo.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "mymoneyenums.h" - -using namespace eMyMoney; - -KMyMoneyOccurrenceCombo::KMyMoneyOccurrenceCombo(QWidget* parent) : - KMyMoneyGeneralCombo(parent) -{ -} - -KMyMoneyOccurrenceCombo::~KMyMoneyOccurrenceCombo() -{ -} - -Schedule::Occurrence KMyMoneyOccurrenceCombo::currentItem() const -{ - return static_cast(KMyMoneyGeneralCombo::currentItem()); -} - diff --git a/kmymoney/widgets/kmymoneyoccurrencecombo.h b/kmymoney/widgets/kmymoneyoccurrencecombo.h deleted file mode 100644 index 8aae66dab..000000000 --- a/kmymoney/widgets/kmymoneyoccurrencecombo.h +++ /dev/null @@ -1,55 +0,0 @@ -/*************************************************************************** - kmymoneyoccurrencecombo.h - description - ------------------- - begin : Mon Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KMYMONEYOCCURRENCECOMBO_H -#define KMYMONEYOCCURRENCECOMBO_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmymoneygeneralcombo.h" - -namespace eMyMoney { namespace Schedule { enum class Occurrence; } } - -/** - * This class implements an occurrence selector - * as a parent class for both OccurrencePeriod and Frequency combos - * - * @author Colin Wright - */ -class KMM_WIDGETS_EXPORT KMyMoneyOccurrenceCombo : public KMyMoneyGeneralCombo -{ - Q_OBJECT - Q_DISABLE_COPY(KMyMoneyOccurrenceCombo) - -public: - explicit KMyMoneyOccurrenceCombo(QWidget* parent = nullptr); - ~KMyMoneyOccurrenceCombo() override; - - eMyMoney::Schedule::Occurrence currentItem() const; -}; - -#endif diff --git a/kmymoney/widgets/kmymoneyoccurrenceperiodcombo.cpp b/kmymoney/widgets/kmymoneyoccurrenceperiodcombo.cpp deleted file mode 100644 index c4ed2dec7..000000000 --- a/kmymoney/widgets/kmymoneyoccurrenceperiodcombo.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/*************************************************************************** - kmymoneyoccurrenceperiodcombo.cpp - description - ------------------- - begin : Sat Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "kmymoneyoccurrenceperiodcombo.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -#include - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "mymoneyschedule.h" -#include "mymoneyenums.h" - -using namespace eMyMoney; - -KMyMoneyOccurrencePeriodCombo::KMyMoneyOccurrencePeriodCombo(QWidget* parent) : - KMyMoneyOccurrenceCombo(parent) -{ - addItem(i18nc("Schedule occurrence period", MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence::Once).toLatin1()), (int)Schedule::Occurrence::Once); - addItem(i18nc("Schedule occurrence period", MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence::Daily).toLatin1()), (int)Schedule::Occurrence::Daily); - addItem(i18nc("Schedule occurrence period", MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence::Weekly).toLatin1()), (int)Schedule::Occurrence::Weekly); - addItem(i18nc("Schedule occurrence period", MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence::EveryHalfMonth).toLatin1()), (int)Schedule::Occurrence::EveryHalfMonth); - addItem(i18nc("Schedule occurrence period", MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence::Monthly).toLatin1()), (int)Schedule::Occurrence::Monthly); - addItem(i18nc("Schedule occurrence period", MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence::Yearly).toLatin1()), (int)Schedule::Occurrence::Yearly); -} - -KMyMoneyOccurrencePeriodCombo::~KMyMoneyOccurrencePeriodCombo() -{ -} diff --git a/kmymoney/widgets/kmymoneyoccurrenceperiodcombo.h b/kmymoney/widgets/kmymoneyoccurrenceperiodcombo.h deleted file mode 100644 index cae01b8b2..000000000 --- a/kmymoney/widgets/kmymoneyoccurrenceperiodcombo.h +++ /dev/null @@ -1,51 +0,0 @@ -/*************************************************************************** - kmymoneyoccurrenceperiodcombo.h - description - ------------------- - begin : Mon Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KMYMONEYOCCURRENCEPERIODCOMBO_H -#define KMYMONEYOCCURRENCEPERIODCOMBO_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmymoneyoccurrencecombo.h" - -/** - * This class implements an occurrence period selector - * - * @author Colin Wright - */ -class KMM_WIDGETS_EXPORT KMyMoneyOccurrencePeriodCombo : public KMyMoneyOccurrenceCombo -{ - Q_OBJECT - Q_DISABLE_COPY(KMyMoneyOccurrencePeriodCombo) - -public: - explicit KMyMoneyOccurrencePeriodCombo(QWidget* parent = nullptr); - ~KMyMoneyOccurrencePeriodCombo() override; - -}; - -#endif diff --git a/kmymoney/widgets/kmymoneypayeecombo.cpp b/kmymoney/widgets/kmymoneypayeecombo.cpp deleted file mode 100644 index 3ba20ba97..000000000 --- a/kmymoney/widgets/kmymoneypayeecombo.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/*************************************************************************** - kmymoneypayeecombo.cpp - description - ------------------- - begin : Sat Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "kmymoneypayeecombo.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "mymoneypayee.h" - -KMyMoneyPayeeCombo::KMyMoneyPayeeCombo(QWidget* parent) : - KMyMoneyMVCCombo(true, parent) -{ -} - -KMyMoneyPayeeCombo::~KMyMoneyPayeeCombo() -{ -} - -void KMyMoneyPayeeCombo::loadPayees(const QList& list) -{ - clear(); - - //add a blank item, since the field is optional - addItem(QString(), QVariant(QString())); - - //add all payees - QList::const_iterator it; - for (it = list.constBegin(); it != list.constEnd(); ++it) { - addItem((*it).name(), QVariant((*it).id())); - } - - //sort the model, which will sort the list in the combo - model()->sort(Qt::DisplayRole, Qt::AscendingOrder); - - //set the text to empty and the index to the first item on the list - setCurrentIndex(0); - clearEditText(); -} diff --git a/kmymoney/widgets/kmymoneypayeecombo.h b/kmymoney/widgets/kmymoneypayeecombo.h deleted file mode 100644 index a6fc2d36b..000000000 --- a/kmymoney/widgets/kmymoneypayeecombo.h +++ /dev/null @@ -1,66 +0,0 @@ -/*************************************************************************** - kmymoneypayeecombo.h - description - ------------------- - begin : Mon Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KMYMONEYPAYEECOMBO_H -#define KMYMONEYPAYEECOMBO_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmymoneymvccombo.h" - -class MyMoneyPayee; - -/** - * This class implements a text based payee selector. - * When initially used, the widget has the functionality of a KComboBox object. - * Whenever a key is pressed, the set of loaded payees is searched for - * payees names which match the currently entered text. - * - * If any match is found a list selection box is opened and the user can use - * the up/down, page-up/page-down keys or the mouse to navigate in the list. If - * a payee is selected, the selection box is closed. Other key-strokes are - * directed to the parent object to manipulate the text. The visible contents of - * the selection box is updated with every key-stroke. - * - * This object is a replacement of the kMyMoneyPayee object and should be used - * for new code. - * - * @author Thomas Baumgart - */ -class KMM_WIDGETS_EXPORT KMyMoneyPayeeCombo : public KMyMoneyMVCCombo -{ - Q_OBJECT - Q_DISABLE_COPY(KMyMoneyPayeeCombo) - -public: - explicit KMyMoneyPayeeCombo(QWidget* parent = nullptr); - ~KMyMoneyPayeeCombo() override; - - void loadPayees(const QList& list); -}; - -#endif diff --git a/kmymoney/widgets/kmymoneyperiodcombo.cpp b/kmymoney/widgets/kmymoneyperiodcombo.cpp deleted file mode 100644 index 84989de47..000000000 --- a/kmymoney/widgets/kmymoneyperiodcombo.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/*************************************************************************** - kmymoneyperiodcombo.cpp - description - ------------------- - begin : Sat Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "kmymoneyperiodcombo.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -#include - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "mymoneytransactionfilter.h" - -using namespace eMyMoney; - -KMyMoneyPeriodCombo::KMyMoneyPeriodCombo(QWidget* parent) : - KMyMoneyGeneralCombo(parent) -{ - insertItem(i18n("All dates"), (int)TransactionFilter::Date::All); - insertItem(i18n("As of today"), (int)TransactionFilter::Date::AsOfToday); - insertItem(i18n("Today"), (int)TransactionFilter::Date::Today); - insertItem(i18n("Current month"), (int)TransactionFilter::Date::CurrentMonth); - insertItem(i18n("Current quarter"), (int)TransactionFilter::Date::CurrentQuarter); - insertItem(i18n("Current year"), (int)TransactionFilter::Date::CurrentYear); - insertItem(i18n("Current fiscal year"), (int)TransactionFilter::Date::CurrentFiscalYear); - insertItem(i18n("Month to date"), (int)TransactionFilter::Date::MonthToDate); - insertItem(i18n("Year to date"), (int)TransactionFilter::Date::YearToDate); - insertItem(i18n("Year to month"), (int)TransactionFilter::Date::YearToMonth); - insertItem(i18n("Last month"), (int)TransactionFilter::Date::LastMonth); - insertItem(i18n("Last year"), (int)TransactionFilter::Date::LastYear); - insertItem(i18n("Last fiscal year"), (int)TransactionFilter::Date::LastFiscalYear); - insertItem(i18n("Last 7 days"), (int)TransactionFilter::Date::Last7Days); - insertItem(i18n("Last 30 days"), (int)TransactionFilter::Date::Last30Days); - insertItem(i18n("Last 3 months"), (int)TransactionFilter::Date::Last3Months); - insertItem(i18n("Last quarter"), (int)TransactionFilter::Date::LastQuarter); - insertItem(i18n("Last 6 months"), (int)TransactionFilter::Date::Last6Months); - insertItem(i18n("Last 11 months"), (int)TransactionFilter::Date::Last11Months); - insertItem(i18n("Last 12 months"), (int)TransactionFilter::Date::Last12Months); - insertItem(i18n("Next 7 days"), (int)TransactionFilter::Date::Next7Days); - insertItem(i18n("Next 30 days"), (int)TransactionFilter::Date::Next30Days); - insertItem(i18n("Next 3 months"), (int)TransactionFilter::Date::Next3Months); - insertItem(i18n("Next quarter"), (int)TransactionFilter::Date::NextQuarter); - insertItem(i18n("Next 6 months"), (int)TransactionFilter::Date::Next6Months); - insertItem(i18n("Next 12 months"), (int)TransactionFilter::Date::Next12Months); - insertItem(i18n("Next 18 months"), (int)TransactionFilter::Date::Next18Months); - insertItem(i18n("Last 3 months to next 3 months"), (int)TransactionFilter::Date::Last3ToNext3Months); - insertItem(i18n("User defined"), (int)TransactionFilter::Date::UserDefined); -} - -KMyMoneyPeriodCombo::~KMyMoneyPeriodCombo() -{ -} - -void KMyMoneyPeriodCombo::setCurrentItem(TransactionFilter::Date id) -{ - if (id >= TransactionFilter::Date::LastDateItem) - id = TransactionFilter::Date::UserDefined; - - KMyMoneyGeneralCombo::setCurrentItem((int)id); -} - -TransactionFilter::Date KMyMoneyPeriodCombo::currentItem() const -{ - return static_cast(KMyMoneyGeneralCombo::currentItem()); -} - -QDate KMyMoneyPeriodCombo::start(TransactionFilter::Date id) -{ - QDate start, end; - MyMoneyTransactionFilter::translateDateRange(id, start, end); - return start; -} - -QDate KMyMoneyPeriodCombo::end(TransactionFilter::Date id) -{ - QDate start, end; - MyMoneyTransactionFilter::translateDateRange(id, start, end); - return end; -} - -#if 0 -void KMyMoneyPeriodCombo::dates(QDate& start, QDate& end, MyMoneyTransactionFilter::dateOptionE id) -{ -} -#endif diff --git a/kmymoney/widgets/kmymoneyperiodcombo.h b/kmymoney/widgets/kmymoneyperiodcombo.h deleted file mode 100644 index 7a4638836..000000000 --- a/kmymoney/widgets/kmymoneyperiodcombo.h +++ /dev/null @@ -1,70 +0,0 @@ -/*************************************************************************** - kmymoneyperiodcombo.h - description - ------------------- - begin : Mon Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KMYMONEYPERIODCOMBO_H -#define KMYMONEYPERIODCOMBO_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmymoneygeneralcombo.h" - -namespace eMyMoney { namespace TransactionFilter { enum class Date; } } - -/** - * This class implements a time period selector - * @author Thomas Baumgart - */ -class KMM_WIDGETS_EXPORT KMyMoneyPeriodCombo : public KMyMoneyGeneralCombo -{ - Q_OBJECT - Q_DISABLE_COPY(KMyMoneyPeriodCombo) - -public: - explicit KMyMoneyPeriodCombo(QWidget* parent = nullptr); - ~KMyMoneyPeriodCombo() override; - - eMyMoney::TransactionFilter::Date currentItem() const; - void setCurrentItem(eMyMoney::TransactionFilter::Date id); - - /** - * This function returns the actual start date for the given - * period definition given by @p id. For user defined periods - * the returned value is QDate() - */ - static QDate start(eMyMoney::TransactionFilter::Date id); - - /** - * This function returns the actual end date for the given - * period definition given by @p id. For user defined periods - * the returned value is QDate() - */ - static QDate end(eMyMoney::TransactionFilter::Date id); - - // static void dates(QDate& start, QDate& end, MyMoneyTransactionFilter::dateOptionE id); -}; - -#endif diff --git a/kmymoney/widgets/kmymoneyreconcilecombo.cpp b/kmymoney/widgets/kmymoneyreconcilecombo.cpp deleted file mode 100644 index 1b2aa74d1..000000000 --- a/kmymoney/widgets/kmymoneyreconcilecombo.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/*************************************************************************** - kmymoneyreconcilecombo.cpp - description - ------------------- - begin : Sat Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "kmymoneyreconcilecombo.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -#include - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "mymoneyenums.h" - -using namespace eMyMoney; - -KMyMoneyReconcileCombo::KMyMoneyReconcileCombo(QWidget* w) : - KMyMoneyMVCCombo(false, w) -{ - // add the items in reverse order of appearance (see KMyMoneySelector::newItem() for details) - addItem(i18n("Reconciled"), QVariant("R")); - addItem(i18nc("Reconciliation state 'Cleared'", "Cleared"), QVariant("C")); - addItem(i18n("Not reconciled"), QVariant(" ")); - addItem(" ", QVariant("U")); - - connect(this, SIGNAL(itemSelected(QString)), this, SLOT(slotSetState(QString))); -} - -KMyMoneyReconcileCombo::~KMyMoneyReconcileCombo() -{ -} - -void KMyMoneyReconcileCombo::slotSetState(const QString& state) -{ - setSelectedItem(state); -} - -void KMyMoneyReconcileCombo::removeDontCare() -{ - //Remove unknown state - removeItem(3); -} - -void KMyMoneyReconcileCombo::setState(Split::State state) -{ - QString id; - - switch (state) { - case Split::State::NotReconciled: - id = ' '; - break; - case Split::State::Cleared: - id = 'C'; - break; - case Split::State::Reconciled: - id = 'R'; - break; - case Split::State::Frozen: - id = 'F'; - break; - case Split::State::Unknown: - id = 'U'; - break; - default: - qDebug() << "Unknown reconcile state '" << (int)state << "' in KMyMoneyReconcileCombo::setState()\n"; - break; - } - setSelectedItem(id); -} - -Split::State KMyMoneyReconcileCombo::state() const -{ - Split::State state = Split::State::NotReconciled; - - QVariant data = itemData(currentIndex()); - QString dataVal; - if (data.isValid()) - dataVal = data.toString(); - else - return state; - - if (!dataVal.isEmpty()) { - if (dataVal == "C") - state = Split::State::Cleared; - if (dataVal == "R") - state = Split::State::Reconciled; - if (dataVal == "F") - state = Split::State::Frozen; - if (dataVal == "U") - state = Split::State::Unknown; - } - return state; -} diff --git a/kmymoney/widgets/kmymoneyreconcilecombo.h b/kmymoney/widgets/kmymoneyreconcilecombo.h deleted file mode 100644 index f6a0011bd..000000000 --- a/kmymoney/widgets/kmymoneyreconcilecombo.h +++ /dev/null @@ -1,60 +0,0 @@ -/*************************************************************************** - kmymoneyreconcilecombo.h - description - ------------------- - begin : Mon Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KMYMONEYRECONCILECOMBO_H -#define KMYMONEYRECONCILECOMBO_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmymoneymvccombo.h" - -namespace eMyMoney { namespace Split { enum class State; } } - -/** - * @author Thomas Baumgart - * This class implements a combo box with the possible states for - * reconciliation. - */ - -class KMM_WIDGETS_EXPORT KMyMoneyReconcileCombo : public KMyMoneyMVCCombo -{ - Q_OBJECT - Q_DISABLE_COPY(KMyMoneyReconcileCombo) - -public: - explicit KMyMoneyReconcileCombo(QWidget *w = 0); - ~KMyMoneyReconcileCombo() override; - - void setState(eMyMoney::Split::State state); - eMyMoney::Split::State state() const; - void removeDontCare(); - -protected slots: - void slotSetState(const QString&); -}; - -#endif diff --git a/kmymoney/widgets/kmymoneyselector.cpp b/kmymoney/widgets/kmymoneyselector.cpp index 1cf1b6d33..fc03aa88a 100644 --- a/kmymoney/widgets/kmymoneyselector.cpp +++ b/kmymoney/widgets/kmymoneyselector.cpp @@ -1,568 +1,525 @@ /*************************************************************************** kmymoneyselector.cpp ------------------- begin : Thu Jun 29 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ -#include "kmymoneyselector_p.h" - #include +#include "kmymoneyselector.h" + // ---------------------------------------------------------------------------- // QT Includes +#include +#include #include #include +#include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyglobalsettings.h" KMyMoneySelector::KMyMoneySelector(QWidget *parent, Qt::WindowFlags flags) : - QWidget(parent, flags), - d_ptr(new KMyMoneySelectorPrivate(this)) + QWidget(parent, flags) { - Q_D(KMyMoneySelector); - d->init(); -} + setAutoFillBackground(true); -KMyMoneySelector::KMyMoneySelector(KMyMoneySelectorPrivate &dd, QWidget* parent, Qt::WindowFlags flags) : - QWidget(parent, flags), - d_ptr(&dd) -{ - Q_D(KMyMoneySelector); - d->init(); + m_selMode = QTreeWidget::SingleSelection; + + m_treeWidget = new QTreeWidget(this); + // don't show horizontal scroll bar + m_treeWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + m_treeWidget->setSortingEnabled(false); + m_treeWidget->setAlternatingRowColors(true); + + m_treeWidget->setAllColumnsShowFocus(true); + + m_layout = new QHBoxLayout(this); + m_layout->setSpacing(0); + m_layout->setMargin(0); + + m_treeWidget->header()->hide(); + + m_layout->addWidget(m_treeWidget); + + // force init + m_selMode = QTreeWidget::MultiSelection; + setSelectionMode(QTreeWidget::SingleSelection); + + connect(m_treeWidget, SIGNAL(itemPressed(QTreeWidgetItem*,int)), this, SLOT(slotItemPressed(QTreeWidgetItem*,int))); + connect(m_treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SIGNAL(stateChanged())); } KMyMoneySelector::~KMyMoneySelector() { - Q_D(KMyMoneySelector); - delete d; } void KMyMoneySelector::clear() { - Q_D(KMyMoneySelector); - d->m_treeWidget->clear(); + m_treeWidget->clear(); } void KMyMoneySelector::setSelectable(QTreeWidgetItem *item, bool selectable) { if (selectable) { item->setFlags(item->flags() | Qt::ItemIsSelectable); } else { item->setFlags(item->flags() & ~Qt::ItemIsSelectable); } } -void KMyMoneySelector::slotSelectAllItems() -{ - selectAllItems(true); -} - -void KMyMoneySelector::slotDeselectAllItems() -{ - selectAllItems(false); -} - void KMyMoneySelector::setSelectionMode(const QTreeWidget::SelectionMode mode) { - Q_D(KMyMoneySelector); - if (d->m_selMode != mode) { - d->m_selMode = mode; + if (m_selMode != mode) { + m_selMode = mode; clear(); // make sure, it's either Multi or Single if (mode != QTreeWidget::MultiSelection) { - d->m_selMode = QTreeWidget::SingleSelection; - connect(d->m_treeWidget, SIGNAL(itemSelectionChanged()), this, SIGNAL(stateChanged())); - connect(d->m_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(slotItemSelected(QTreeWidgetItem*))); - connect(d->m_treeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(slotItemSelected(QTreeWidgetItem*))); + m_selMode = QTreeWidget::SingleSelection; + connect(m_treeWidget, SIGNAL(itemSelectionChanged()), this, SIGNAL(stateChanged())); + connect(m_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(slotItemSelected(QTreeWidgetItem*))); + connect(m_treeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(slotItemSelected(QTreeWidgetItem*))); } else { - disconnect(d->m_treeWidget, SIGNAL(itemSelectionChanged()), this, SIGNAL(stateChanged())); - disconnect(d->m_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(slotItemSelected(QTreeWidgetItem*))); - disconnect(d->m_treeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(slotItemSelected(QTreeWidgetItem*))); + disconnect(m_treeWidget, SIGNAL(itemSelectionChanged()), this, SIGNAL(stateChanged())); + disconnect(m_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(slotItemSelected(QTreeWidgetItem*))); + disconnect(m_treeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(slotItemSelected(QTreeWidgetItem*))); } } QWidget::update(); } -QTreeWidget::SelectionMode KMyMoneySelector::selectionMode() const -{ - Q_D(const KMyMoneySelector); - return d->m_selMode; -} - void KMyMoneySelector::slotItemSelected(QTreeWidgetItem *item) { - Q_D(KMyMoneySelector); - if (d->m_selMode == QTreeWidget::SingleSelection) { + if (m_selMode == QTreeWidget::SingleSelection) { if (item && item->flags().testFlag(Qt::ItemIsSelectable)) { emit itemSelected(item->data(0, IdRole).toString()); } } } QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, QTreeWidgetItem* after, const QString& key, const QString& id) { - Q_D(KMyMoneySelector); - QTreeWidgetItem* item = new QTreeWidgetItem(d->m_treeWidget, after); + QTreeWidgetItem* item = new QTreeWidgetItem(m_treeWidget, after); item->setText(0, name); item->setData(0, KeyRole, key); item->setData(0, IdRole, id); item->setText(1, key); // hidden, but used for sorting item->setFlags(item->flags() & ~Qt::ItemIsUserCheckable); if (id.isEmpty()) { QFont font = item->font(0); font.setBold(true); item->setFont(0, font); setSelectable(item, false); } item->setExpanded(true); return item; } -QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, QTreeWidgetItem* after, const QString& key) -{ - return newItem(name, after, key, QString()); -} - -QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, QTreeWidgetItem* after) -{ - return newItem(name, after, QString(), QString()); -} - QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, const QString& key, const QString& id) { return newItem(name, 0, key, id); } -QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name, const QString& key) -{ - return newItem(name, 0, key, QString()); -} - -QTreeWidgetItem* KMyMoneySelector::newItem(const QString& name) -{ - return newItem(name, 0, QString(), QString()); -} - QTreeWidgetItem* KMyMoneySelector::newTopItem(const QString& name, const QString& key, const QString& id) { - Q_D(KMyMoneySelector); - QTreeWidgetItem* item = new QTreeWidgetItem(d->m_treeWidget); + QTreeWidgetItem* item = new QTreeWidgetItem(m_treeWidget); item->setText(0, name); item->setData(0, KeyRole, key); item->setData(0, IdRole, id); item->setText(1, key); // hidden, but used for sorting item->setFlags(item->flags() & ~Qt::ItemIsUserCheckable); - if (d->m_selMode == QTreeWidget::MultiSelection) { + if (m_selMode == QTreeWidget::MultiSelection) { item->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setCheckState(0, Qt::Checked); } return item; } QTreeWidgetItem* KMyMoneySelector::newItem(QTreeWidgetItem* parent, const QString& name, const QString& key, const QString& id) { - Q_D(KMyMoneySelector); QTreeWidgetItem* item = new QTreeWidgetItem(parent); item->setText(0, name); item->setData(0, KeyRole, key); item->setData(0, IdRole, id); item->setText(1, key); // hidden, but used for sorting item->setFlags(item->flags() & ~Qt::ItemIsUserCheckable); - if (d->m_selMode == QTreeWidget::MultiSelection) { + if (m_selMode == QTreeWidget::MultiSelection) { item->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setCheckState(0, Qt::Checked); } return item; } void KMyMoneySelector::protectItem(const QString& itemId, const bool protect) { - Q_D(KMyMoneySelector); - QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); + QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; // scan items while ((it_v = *it) != 0) { if (it_v->data(0, IdRole).toString() == itemId) { setSelectable(it_v, !protect); break; } ++it; } } QTreeWidgetItem* KMyMoneySelector::item(const QString& id) const { - Q_D(const KMyMoneySelector); - QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); + QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; while ((it_v = *it) != 0) { if (it_v->data(0, IdRole).toString() == id) break; ++it; } return it_v; } bool KMyMoneySelector::allItemsSelected() const { - Q_D(const KMyMoneySelector); - QTreeWidgetItem* rootItem = d->m_treeWidget->invisibleRootItem(); + QTreeWidgetItem* rootItem = m_treeWidget->invisibleRootItem(); - if (d->m_selMode == QTreeWidget::SingleSelection) + if (m_selMode == QTreeWidget::SingleSelection) return false; - for (auto i = 0; i < rootItem->childCount(); ++i) { + for (int i = 0; i < rootItem->childCount(); ++i) { QTreeWidgetItem* item = rootItem->child(i); if (item->flags().testFlag(Qt::ItemIsUserCheckable)) { if (!(item->checkState(0) == Qt::Checked && allItemsSelected(item))) return false; } else { if (!allItemsSelected(item)) return false; } } return true; } bool KMyMoneySelector::allItemsSelected(const QTreeWidgetItem *item) const { - for (auto i = 0; i < item->childCount(); ++i) { + for (int i = 0; i < item->childCount(); ++i) { QTreeWidgetItem* child = item->child(i); if (child->flags().testFlag(Qt::ItemIsUserCheckable)) { if (!(child->checkState(0) == Qt::Checked && allItemsSelected(child))) return false; } } return true; } void KMyMoneySelector::removeItem(const QString& id) { - Q_D(KMyMoneySelector); QTreeWidgetItem* it_v; - QTreeWidgetItemIterator it(d->m_treeWidget); + QTreeWidgetItemIterator it(m_treeWidget); while ((it_v = *it) != 0) { if (id == it_v->data(0, IdRole).toString()) { if (it_v->childCount() > 0) { setSelectable(it_v, false); } else { delete it_v; } } it++; } // get rid of top items that just lost the last children (e.g. Favorites) - it = QTreeWidgetItemIterator(d->m_treeWidget, QTreeWidgetItemIterator::NotSelectable); + it = QTreeWidgetItemIterator(m_treeWidget, QTreeWidgetItemIterator::NotSelectable); while ((it_v = *it) != 0) { if (it_v->childCount() == 0) delete it_v; it++; } } void KMyMoneySelector::selectAllItems(const bool state) { - Q_D(KMyMoneySelector); - selectAllSubItems(d->m_treeWidget->invisibleRootItem(), state); + selectAllSubItems(m_treeWidget->invisibleRootItem(), state); emit stateChanged(); } void KMyMoneySelector::selectItems(const QStringList& itemList, const bool state) { - Q_D(KMyMoneySelector); - selectSubItems(d->m_treeWidget->invisibleRootItem(), itemList, state); + selectSubItems(m_treeWidget->invisibleRootItem(), itemList, state); emit stateChanged(); } void KMyMoneySelector::selectSubItems(QTreeWidgetItem* item, const QStringList& itemList, const bool state) { - for (auto i = 0; i < item->childCount(); ++i) { + for (int i = 0; i < item->childCount(); ++i) { QTreeWidgetItem* child = item->child(i); if (child->flags().testFlag(Qt::ItemIsUserCheckable) && itemList.contains(child->data(0, IdRole).toString())) { child->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); } selectSubItems(child, itemList, state); } emit stateChanged(); } void KMyMoneySelector::selectAllSubItems(QTreeWidgetItem* item, const bool state) { - for (auto i = 0; i < item->childCount(); ++i) { + for (int i = 0; i < item->childCount(); ++i) { QTreeWidgetItem* child = item->child(i); if (child->flags().testFlag(Qt::ItemIsUserCheckable)) { child->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); } selectAllSubItems(child, state); } emit stateChanged(); } void KMyMoneySelector::selectedItems(QStringList& list) const { - Q_D(const KMyMoneySelector); list.clear(); - if (d->m_selMode == QTreeWidget::SingleSelection) { - QTreeWidgetItem* it_c = d->m_treeWidget->currentItem(); + if (m_selMode == QTreeWidget::SingleSelection) { + QTreeWidgetItem* it_c = m_treeWidget->currentItem(); if (it_c != 0) list << it_c->data(0, IdRole).toString(); } else { - QTreeWidgetItem* rootItem = d->m_treeWidget->invisibleRootItem(); - for (auto i = 0; i < rootItem->childCount(); ++i) { + QTreeWidgetItem* rootItem = m_treeWidget->invisibleRootItem(); + for (int i = 0; i < rootItem->childCount(); ++i) { QTreeWidgetItem* child = rootItem->child(i); if (child->flags().testFlag(Qt::ItemIsUserCheckable)) { if (child->checkState(0) == Qt::Checked) list << child->data(0, IdRole).toString(); } selectedItems(list, child); } } } void KMyMoneySelector::selectedItems(QStringList& list, QTreeWidgetItem* item) const { - for (auto i = 0; i < item->childCount(); ++i) { + for (int i = 0; i < item->childCount(); ++i) { QTreeWidgetItem* child = item->child(i); if (child->flags().testFlag(Qt::ItemIsUserCheckable)) { if (child->checkState(0) == Qt::Checked) list << child->data(0, IdRole).toString(); } selectedItems(list, child); } } void KMyMoneySelector::itemList(QStringList& list) const { - Q_D(const KMyMoneySelector); - QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); + QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; while ((it_v = *it) != 0) { list << it_v->data(0, IdRole).toString(); it++; } } void KMyMoneySelector::setSelected(const QString& id, const bool state) { - Q_D(const KMyMoneySelector); - QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); + QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* item; QTreeWidgetItem* it_visible = 0; while ((item = *it) != 0) { if (item->data(0, IdRole).toString() == id) { if (item->flags().testFlag(Qt::ItemIsUserCheckable)) { item->setCheckState(0, state ? Qt::Checked : Qt::Unchecked); } - d->m_treeWidget->setCurrentItem(item); + m_treeWidget->setCurrentItem(item); if (!it_visible) it_visible = item; } it++; } // make sure the first one found is visible if (it_visible) - d->m_treeWidget->scrollToItem(it_visible); -} - -QTreeWidget* KMyMoneySelector::listView() const -{ - Q_D(const KMyMoneySelector); - return d->m_treeWidget; + m_treeWidget->scrollToItem(it_visible); } int KMyMoneySelector::slotMakeCompletion(const QString& _txt) { QString txt(QRegExp::escape(_txt)); if (KMyMoneyGlobalSettings::stringMatchFromStart() && QLatin1String(this->metaObject()->className()) == QLatin1String("KMyMoneySelector")) txt.prepend('^'); return slotMakeCompletion(QRegExp(txt, Qt::CaseInsensitive)); } bool KMyMoneySelector::match(const QRegExp& exp, QTreeWidgetItem* item) const { return exp.indexIn(item->text(0)) != -1; } int KMyMoneySelector::slotMakeCompletion(const QRegExp& exp) { - Q_D(KMyMoneySelector); - QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); + QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; // The logic used here seems to be awkward. The problem is, that // QListViewItem::setVisible works recursively on all it's children // and grand-children. // // The way out of this is as follows: Make all items visible. // Then go through the list again and perform the checks. // If an item does not have any children (last leaf in the tree view) // perform the check. Then check recursively on the parent of this // leaf that it has no visible children. If that is the case, make the // parent invisible and continue this check with it's parent. while ((it_v = *it) != 0) { it_v->setHidden(false); ++it; } QTreeWidgetItem* firstMatch = 0; if (!exp.pattern().isEmpty()) { - it = QTreeWidgetItemIterator(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); + it = QTreeWidgetItemIterator(m_treeWidget, QTreeWidgetItemIterator::Selectable); while ((it_v = *it) != 0) { if (it_v->childCount() == 0) { if (!match(exp, it_v)) { // this is a node which does not contain the // text and does not have children. So we can // safely hide it. Then we check, if the parent // has more children which are still visible. If // none are found, the parent node is hidden also. We // continue until the top of the tree or until we // find a node that still has visible children. bool hide = true; while (hide) { it_v->setHidden(true); it_v = it_v->parent(); if (it_v && (it_v->flags() & Qt::ItemIsSelectable)) { hide = !match(exp, it_v); - for (auto i = 0; hide && i < it_v->childCount(); ++i) { + for (int i = 0; hide && i < it_v->childCount(); ++i) { if (!it_v->child(i)->isHidden()) hide = false; } } else hide = false; } } else if (!firstMatch) { firstMatch = it_v; } ++it; } else if (match(exp, it_v)) { if (!firstMatch) { firstMatch = it_v; } // a node with children contains the text. We want // to display all child nodes in this case, so we need // to advance the iterator to the next sibling of the // current node. This could well be the sibling of a // parent or grandparent node. QTreeWidgetItem* curr = it_v; QTreeWidgetItem* item; while ((item = curr->treeWidget()->itemBelow(curr)) == 0) { curr = curr->parent(); if (curr == 0) break; if (match(exp, curr)) firstMatch = curr; } do { ++it; } while (*it && *it != item); } else { // It's a node with children that does not match. We don't // change it's status here. ++it; } } } // make the first match the one that is selected // if we have no match, make sure none is selected - if (d->m_selMode == QTreeWidget::SingleSelection) { + if (m_selMode == QTreeWidget::SingleSelection) { if (firstMatch) { - d->m_treeWidget->setCurrentItem(firstMatch); - d->m_treeWidget->scrollToItem(firstMatch); + m_treeWidget->setCurrentItem(firstMatch); + m_treeWidget->scrollToItem(firstMatch); } else - d->m_treeWidget->clearSelection(); + m_treeWidget->clearSelection(); } // Get the number of visible nodes for the return code int cnt = 0; - it = QTreeWidgetItemIterator(d->m_treeWidget, QTreeWidgetItemIterator::Selectable | QTreeWidgetItemIterator::NotHidden); + it = QTreeWidgetItemIterator(m_treeWidget, QTreeWidgetItemIterator::Selectable | QTreeWidgetItemIterator::NotHidden); while ((it_v = *it) != 0) { cnt++; it++; } return cnt; } bool KMyMoneySelector::contains(const QString& txt) const { - Q_D(const KMyMoneySelector); - QTreeWidgetItemIterator it(d->m_treeWidget, QTreeWidgetItemIterator::Selectable); + QTreeWidgetItemIterator it(m_treeWidget, QTreeWidgetItemIterator::Selectable); QTreeWidgetItem* it_v; while ((it_v = *it) != 0) { if (it_v->text(0) == txt) { return true; } it++; } return false; } void KMyMoneySelector::slotItemPressed(QTreeWidgetItem* item, int /* col */) { - Q_D(KMyMoneySelector); if (QApplication::mouseButtons() != Qt::RightButton) return; if (item->flags().testFlag(Qt::ItemIsUserCheckable)) { QStyleOptionButton opt; - opt.rect = d->m_treeWidget->visualItemRect(item); - QRect rect = d->m_treeWidget->style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt, d->m_treeWidget); - if (rect.contains(d->m_treeWidget->mapFromGlobal(QCursor::pos()))) { + opt.rect = m_treeWidget->visualItemRect(item); + QRect rect = m_treeWidget->style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt, m_treeWidget); + if (rect.contains(m_treeWidget->mapFromGlobal(QCursor::pos()))) { // we get down here, if we have a right click onto the checkbox item->setCheckState(0, item->checkState(0) == Qt::Checked ? Qt::Unchecked : Qt::Checked); selectAllSubItems(item, item->checkState(0) == Qt::Checked); } } } QStringList KMyMoneySelector::selectedItems() const { QStringList list; selectedItems(list); return list; } QStringList KMyMoneySelector::itemList() const { QStringList list; itemList(list); return list; } diff --git a/kmymoney/widgets/kmymoneyselector.h b/kmymoney/widgets/kmymoneyselector.h index 746f8a087..ba073c029 100644 --- a/kmymoney/widgets/kmymoneyselector.h +++ b/kmymoney/widgets/kmymoneyselector.h @@ -1,370 +1,371 @@ /*************************************************************************** kmymoneyselector.h ------------------- begin : Thu Jun 29 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYSELECTOR_H #define KMYMONEYSELECTOR_H // ---------------------------------------------------------------------------- // QT Includes #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmm_widgets_export.h" class QHBoxLayout; /** * This class implements a general selector for id based objects. It is based * on a tree view. Using this widget, one can select one or multiple * items depending on the mode of operation and the set of items * selected to be displayed. (see setSelectionMode() ). * * - Single selection mode\n * In this mode the widget allows to select a single entry out of * the set of displayed items. * * - Multi selection mode\n * In this mode, the widget allows to select one or more entries * out of the set of displayed items. Selection is performed * by marking the item in the view. */ -class KMyMoneySelectorPrivate; class KMM_WIDGETS_EXPORT KMyMoneySelector : public QWidget { Q_OBJECT - Q_DISABLE_COPY(KMyMoneySelector) - Q_PROPERTY(QStringList selectedItems READ selectedItems DESIGNABLE false STORED false) public: - explicit KMyMoneySelector(QWidget* parent = nullptr, Qt::WindowFlags flags = 0); + explicit KMyMoneySelector(QWidget *parent = 0, Qt::WindowFlags flags = 0); virtual ~KMyMoneySelector(); enum KMyMoneySelectorItemRoles { IdRole = Qt::UserRole, /**< The id is stored in this role in column 0 as a string.*/ KeyRole = Qt::UserRole + 1, /**< The key is stored in this role in column 0 as a string.*/ }; /** * This method sets the mode of operation of this widget. * Supported values are @p QListView::Single and @p QListView::Multi. * * @param mode @p QListView::Single selects single selection mode and * @p QListView::Multi selects multi selection mode * * @note When the widget is created, it defaults to QListView::Single. * Any items loaded into the widget will be cleared if the mode changes. * Changing the selection mode also changes the type of the items * created through newItem(). You should therefor set the selection mode * before you create items. */ void setSelectionMode(const QTreeWidget::SelectionMode mode); /** * returns the selection mode of the widget. * * @sa setSelectionMode() */ - QTreeWidget::SelectionMode selectionMode() const; + QTreeWidget::SelectionMode selectionMode() const { + return m_selMode; + } /** * This method returns the list of selected item ids. If * no item is selected, the list is empty. The list is cleared * when the method is called. * * @param list reference to id list */ void selectedItems(QStringList& list) const; /** * Convenience method for above method. Requires more resources. * Provided only for backward compatibility. * * @todo Deprecated after 1.0 */ QStringList selectedItems() const; /** * This method returns the list of all item ids. * The list is cleared when the method is called. * * @param list reference to id list */ void itemList(QStringList& list) const; /** * Convenience method for above method. Requires more resources. * Provided only for backward compatibility. * * @todo Deprecated after 1.0 */ QStringList itemList() const; /** * This method returns an information if all items * currently shown are selected or not. * * @retval true All items shown are selected * @retval false Not all items are selected * * @note If the selection mode is set to Single, this * method always returns false. */ bool allItemsSelected() const; /** * This method sets the current selected item and marks the * checkbox according to @p state in multi-selection-mode. * * @param id id of account * @param state state of checkbox in multi-selection-mode * @p true checked * @p false not checked (default) */ void setSelected(const QString& id, const bool state = false); /** * Return a pointer to the QTreeWidget object */ - QTreeWidget* listView() const; + QTreeWidget* listView() const { + return m_treeWidget; + }; /** * This method selects/deselects all items that * are currently in the item list according * to the parameter @p state. * * @param state select items if @p true, deselect otherwise */ void selectAllItems(const bool state); /** * This method selects/deselects all items that * are currently in this object's item list AND are present in the supplied * @p itemList of items to select, according to the @p state. * * @param itemList of item ids to apply @p state to * @param state select items if @p true, deselect otherwise */ void selectItems(const QStringList& itemList, const bool state); /** * Protect an entry from selection. Protection is controlled by * the parameter @p protect. * * @param itemId id of item for which to modify the protection * @param protect if true, the entry specified by @p accId cannot be * selected. If false, it can be selected. Defaults to @p true. */ void protectItem(const QString& itemId, const bool protect = true); /** * This method removes an item with a given id from the list. * * @param id QString containing id of item to be removed */ void removeItem(const QString& id); /** * This method creates a new top level KMyMoneyTreeWidgetItem object in the list view. * The type can be influenced with the @a type argument. It defaults * to QCheckListItem::RadioButtonController. If @a id is empty, the item is not * selectable. It will be shown 'opened' (see QListViewItem::setOpen()) * * @return pointer to newly created object */ - QTreeWidgetItem* newItem(const QString& name, const QString& key, const QString& id); - QTreeWidgetItem* newItem(const QString& name, const QString& key); - QTreeWidgetItem* newItem(const QString& name); + QTreeWidgetItem* newItem(const QString& name, const QString& key = QString(), const QString& id = QString()); /** * Same as above, but create the item following the item pointed to by @c after. * If @c after is 0, then behave as previous method */ - QTreeWidgetItem* newItem(const QString& name, QTreeWidgetItem* after, const QString& key, const QString& id); - QTreeWidgetItem* newItem(const QString& name, QTreeWidgetItem* after, const QString& key); - QTreeWidgetItem* newItem(const QString& name, QTreeWidgetItem* after); + QTreeWidgetItem* newItem(const QString& name, QTreeWidgetItem* after, const QString& key = QString(), const QString& id = QString()); /** * This method creates a new selectable object depending on the * selection mode. This is either a QTreeWidgetItem for single * selection mode or a KMyMoneyTreeWidgetItem for multi selection mode * * @note The new item will be the first one in the selection * * @param parent pointer to parent item * @param name the displayed name * @param key string to be used for completion. If empty defaults to @a name * @param id the id used to identify the objects * * @return pointer to newly created object */ QTreeWidgetItem* newItem(QTreeWidgetItem* parent, const QString& name, const QString& key, const QString& id); /** * This method creates a new selectable object depending on the * selection mode. This is either a QTreeWidgetItem for single * selection mode or a KMyMoneyTreeWidgetItem for multi selection mode. * In contrast to the above method, the parent is always the view. * * @note The new item will be the first one in the selection * * @param name the displayed name * @param key string to be used for completion. If empty defaults to @a name * @param id the id used to identify the objects * * @return pointer to newly created object */ QTreeWidgetItem* newTopItem(const QString& name, const QString& key, const QString& id); /** * This method checks if a given @a item matches the given regular expression @a exp. * * @param exp const reference to a regular expression object * @param item pointer to QListViewItem * * @retval true item matches * @retval false item does not match */ virtual bool match(const QRegExp& exp, QTreeWidgetItem* item) const; /** * This method returns a pointer to the QListViewItem with the id @a id. * If such an item is not contained in the list, @a 0 will be returned. * * @param id id to be used to find a QListViewItem pointer for */ QTreeWidgetItem* item(const QString& id) const; /** * This method returns, if any of the items in the selector contains * the text @a txt. * * @param txt const reference to string to be looked for * @retval true exact match found * @retval false no match found */ virtual bool contains(const QString& txt) const; /** * Clears all items of the selector and the associated listview. */ virtual void clear(); /** * Sets the give item's selectable state. */ void setSelectable(QTreeWidgetItem *item, bool selectable); public slots: /** * This slot selects all items that are currently in * the item list of the widget. */ - void slotSelectAllItems(); + void slotSelectAllItems() { + selectAllItems(true); + }; /** * This slot deselects all items that are currently in * the item list of the widget. */ - void slotDeselectAllItems(); + void slotDeselectAllItems() { + selectAllItems(false); + }; signals: void stateChanged(); void itemSelected(const QString& id); protected: /** * Helper method for selectedItems() to traverse the tree. * * @param list list of selected ids * @param item pointer to item to start with */ void selectedItems(QStringList& list, QTreeWidgetItem* item) const; /** * Helper method for allItemsSelected() to traverse the tree. * * @param item pointer to item to start with */ bool allItemsSelected(const QTreeWidgetItem *item) const; /** * This is a helper method for selectAllItems(). * * @param item pointer to item to start with * @param state selection state (@a true = selected, @a false = not selected) */ void selectAllSubItems(QTreeWidgetItem* item, const bool state); /** * This is a helper method for selectItems(). * * @param item pointer to item to start with * @param itemList list of ids to be selected * @param state selection state (@a true = selected, @a false = not selected) */ void selectSubItems(QTreeWidgetItem* item, const QStringList& itemList, const bool state); public slots: /** * Hide all listview items that do not match the regular expression @a exp. * This method returns the number of visible items * * @param exp const reference to QRegExp that an item must match to stay visible * * @return number of visible items */ int slotMakeCompletion(const QRegExp& exp); /** * This is an overloaded member function, provided for convenience. It behaves essentially like the above function. * * @param txt contains the pattern for a QRegExp */ int slotMakeCompletion(const QString& txt); protected slots: /** * This slot is connected to the QTreeWidget executed signal */ void slotItemSelected(QTreeWidgetItem *it_v); /** * This slot processes the right mouse button press on a list view item. * * @param item pointer to the item where the mouse was pressed * @param col the column of the tree (unused here) */ void slotItemPressed(QTreeWidgetItem* item, int col); protected: - KMyMoneySelectorPrivate * const d_ptr; - KMyMoneySelector(KMyMoneySelectorPrivate &dd, QWidget* parent = nullptr, Qt::WindowFlags flags = 0); - -private: - Q_DECLARE_PRIVATE(KMyMoneySelector) + QTreeWidget* m_treeWidget; + QStringList m_itemList; + QString m_baseName; + QTreeWidget::SelectionMode m_selMode; + QHBoxLayout* m_layout; }; #endif diff --git a/kmymoney/widgets/kmymoneyselector_p.h b/kmymoney/widgets/kmymoneyselector_p.h deleted file mode 100644 index 8348f80e5..000000000 --- a/kmymoney/widgets/kmymoneyselector_p.h +++ /dev/null @@ -1,92 +0,0 @@ -/*************************************************************************** - kmymoneyselector_p.h - ------------------- - begin : Thu Jun 29 2006 - copyright : (C) 2006 by Thomas Baumgart - email : Thomas Baumgart - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KMYMONEYSELECTOR_P_H -#define KMYMONEYSELECTOR_P_H - -#include "kmymoneyselector.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include -#include -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -class QHBoxLayout; - -class KMyMoneySelectorPrivate -{ - Q_DISABLE_COPY(KMyMoneySelectorPrivate) - Q_DECLARE_PUBLIC(KMyMoneySelector) - -public: - - KMyMoneySelectorPrivate(KMyMoneySelector *qq) : - q_ptr(qq), - m_treeWidget(nullptr), - m_layout(nullptr) - { - } - - void init() - { - Q_Q(KMyMoneySelector); - q->setAutoFillBackground(true); - - m_selMode = QTreeWidget::SingleSelection; - - m_treeWidget = new QTreeWidget(q); - // don't show horizontal scroll bar - m_treeWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - m_treeWidget->setSortingEnabled(false); - m_treeWidget->setAlternatingRowColors(true); - - m_treeWidget->setAllColumnsShowFocus(true); - - m_layout = new QHBoxLayout(q); - m_layout->setSpacing(0); - m_layout->setMargin(0); - - m_treeWidget->header()->hide(); - - m_layout->addWidget(m_treeWidget); - - // force init - m_selMode = QTreeWidget::MultiSelection; - q->setSelectionMode(QTreeWidget::SingleSelection); - - q->connect(m_treeWidget, &QTreeWidget::itemPressed, q, &KMyMoneySelector::slotItemPressed); - q->connect(m_treeWidget, &QTreeWidget::itemChanged, q, &KMyMoneySelector::stateChanged); - } - - KMyMoneySelector *q_ptr; - QTreeWidget* m_treeWidget; - QStringList m_itemList; - QString m_baseName; - QTreeWidget::SelectionMode m_selMode; - QHBoxLayout* m_layout; -}; - -#endif diff --git a/kmymoney/widgets/kmymoneytagcombo.cpp b/kmymoney/widgets/kmymoneytagcombo.cpp deleted file mode 100644 index cf6669285..000000000 --- a/kmymoney/widgets/kmymoneytagcombo.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/*************************************************************************** - kmymoneytagcombo.cpp - description - ------------------- - begin : Sat Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "kmymoneytagcombo.h" -#include "kmymoneymvccombo_p.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -#include -#include - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "mymoneytag.h" - -class KMyMoneyTagComboPrivate : public KMyMoneyMVCComboPrivate -{ - Q_DISABLE_COPY(KMyMoneyTagComboPrivate) - -public: - KMyMoneyTagComboPrivate() : - KMyMoneyMVCComboPrivate() - { - } - - QList m_usedIdList; - QList m_usedTagNameList; - QList m_closedIdList; - QList m_closedTagNameList; -}; - -KMyMoneyTagCombo::KMyMoneyTagCombo(QWidget* parent) : - KMyMoneyMVCCombo(*new KMyMoneyTagComboPrivate, true, parent) -{ -} - -KMyMoneyTagCombo::~KMyMoneyTagCombo() -{ -} - -void KMyMoneyTagCombo::loadTags(const QList& list) -{ - Q_D(KMyMoneyTagCombo); - clear(); - - //add a blank item, since the field is optional - addItem(QString(), QVariant(QString())); - - //add all not closed tags - QList::const_iterator it; - for (it = list.constBegin(); it != list.constEnd(); ++it) { - if (!(*it).isClosed()) - addItem((*it).name(), QVariant((*it).id())); - else { - d->m_closedIdList.append((*it).id()); - d->m_closedTagNameList.append((*it).name()); - } - } - - //sort the model, which will sort the list in the combo - model()->sort(Qt::DisplayRole, Qt::AscendingOrder); - - //set the text to empty and the index to the first item on the list - setCurrentIndex(0); - clearEditText(); -} - -void KMyMoneyTagCombo::setUsedTagList(QList& usedIdList, QList& usedTagNameList) -{ - Q_D(KMyMoneyTagCombo); - d->m_usedIdList = usedIdList; - d->m_usedTagNameList = usedTagNameList; - for (auto i = 0; i < d->m_usedIdList.size(); ++i) { - int index = findData(QVariant(d->m_usedIdList.at(i)), Qt::UserRole, Qt::MatchExactly); - if (index != -1) removeItem(index); - } -} - -void KMyMoneyTagCombo::checkCurrentText() -{ - Q_D(KMyMoneyTagCombo); - if (!contains(currentText())) { - if (d->m_closedTagNameList.contains(currentText())) { - // Tell the user what's happened - QString msg = QString("") + i18n("Closed tags cannot be used.") + QString(""); - KMessageBox::information(this, msg, i18n("Closed tag"), "Closed tag"); - setCurrentText(); - return; - } else if (d->m_usedTagNameList.contains(currentText())) { - // Tell the user what's happened - QString msg = QString("") + i18n("The tag is already present.") + QString(""); - KMessageBox::information(this, msg, i18n("Duplicate tag"), "Duplicate tag"); - setCurrentText(); - return; - } - QString id; - // annouce that we go into a possible dialog to create an object - // This can be used by upstream widgets to disable filters etc. - emit objectCreation(true); - - emit createItem(currentText(), id); - - // Announce that we return from object creation - emit objectCreation(false); - - // update the field to a possibly created object - //m_id = id; - setCurrentTextById(id); - } -} diff --git a/kmymoney/widgets/kmymoneytagcombo.h b/kmymoney/widgets/kmymoneytagcombo.h deleted file mode 100644 index 92621ab88..000000000 --- a/kmymoney/widgets/kmymoneytagcombo.h +++ /dev/null @@ -1,71 +0,0 @@ -/*************************************************************************** - kmymoneytagcombo.h - description - ------------------- - begin : Mon Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KMYMONEYTAGCOMBO_H -#define KMYMONEYTAGCOMBO_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmymoneymvccombo.h" - -class MyMoneyTag; - -/** - * This class implements a text based tag selector. - * The widget has the functionality of a KMyMoneyPayeeCombo object. - * Whenever a key is pressed, the set of loaded tags is searched for - * tags names which match the currently entered text. - * - * @author Alessandro Russo - */ -class KMyMoneyTagComboPrivate; -class KMM_WIDGETS_EXPORT KMyMoneyTagCombo : public KMyMoneyMVCCombo -{ - Q_OBJECT - Q_DISABLE_COPY(KMyMoneyTagCombo) - -public: - explicit KMyMoneyTagCombo(QWidget* parent = nullptr); - ~KMyMoneyTagCombo() override; - - void loadTags(const QList& list); - /** ids in usedIdList are escluded from the internal list - * you should call loadTags before calling setUsedTagList because it doesn't readd - * tag removed in previous call*/ - void setUsedTagList(QList& usedIdList, QList& usedTagNameList); - -protected: - /** - * check if the current text is contained in the internal list, if not ask the user if want to create a new item. - */ - virtual void checkCurrentText(); - -private: - Q_DECLARE_PRIVATE(KMyMoneyTagCombo) -}; - -#endif diff --git a/kmymoney/widgets/kmymoneytextedit.cpp b/kmymoney/widgets/kmymoneytextedit.cpp index 81322de44..f5e667d03 100644 --- a/kmymoney/widgets/kmymoneytextedit.cpp +++ b/kmymoney/widgets/kmymoneytextedit.cpp @@ -1,226 +1,226 @@ /* This file is part of KMyMoney, A Personal Finance Manager by KDE Copyright (C) 2013 Christian Dávid This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "kmymoneytextedit.h" #include "kmymoneytextedit_p.h" #include /* The Higligther */ KMyMoneyTextEditHighlighter::KMyMoneyTextEditHighlighter(QTextEdit * parent) : Highlighter(parent), - m_allowedChars(QString(QString())), + m_allowedChars(QString("")), m_maxLines(-1), m_maxLineLength(-1), m_maxLength(-1) { } void KMyMoneyTextEditHighlighter::setAllowedChars(const QString& chars) { m_allowedChars = chars; rehighlight(); } void KMyMoneyTextEditHighlighter::setMaxLength(const int& length) { m_maxLength = length; rehighlight(); } void KMyMoneyTextEditHighlighter::setMaxLines(const int& lines) { m_maxLines = lines; rehighlight(); } void KMyMoneyTextEditHighlighter::setMaxLineLength(const int& length) { m_maxLineLength = length; rehighlight(); } void KMyMoneyTextEdit::setReadOnly(bool readOnly) { KTextEdit::setReadOnly(readOnly); } void KMyMoneyTextEditHighlighter::highlightBlock(const QString& text) { // Spell checker first Highlighter::highlightBlock(text); QTextCharFormat invalidFormat; invalidFormat.setFontItalic(true); invalidFormat.setForeground(Qt::red); invalidFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline); // Check used characters const int length = text.length(); - for (auto i = 0; i < length; ++i) { + for (int i = 0; i < length; ++i) { if (!m_allowedChars.contains(text.at(i))) { setFormat(i, 1, invalidFormat); } } if (m_maxLines != -1) { //! @todo Is using QTextBlock::blockNumber() as line number dangerous? if (currentBlock().blockNumber() >= m_maxLines) { setFormat(0, length, invalidFormat); return; } } if (m_maxLength != -1) { const int blockPosition = currentBlock().position(); if (m_maxLength < (length + blockPosition)) { setFormat(m_maxLength, length - m_maxLength - blockPosition, invalidFormat); return; } } if (m_maxLineLength != -1 && length >= m_maxLineLength) { setFormat(m_maxLineLength, length - m_maxLineLength, invalidFormat); return; } } /* KMyMoneyTextEdit */ KMyMoneyTextEdit::KMyMoneyTextEdit(QWidget* parent) : KTextEdit(parent), m_maxLength(-1), m_maxLineLength(-1), m_maxLines(-1), - m_allowedChars(QString(QString())), + m_allowedChars(QString("")), m_highligther(0) { setWordWrapMode(QTextOption::ManualWrap); m_highligther = new KMyMoneyTextEditHighlighter(this); } bool KMyMoneyTextEdit::isEventAllowed(QKeyEvent* e) const { const QString text = e->text(); if (!text.isEmpty()) { if (text.at(0).isPrint()) { if (!m_allowedChars.contains(text)) return false; // Do not check max lengths etc if something is replaced if (textCursor().hasSelection()) return true; const QString plainText = toPlainText(); if (m_maxLength != -1 && plainText.length() >= m_maxLength) return false; if (m_maxLineLength != -1 && textCursor().block().length() - 1 >= m_maxLineLength) return false; } else if (m_maxLines != -1 && text.at(0) == '\r' && toPlainText().count('\n') + 1 >= m_maxLines) { // Does this work on non-linux OSes as well? return false; } } return true; } bool KMyMoneyTextEdit::isValid() const { const QString text = toPlainText(); if (m_maxLength != -1 && text.length() >= m_maxLength) return false; const QStringList lines = text.split('\n'); if (m_maxLines != -1 && lines.count() >= m_maxLines) { return false; } if (m_maxLineLength != -1) { foreach (QString line, lines) { if (line.length() > m_maxLineLength) return false; } } const int length = text.length(); - for (auto i = 0; i < length; ++i) { + for (int i = 0; i < length; ++i) { if (!m_allowedChars.contains(text.at(i))) return false; } return true; } void KMyMoneyTextEdit::keyReleaseEvent(QKeyEvent* e) { if (isEventAllowed(e)) KTextEdit::keyReleaseEvent(e); } void KMyMoneyTextEdit::keyPressEvent(QKeyEvent* e) { if (isEventAllowed(e)) KTextEdit::keyPressEvent(e); } int KMyMoneyTextEdit::maxLength() const { return m_maxLength; } void KMyMoneyTextEdit::setMaxLength(const int& maxLength) { m_maxLength = maxLength; m_highligther->setMaxLength(m_maxLength); } int KMyMoneyTextEdit::maxLineLength() const { return m_maxLineLength; } void KMyMoneyTextEdit::setMaxLineLength(const int& maxLineLength) { m_maxLineLength = maxLineLength; m_highligther->setMaxLineLength(maxLineLength); } int KMyMoneyTextEdit::maxLines() const { return m_maxLines; } void KMyMoneyTextEdit::setMaxLines(const int& maxLines) { m_maxLines = maxLines; m_highligther->setMaxLines(maxLines); } QString KMyMoneyTextEdit::allowedChars() const { return m_allowedChars; } void KMyMoneyTextEdit::setAllowedChars(const QString& allowedChars) { m_allowedChars = allowedChars; m_highligther->setAllowedChars(allowedChars); } diff --git a/kmymoney/widgets/kmymoneytextedit.h b/kmymoney/widgets/kmymoneytextedit.h index 16cb8e6f4..a1f24a097 100644 --- a/kmymoney/widgets/kmymoneytextedit.h +++ b/kmymoney/widgets/kmymoneytextedit.h @@ -1,92 +1,92 @@ /* This file is part of KMyMoney, A Personal Finance Manager by KDE Copyright (C) 2013 Christian Dávid This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KMYMONEYTEXTEDIT_H #define KMYMONEYTEXTEDIT_H #include #include "kmm_widgets_export.h" class KMyMoneyTextEditHighlighter; /** * @brief KTextEdit with restricted character set and length * * Used to set constraints on input. It allows to set readOnly property by * slots as well (not possible with KTextEdit). */ class KMM_WIDGETS_EXPORT KMyMoneyTextEdit : public KTextEdit { Q_OBJECT /** * @brief Maximal number of characters allowed */ Q_PROPERTY(int maxLength READ maxLength WRITE setMaxLength) /** * @brief Maximal number of characters allowed per line */ Q_PROPERTY(int maxLineLength READ maxLineLength WRITE setMaxLineLength) /** * @brief Maximal number of lines */ Q_PROPERTY(int maxLines READ maxLines WRITE setMaxLines) /** * @brief List of all allowed chars */ Q_PROPERTY(QString allowedChars READ allowedChars WRITE setAllowedChars) Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly); public: - KMyMoneyTextEdit(QWidget* parent = nullptr); + KMyMoneyTextEdit(QWidget* parent = 0); int maxLength() const; int maxLineLength() const; int maxLines() const; QString allowedChars() const; bool isValid() const; public Q_SLOTS: void setMaxLength(const int& maxLength); void setMaxLineLength(const int& maxLineLength); void setMaxLines(const int& maxLines); void setAllowedChars(const QString& allowedChars); /** @brief Slot to set this text edit read only */ void setReadOnly(bool); protected: virtual void keyReleaseEvent(QKeyEvent* e); virtual void keyPressEvent(QKeyEvent* e); private: bool isEventAllowed(QKeyEvent* e) const; int m_maxLength; int m_maxLineLength; int m_maxLines; QString m_allowedChars; KMyMoneyTextEditHighlighter* m_highligther; }; #endif // KMYMONEYTEXTEDIT_H diff --git a/kmymoney/widgets/kmymoneytitlelabel.h b/kmymoney/widgets/kmymoneytitlelabel.h index 2c9306437..82a962e0c 100644 --- a/kmymoney/widgets/kmymoneytitlelabel.h +++ b/kmymoney/widgets/kmymoneytitlelabel.h @@ -1,85 +1,85 @@ /*************************************************************************** kmymoneytitlelabel.h ------------------- begin : Sun Feb 05 2005 copyright : (C) 2005 by Ace Jones email : acejones@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYTITLELABEL_H #define KMYMONEYTITLELABEL_H // ---------------------------------------------------------------------------- // QT Includes #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes /** * @author ace jones */ class KMyMoneyTitleLabel : public QLabel { Q_OBJECT Q_PROPERTY(QString leftImageFile READ leftImageFile WRITE setLeftImageFile DESIGNABLE true) Q_PROPERTY(QString rightImageFile READ rightImageFile WRITE setRightImageFile DESIGNABLE true) Q_PROPERTY(QColor bgColor READ bgColor WRITE setBgColor DESIGNABLE true) Q_PROPERTY(QString text READ text WRITE setText DESIGNABLE true) public: - explicit KMyMoneyTitleLabel(QWidget* parent = nullptr); + KMyMoneyTitleLabel(QWidget *parent = 0); ~KMyMoneyTitleLabel(); void setBgColor(const QColor& _color) { m_bgColor = _color; } void setLeftImageFile(const QString& _file); void setRightImageFile(const QString& _file); const QString& leftImageFile() const { return m_leftImageFile; } const QString& rightImageFile() const { return m_rightImageFile; } QColor bgColor() const { return m_bgColor; } QString text() const { return m_text; } public slots: virtual void setText(const QString& txt); protected: void updatePixmap(); void paintEvent(QPaintEvent *); private: QImage m_leftImage; QImage m_rightImage; QColor m_bgColor; QString m_text; QString m_leftImageFile; QString m_rightImageFile; }; #endif diff --git a/kmymoney/widgets/kmymoneyvalidationfeedback.h b/kmymoney/widgets/kmymoneyvalidationfeedback.h index 1b933fab6..52c6fa05e 100644 --- a/kmymoney/widgets/kmymoneyvalidationfeedback.h +++ b/kmymoney/widgets/kmymoneyvalidationfeedback.h @@ -1,77 +1,77 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2014 Christian Dávid * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KMYMONEYVALIDATIONFEEDBACK_H #define KMYMONEYVALIDATIONFEEDBACK_H #include #include "kmm_widgets_export.h" namespace Ui { class KMyMoneyValidationFeedback; } class KMM_WIDGETS_EXPORT KMyMoneyValidationFeedback : public QWidget { Q_OBJECT public: - explicit KMyMoneyValidationFeedback(QWidget* parent = nullptr); + KMyMoneyValidationFeedback(QWidget *parent = 0); ~KMyMoneyValidationFeedback(); enum MessageType { None, Positive, Information, Warning, Error }; public slots: /** * @brief Removes the shown feedback */ void removeFeedback(); /** * @brief Removes a sepecific feedback * * Removes the feedback only if type and message fit. This is useful * if several objects are connected to setFeedback(). */ void removeFeedback(KMyMoneyValidationFeedback::MessageType type, QString message); /** * @brief Show a feedback * * If type == None and !message.isEmpty() holds, the feedback is only hidden if * the currently shown message equals message. */ void setFeedback(KMyMoneyValidationFeedback::MessageType type, QString message); private: Ui::KMyMoneyValidationFeedback* ui; class Private; Private* d_ptr; Q_DECLARE_PRIVATE(); }; #endif // KMYMONEYVALIDATIONFEEDBACK_H diff --git a/kmymoney/widgets/kmymoneywizard.cpp b/kmymoney/widgets/kmymoneywizard.cpp index 281196889..6c783af93 100644 --- a/kmymoney/widgets/kmymoneywizard.cpp +++ b/kmymoney/widgets/kmymoneywizard.cpp @@ -1,393 +1,393 @@ /*************************************************************************** kmymoneywizard.cpp ------------------- copyright : (C) 2006 by Thomas Baumagrt email : ipwizard@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kmymoneywizard_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneytitlelabel.h" #include "kguiutils.h" #include "icons/icons.h" #include "kmymoneywizard.h" using namespace Icons; KMyMoneyWizardPagePrivate::KMyMoneyWizardPagePrivate(QObject* parent) : QObject(parent) { } void KMyMoneyWizardPagePrivate::emitCompleteStateChanged() { emit completeStateChanged(); } KMyMoneyWizardPage::KMyMoneyWizardPage(unsigned int step, QWidget* widget) : m_step(step), m_widget(widget), d(new KMyMoneyWizardPagePrivate(widget)) { m_mandatoryGroup = new kMandatoryFieldGroup(widget); QObject::connect(m_mandatoryGroup, SIGNAL(stateChanged()), object(), SIGNAL(completeStateChanged())); widget->hide(); } QObject* KMyMoneyWizardPage::object() const { return d; } void KMyMoneyWizardPage::completeStateChanged() const { d->emitCompleteStateChanged(); } void KMyMoneyWizardPage::resetPage() { } void KMyMoneyWizardPage::enterPage() { } void KMyMoneyWizardPage::leavePage() { } KMyMoneyWizardPage* KMyMoneyWizardPage::nextPage() const { return 0; } bool KMyMoneyWizardPage::isLastPage() const { return nextPage() == 0; } bool KMyMoneyWizardPage::isComplete() const { if (!isLastPage()) wizard()->m_nextButton->setToolTip(i18n("Continue with next page")); else wizard()->m_finishButton->setToolTip(i18n("Finish wizard")); return m_mandatoryGroup->isEnabled(); } QString KMyMoneyWizardPage::helpContext() const { return QString(); } KMyMoneyWizard::KMyMoneyWizard(QWidget *parent, bool modal, Qt::WindowFlags f) : QDialog(parent, f), m_step(0) { setModal(modal); // enable the little grip in the right corner setSizeGripEnabled(true); // create buttons m_cancelButton = new QPushButton(i18n("&Cancel"), this); m_backButton = new QPushButton(i18nc("Go to previous page of the wizard", "&Back"), this); m_nextButton = new QPushButton(i18nc("Go to next page of the wizard", "&Next"), this); m_finishButton = new QPushButton(i18nc("Finish the wizard", "&Finish"), this); m_helpButton = new QPushButton(i18n("&Help"), this); if (style()->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons, 0, this)) { m_backButton->setIcon(KStandardGuiItem::back(KStandardGuiItem::UseRTL).icon()); m_nextButton->setIcon(KStandardGuiItem::forward(KStandardGuiItem::UseRTL).icon()); m_finishButton->setIcon(QIcon::fromTheme(g_Icons[Icon::DialogOKApply])); m_cancelButton->setIcon(QIcon::fromTheme(g_Icons[Icon::DialogCancel])); m_helpButton->setIcon(QIcon::fromTheme(g_Icons[Icon::HelpContents])); } // create button layout m_buttonLayout = new QHBoxLayout; m_buttonLayout->addWidget(m_helpButton); m_buttonLayout->addStretch(1); m_buttonLayout->addWidget(m_backButton); m_buttonLayout->addWidget(m_nextButton); m_buttonLayout->addWidget(m_finishButton); m_buttonLayout->addWidget(m_cancelButton); // create wizard layout m_wizardLayout = new QVBoxLayout(this); m_wizardLayout->setContentsMargins(6, 6, 6, 6); m_wizardLayout->setSpacing(0); m_wizardLayout->setObjectName("wizardLayout"); m_titleLabel = new KMyMoneyTitleLabel(this); m_titleLabel->setObjectName("titleLabel"); m_wizardLayout->addWidget(m_titleLabel); QHBoxLayout* hboxLayout = new QHBoxLayout; hboxLayout->setContentsMargins(0, 0, 0, 0); hboxLayout->setSpacing(6); hboxLayout->setObjectName("hboxLayout"); // create stage layout and frame m_stepFrame = new QFrame(this); m_stepFrame->setObjectName("stepFrame"); QPalette palette = m_stepFrame->palette(); palette.setColor(m_stepFrame->backgroundRole(), KColorScheme::NormalText); m_stepFrame->setPalette(palette); m_stepLayout = new QVBoxLayout(m_stepFrame); m_stepLayout->setContentsMargins(11, 11, 11, 11); m_stepLayout->setSpacing(6); m_stepLayout->setObjectName("stepLayout"); - m_stepLayout->addWidget(new QLabel(QString(), m_stepFrame)); + m_stepLayout->addWidget(new QLabel("", m_stepFrame)); m_stepLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); m_stepLabel = new QLabel(m_stepFrame); m_stepLabel->setAlignment(Qt::AlignHCenter); m_stepLayout->addWidget(m_stepLabel); hboxLayout->addWidget(m_stepFrame); m_stepPalette = m_stepLabel->palette(); // add a vertical line between the stepFrame and the pages QFrame* line = new QFrame(this); line->setObjectName("line"); line->setFrameShadow(QFrame::Sunken); line->setFrameShape(QFrame::VLine); hboxLayout->addWidget(line); // create page layout m_pageLayout = new QVBoxLayout; m_pageLayout->setContentsMargins(0, 0, 0, 0); m_pageLayout->setSpacing(6); m_pageLayout->setObjectName("pageLayout"); // the page will be inserted later dynamically above this line line = new QFrame(this); line->setObjectName("line"); line->setFrameShadow(QFrame::Sunken); line->setFrameShape(QFrame::HLine); m_pageLayout->addWidget(line); m_pageLayout->addLayout(m_buttonLayout); // now glue everything together hboxLayout->addLayout(m_pageLayout); m_wizardLayout->addLayout(hboxLayout); resize(QSize(670, 550).expandedTo(minimumSizeHint())); m_titleLabel->setText(i18n("No Title specified")); m_titleLabel->setRightImageFile("pics/titlelabel_background.png"); m_finishButton->hide(); connect(m_backButton, SIGNAL(clicked()), this, SLOT(backButtonClicked())); connect(m_nextButton, SIGNAL(clicked()), this, SLOT(nextButtonClicked())); connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(reject())); connect(m_finishButton, SIGNAL(clicked()), this, SLOT(accept())); connect(m_helpButton, SIGNAL(clicked()), this, SLOT(helpButtonClicked())); } void KMyMoneyWizard::setTitle(const QString& txt) { m_titleLabel->setText(txt); } void KMyMoneyWizard::addStep(const QString& text) { QLabel* step = new QLabel(text, m_stepFrame); step->setFrameStyle(QFrame::Panel | QFrame::Raised); step->setAlignment(Qt::AlignHCenter); step->setFrameStyle(QFrame::Box | QFrame::Sunken); step->setMargin(2); step->setPalette(m_stepPalette); m_steps.append(step); m_stepLayout->insertWidget(m_steps.count(), step); QFont font(step->font()); font.setBold(true); QFontMetrics fm(font); int w = fm.width(text) + 30; if (m_stepFrame->minimumWidth() < w) { m_stepFrame->setMinimumWidth(w); } } void KMyMoneyWizard::setStepHidden(int step, bool hidden) { if ((step < 1) || (step > m_steps.count())) return; m_steps[--step]->setHidden(hidden); updateStepCount(); } void KMyMoneyWizard::selectStep(int step) { if ((step < 1) || (step > m_steps.count())) return; m_step = step; QList::iterator it_l; QFont f = m_steps[0]->font(); for (it_l = m_steps.begin(); it_l != m_steps.end(); ++it_l) { f.setBold(false); (*it_l)->setFrameStyle(QFrame::NoFrame); if (--step == 0) { f.setBold(true); (*it_l)->setFrameStyle(QFrame::Box | QFrame::Sunken); } (*it_l)->setFont(f); } updateStepCount(); } void KMyMoneyWizard::reselectStep() { selectStep(m_step); } void KMyMoneyWizard::updateStepCount() { QList::iterator it_l; int stepCount = 0; int hiddenAdjust = 0; int step = 0; for (it_l = m_steps.begin(); it_l != m_steps.end(); ++it_l) { if (!(*it_l)->isHidden()) ++stepCount; else if (step < m_step) hiddenAdjust++; ++step; } m_stepLabel->setText(i18n("Step %1 of %2", (m_step - hiddenAdjust), stepCount)); } void KMyMoneyWizard::setFirstPage(KMyMoneyWizardPage* page) { page->resetPage(); m_history.clear(); m_history.append(page); switchPage(0); } void KMyMoneyWizard::switchPage(KMyMoneyWizardPage* oldPage) { if (oldPage) { oldPage->widget()->hide(); m_pageLayout->removeWidget(oldPage->widget()); disconnect(oldPage->object(), SIGNAL(completeStateChanged()), this, SLOT(completeStateChanged())); } KMyMoneyWizardPage* newPage = m_history.back(); if (newPage) { m_pageLayout->insertWidget(0, newPage->widget()); connect(newPage->object(), SIGNAL(completeStateChanged()), this, SLOT(completeStateChanged())); newPage->widget()->show(); selectStep(newPage->step()); if (newPage->isLastPage()) { m_nextButton->setDefault(false); m_finishButton->setDefault(true); } else { m_finishButton->setDefault(false); m_nextButton->setDefault(true); } QWidget* w = newPage->initialFocusWidget(); if (w) w->setFocus(); } completeStateChanged(); } void KMyMoneyWizard::backButtonClicked() { KMyMoneyWizardPage* oldPage = m_history.back(); m_history.pop_back(); oldPage->leavePage(); oldPage->resetPage(); switchPage(oldPage); } void KMyMoneyWizard::nextButtonClicked() { // make sure it is really complete. Some widgets only change state during focusOutEvent, // so we just create such an animal by changing the focus to the next button and // check again for copmpleness m_nextButton->setFocus(); KMyMoneyWizardPage* oldPage = m_history.back(); if (oldPage->isComplete()) { KMyMoneyWizardPage* newPage = oldPage->nextPage(); m_history.append(newPage); newPage->enterPage(); newPage->resetPage(); switchPage(oldPage); } } void KMyMoneyWizard::helpButtonClicked() { KMyMoneyWizardPage* currentPage = m_history.back(); QString ctx = currentPage->helpContext(); if (ctx.isEmpty()) ctx = m_helpContext; KHelpClient::invokeHelp(ctx); } void KMyMoneyWizard::completeStateChanged() { KMyMoneyWizardPage* currentPage = m_history.back(); bool lastPage = currentPage->isLastPage(); m_finishButton->setVisible(lastPage); m_nextButton->setVisible(!lastPage); QPushButton* button; button = lastPage ? m_finishButton : m_nextButton; - auto rc = currentPage->isComplete(); + bool rc = currentPage->isComplete(); button->setEnabled(rc); m_backButton->setEnabled(m_history.count() > 1); } void KMyMoneyWizard::accept() { // make sure it is really complete. Some widgets only change state during focusOutEvent, // so we just create such an animal by changing the focus to the finish button and // check again for completeness. m_finishButton->setFocus(); KMyMoneyWizardPage* page = m_history.back(); if (page->isComplete()) QDialog::accept(); } diff --git a/kmymoney/widgets/kmymoneywizard.h b/kmymoney/widgets/kmymoneywizard.h index d22504d0a..7389b1f48 100644 --- a/kmymoney/widgets/kmymoneywizard.h +++ b/kmymoney/widgets/kmymoneywizard.h @@ -1,550 +1,550 @@ /*************************************************************************** kmymoneywizard.h ------------------- copyright : (C) 2006 by Thomas Baumagrt email : ipwizard@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KMYMONEYWIZARD_H #define KMYMONEYWIZARD_H // ---------------------------------------------------------------------------- // QT Includes #include #include #include class QVBoxLayout; class QHBoxLayout; class QLabel; class QFrame; // ---------------------------------------------------------------------------- // KDE Includes class QPushButton; // ---------------------------------------------------------------------------- // Project Includes class KMyMoneyTitleLabel; class KMyMoneyWizard; class KMyMoneyWizardPagePrivate; class kMandatoryFieldGroup; /** * @author Thomas Baumgart (C) 2006 * * @note: the following documentation is somewhat outdated * as of May 2007. Wizards should use a namespace * for the pages and can use the WizardPage template class. * See the NewUserWizard class and NewUserWizardPages namespace * as an example of this setup. * * This class represents the base class for wizard pages for the * KMyMoneyWizard. One cannot create a wizard page directly, but * must derive from it. The KMyMoneyWizardPage class provides the * necessary functionality to work in concert with KMyMoneyWizard. * * Therefore, few steps are necessary to use this class. They seem to * be awkward at some stage, but I wanted to be able to use Qt designer * to actually design the widget for the page. That's why some things * aren't implemented in a more straight fashion than one would * normally do this. * * The first step is to derive a specific base page for the specific wizard. * In this example we use the name NewUser as template for the specific wizard. * This class provides a 'back'-pointer to the actual wizard object * for all pages. * * @code * class KNewUserPage : public KMyMoneyWizardPage * { * public: * KNewUserPage(unsigned int step, QWidget* widget, KNewUserWizard* parent, const char* name); * * protected: * KNewUserWizard* m_wizard; * } * @endcode * * The implementation of this class is rather straight-forward: * * @code * KNewUserPage::KNewUserPage(unsigned int step, QWidget* widget, KNewUserWizard* parent, const char* name) : * KMyMoneyWizardPage(step, widget, name), * m_wizard(parent) * { * } * @endcode * * For each page of the wizard, you will have to create a @p ui file with * Qt designer. * Let's assume we call the first page of the wizard 'General' and go * from there. * We also assume, that the wizard has more than one page. - * The ui designer generated class should have the name KNewUserGeneral + * The ui designer generated class should have the name KNewUserGeneralDecl * as all other dialogs. The class definition of KNewUserGeneral will * look like this: * * @code - * class KNewUserGeneral : public KNewUserGeneral, public KNewUserPage + * class KNewUserGeneral : public KNewUserGeneralDecl, public KNewUserPage * { * Q_OBJECT * public: * KNewUserGeneral(KNewUserWizard* parent, const char* name = 0); * KMyMoneyWizardPage* nextPage(); * bool isLastPage() { return false; } * * protected: * KNewUserWizard* m_wizard; * } * @endcode * * The implementation depends heavily on the logic of your code. If you only * fill some widgets, it could be as simple as: * * @code * KNewUserGeneral::KNewUserGeneral(KNewUserWizard* parent, const char* name) : - * KNewUserGeneral(parent), + * KNewUserGeneralDecl(parent), * KNewUserPage(1, this, parent, name) * { * kMandatoryFieldGroup* mandatoryGroup = new kMandatoryFieldGroup(this); * mandatoryGroup->add(m_userName); * connect(m_mandatoryGroup, SIGNAL(stateChanged()), object(), SIGNAL(completeStateChanged())); * } * * KMyMoneyWizardPage* KNewUserGeneral::nextPage() * { * return m_wizard->m_personalPage; * } * @endcode * * A note on the first parameter to KNewUserPage in the above example: it ties * this page to be part of step 1 (see KMyMoneyWizard::addStep() for details). * * Depending on the actual logic of the page, you would want to override the * following methods: resetPage, nextPage, isLastPage and isComplete. * * @note The implementation of this class is heavily based on ideas found at * http://doc.trolltech.com/4.1/dialogs-complexwizard.html */ class KMyMoneyWizardPage { public: /** * This method is called by the wizard when the page is entered from * the previous page. The default implementation does nothing. */ virtual void enterPage(); /** * This method is called by the wizard when the page is left to return to * the previous page. The default implementation does nothing. */ virtual void leavePage(); /** * This method is called by the wizard whenever a page is entered * (either in forward or backward direction). The default * implementation does nothing. */ virtual void resetPage(); /** * This method returns a pointer to the next page that should be * shown when the user presses the 'Next' button. * * @return pointer to next wizard page */ virtual KMyMoneyWizardPage* nextPage() const; /** * This returns, if the current page is the last page of the wizard. * The default implementation returns @p false if nextPage() returns 0, * @p true otherwise. * * @retval false more pages follow * @retval true this is the last page of the wizard */ virtual bool isLastPage() const; /** * This returns, if all necessary data for this page has been * filled. It is used to enabled the 'Next' or 'Finish' button. * The button is only enabled, if this method returns @p true, * which is the default implementation. * * @retval false more data required from the user before we can proceed * @retval true all data available, we allow to switch to the next page */ virtual bool isComplete() const; /** * This method returns the step to which this page belongs. * It is required by the KMyMoneyWizard and is not intended * to be used by application code. * * @return step of wizard this page belongs to */ unsigned int step() const { return m_step; } /** * This method returns a pointer to the widget of the page. * It is required by the KMyMoneyWizard and is not intended * to be used by application code. * * @return pointer to widget of page */ QWidget* widget() const { return m_widget; } /** * This method returns a pointer to the QObject used for * the signal/slot mechanism. * It is required by the KMyMoneyWizard and can be used * by application code for signal/slot connections as well. * Other use is not foreseen. */ QObject* object() const; /** * This method returns a pointer to the widget which should * receive the focus when the page is opened. * * @return pointer to widget or 0 if none is to be selected * The default implementation returns 0 */ virtual QWidget* initialFocusWidget() const { return 0; } virtual KMyMoneyWizard* wizard() const = 0; /** * This method returns a specific help context for the page shown * The default returns an empty string. */ virtual QString helpContext() const; virtual ~KMyMoneyWizardPage() {} protected: /** * Constructor (kept protected, so that one cannot create such an object directly) */ KMyMoneyWizardPage(unsigned int step, QWidget* widget); /** * This method must be called by the implementation when the * data in the fields of the wizard change and the state of * completeness changed. * * @note If you do not override isComplete() then there is no need * to call this method. */ void completeStateChanged() const; protected: kMandatoryFieldGroup* m_mandatoryGroup; private: unsigned int m_step; QWidget* m_widget; KMyMoneyWizardPagePrivate* const d; }; /** * The general base class for wizard pages * * @author Thomas Baumgart */ template class WizardPage : public KMyMoneyWizardPage { public: WizardPage(unsigned int step, QWidget* widget, T* parent) : KMyMoneyWizardPage(step, widget), m_wizard(parent), m_wizardBase(parent) { } virtual ~WizardPage() {} virtual KMyMoneyWizard* wizard() const { return m_wizardBase; } protected: T* m_wizard; KMyMoneyWizard* m_wizardBase; }; /** * @author Thomas Baumgart (C) 2006 * * This is a base class for implementation of the KMyMoneyWizard. It provides * the following layout of a wizard: * * @code * +-wizardLayout-----------------------------------------------+ * | | * +------------------------------------------------------------+ * |+-stepLayout--++-------------------------------------------+| * || ||+-pageLayout------------------------------+|| * || ||| ||| * || ||| ||| * || ||| ||| * || ||| ||| * || ||| ||| * || ||| ||| * || ||+-----------------------------------------+|| * || |||+-buttonLayout--------------------------+||| * || |||| |||| * || |||+---------------------------------------+||| * || ||+-----------------------------------------+|| * |+-------------++-------------------------------------------+| * +------------------------------------------------------------+ * @endcode * * The top bar is filled with a KMyMoneyTitleLabel as known from * KMyMoney's views. To the left there is an area in the same color * as the title bar showing the steps for this wizard. Each such step * can consist of one or more wizard pages. At the bottom of this area * the text "Step x of y" is shown and updated. To the right of this * part, the actual wizard page is shown. At the bottom of the page * the class inserts a standard button widget consisting of a Help, * Back, Next/Finish and Cancel button. * * The wizard serves as container for the wizard pages. In order to access * the data filled into the pages, one would have to provide getter methods. * * Here is an example how this object could be used. Please also see the * example described with the KMyMoneyWizardPage class. * * @code * * class KNewUserGeneral; * class KNewUserPersonal; * * class KNewUserWizard : public KMyMoneyWizard * { * Q_OBJECT * public: - * KNewUserWizard(QWidget* parent = nullptr, const char* name = 0, bool modal = false, Qt::WindowFlags flags = 0); + * KNewUserWizard(QWidget* parent = 0, const char* name = 0, bool modal = false, Qt::WindowFlags flags = 0); * * private: * KNewUserGeneral* m_generalPage; * KNewUserPersonal* m_personalPage; * KNewUserFinal* m_finalPage; * // add more pages here * * friend class KNewUserGeneral; * friend class KNewUserPersonal; * friend class KNewUserFinal; * // add more pages here * }; * @endcode * * The implementation is also easy and looks like this: * * @code * KNewUserWizard::KNewUserWizard(QWidget* parent, const char* name, bool modal, Qt::WindowFlags flags) : * KMyMoneyWizard(parent, name, modal, flags) * { * setTitle("KMyMoney New User Setup"); * addStep("General Data"); * addStep("Personal Data"); * addStep("Finish"); * * m_generalPage = new KNewUserGeneral(this); * m_personalPage = new KNewUserPersonal(this); * m_finalPage = new KNewUserFinal(this); * * setFirstPage(m_testPage1); * } * @endcode * * Don't forget to call setFirstPage() to get things started. * * The code to use this whole structure would then look something like this: * * @code * KNewUserWizard* wizard = new KNewUserWizard(this, "NewUserWizard"); * int rc = wizard->exec(); * @endcode * * The return code of exec() is either @p QDialog::Accepted or * @p QDialog::Rejected. * * @note The implementation of this class is heavily based on ideas found at * http://doc.trolltech.com/4.1/dialogs-complexwizard.html */ class KMyMoneyWizard : public QDialog { friend class KMyMoneyWizardPage; Q_OBJECT public: /** * Modify the title of the wizard to be @p txt. * * @param txt The text that should be used as title */ void setTitle(const QString& txt); /** * Add step @p text to the wizard * * @param text Text to be shown for this step */ void addStep(const QString& text); QList historyPages() const { return m_history; } /** * This method repeats selection of the current step in the * step frame. * This is used to allow changes made to showing and hiding * pages to immediately to be reflected in the step frame */ void reselectStep(); /** * Setup a global help context for the wizard. It will be used whenever * there is no specific help context available for the current page. * * @sa KMyMoneyWizardPage::helpContext() */ void setHelpContext(const QString& ctx) { m_helpContext = ctx; } virtual ~KMyMoneyWizard() {} signals: /** * This signal is sent out, when a new payee needs to be created * @sa KMyMoneyCombo::createItem() * * @param txt The name of the payee to be created * @param id A connected slot should store the id of the created object in this variable */ void createPayee(const QString& txt, QString& id); /** * This signal is sent out, when a new category needs to be created * @sa KMyMoneyCombo::createItem() * * @param txt The name of the category to be created * @param id A connected slot should store the id of the created object in this variable */ void createCategory(const QString& txt, QString& id); protected: /** * Constructor (kept protected, so that one cannot create such an object directly) */ - explicit KMyMoneyWizard(QWidget* parent = nullptr, bool modal = false, Qt::WindowFlags f = 0); + KMyMoneyWizard(QWidget *parent = 0, bool modal = false, Qt::WindowFlags f = 0); /** * This method sets up the first page after creation of the object * * @param page pointer to first page of wizard */ void setFirstPage(KMyMoneyWizardPage* page); /** * This method allows to hide or show a @p step. * * @param step step to be shown/hidden * @param hidden hide step if true (the default) or show it if false */ void setStepHidden(int step, bool hidden = true); protected slots: virtual void accept(); void completeStateChanged(); private: void updateStepCount(); private slots: void backButtonClicked(); void nextButtonClicked(); void helpButtonClicked(); protected: /* * The buttons */ QPushButton* m_cancelButton; QPushButton* m_backButton; QPushButton* m_nextButton; QPushButton* m_finishButton; QPushButton* m_helpButton; private: /** * Switch to page which is currently the top of the history stack. * @p oldPage is a pointer to the current page or 0 if no page * is shown. * * @param oldPage pointer to currently displayed page */ void switchPage(KMyMoneyWizardPage* oldPage); /** * This method selects the step given by @p step. * * @param step step to be selected */ void selectStep(int step); /* * The layouts */ QVBoxLayout* m_wizardLayout; QVBoxLayout* m_stepLayout; QVBoxLayout* m_pageLayout; QHBoxLayout* m_buttonLayout; /* * Some misc. widgets required */ QFrame* m_stepFrame; QLabel* m_stepLabel; QPalette m_stepPalette; QList m_steps; // the list of step labels int m_step; // the currently selected step /* * The title bar */ KMyMoneyTitleLabel* m_titleLabel; /* * The history stack */ QList m_history; QString m_helpContext; }; #endif diff --git a/kmymoney/widgets/ktagcontainer.cpp b/kmymoney/widgets/ktagcontainer.cpp deleted file mode 100644 index b5d5cae29..000000000 --- a/kmymoney/widgets/ktagcontainer.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/*************************************************************************** - ktagcontainer.cpp - description - ------------------- - begin : Sat Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "ktagcontainer.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "mymoneypayee.h" -#include "mymoneytag.h" -#include "ktaglabel.h" -#include "kmymoneytagcombo.h" - -class KTagContainerPrivate -{ - Q_DISABLE_COPY(KTagContainerPrivate) - -public: - KTagContainerPrivate() - { - } - - KMyMoneyTagCombo *m_tagCombo; - QList m_tagLabelList; - QList m_tagIdList; - QList m_tagNameList; - - // A local cache of the list of all Tags, it's updated when loadTags is called - QList m_list; -}; - -KTagContainer::KTagContainer(QWidget* parent) : - QWidget(parent), - d_ptr(new KTagContainerPrivate) -{ - Q_D(KTagContainer); - d->m_tagCombo = new KMyMoneyTagCombo; - QHBoxLayout *layout = new QHBoxLayout; - layout->setContentsMargins(0, 0, 5, 0); - layout->setSpacing(0); - layout->addWidget(d->m_tagCombo, 100); - this->setLayout(layout); - this->setFocusProxy(d->m_tagCombo); - connect(d->m_tagCombo, SIGNAL(lostFocus()), this, SLOT(slotAddTagWidget())); -} - -KTagContainer::~KTagContainer() -{ - Q_D(KTagContainer); - delete d; -} - -void KTagContainer::loadTags(const QList& list) -{ - Q_D(KTagContainer); - d->m_list = list; - d->m_tagCombo->loadTags(list); -} - -KMyMoneyTagCombo* KTagContainer::tagCombo() -{ - Q_D(KTagContainer); - return d->m_tagCombo; -} - -const QList KTagContainer::selectedTags() -{ - Q_D(KTagContainer); - return d->m_tagIdList; -} - -void KTagContainer::addTagWidget(const QString& id) -{ - Q_D(KTagContainer); - if (id.isNull() || d->m_tagIdList.contains(id)) - return; - const QString tagName = d->m_tagCombo->itemText(d->m_tagCombo->findData(QVariant(id), Qt::UserRole, Qt::MatchExactly)); - KTagLabel *t = new KTagLabel(id, tagName, this); - connect(t, SIGNAL(clicked(bool)), this, SLOT(slotRemoveTagWidget())); - d->m_tagLabelList.append(t); - d->m_tagNameList.append(tagName); - d->m_tagIdList.append(id); - this->layout()->addWidget(t); - d->m_tagCombo->loadTags(d->m_list); - d->m_tagCombo->setUsedTagList(d->m_tagIdList, d->m_tagNameList); - d->m_tagCombo->setCurrentIndex(0); - d->m_tagCombo->setFocus(); -} - -void KTagContainer::RemoveAllTagWidgets() -{ - Q_D(KTagContainer); - d->m_tagIdList.clear(); - d->m_tagNameList.clear(); - while (!d->m_tagLabelList.isEmpty()) - delete d->m_tagLabelList.takeLast(); - d->m_tagCombo->loadTags(d->m_list); - d->m_tagCombo->setUsedTagList(d->m_tagIdList, d->m_tagNameList); - d->m_tagCombo->setCurrentIndex(0); -} - -void KTagContainer::slotAddTagWidget() -{ - Q_D(KTagContainer); - addTagWidget(d->m_tagCombo->selectedItem()); -} - -void KTagContainer::slotRemoveTagWidget() -{ - Q_D(KTagContainer); - this->tagCombo()->setFocus(); - KTagLabel *t = (KTagLabel *)sender(); - int index = d->m_tagLabelList.indexOf(t); - d->m_tagLabelList.removeAt(index); - d->m_tagIdList.removeAt(index); - d->m_tagNameList.removeAt(index); - delete t; - d->m_tagCombo->loadTags(d->m_list); - d->m_tagCombo->setUsedTagList(d->m_tagIdList, d->m_tagNameList); - d->m_tagCombo->setCurrentIndex(0); -} diff --git a/kmymoney/widgets/ktagcontainer.h b/kmymoney/widgets/ktagcontainer.h deleted file mode 100644 index 8e7d30302..000000000 --- a/kmymoney/widgets/ktagcontainer.h +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************** - ktagcontainer.h - description - ------------------- - begin : Mon Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KTAGCONTAINER_H -#define KTAGCONTAINER_H - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmm_widgets_export.h" - -class KMyMoneyTagCombo; -class MyMoneyTag; - -/** - * This widget contain a KMyMoneyTagCombo widget and 0 or more KTagLabel widgets - * call KMyMoneyTagCombo.loadTags with the correct list whenever a new KTagLabel is created or - * deleted by removing or adding the relative tag - * - * @author Alessandro Russo - */ -class KTagContainerPrivate; -class KMM_WIDGETS_EXPORT KTagContainer : public QWidget -{ - Q_OBJECT - Q_DISABLE_COPY(KTagContainer) - -public: - explicit KTagContainer(QWidget* parent = nullptr); - ~KTagContainer(); - - void loadTags(const QList& list); - KMyMoneyTagCombo* tagCombo(); - const QList selectedTags(); - void addTagWidget(const QString& id); - void RemoveAllTagWidgets(); - -protected slots: - void slotRemoveTagWidget(); - void slotAddTagWidget(); - -private: - KTagContainerPrivate * const d_ptr; - Q_DECLARE_PRIVATE(KTagContainer) -}; - -#endif diff --git a/kmymoney/widgets/ktaglabel.cpp b/kmymoney/widgets/ktaglabel.cpp deleted file mode 100644 index 41f4d8b5c..000000000 --- a/kmymoney/widgets/ktaglabel.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/*************************************************************************** - ktaglabel.cpp - description - ------------------- - begin : Sat Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "ktaglabel.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include -#include -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "icons.h" - -using namespace Icons; - -KTagLabel::KTagLabel(const QString& id, const QString& name, QWidget* parent) : - QFrame(parent) -{ - QToolButton *t = new QToolButton(this); - t->setIcon(QIcon::fromTheme(g_Icons[Icon::DialogClose])); - t->setAutoRaise(true); - QLabel *l = new QLabel(name, this); - m_tagId = id; - QHBoxLayout *layout = new QHBoxLayout; - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - this->setLayout(layout); - layout->addWidget(t); - layout->addWidget(l); - connect(t, SIGNAL(clicked(bool)), this, SIGNAL(clicked(bool))); - //this->setFrameStyle(QFrame::Panel | QFrame::Plain); -} diff --git a/kmymoney/widgets/ktaglabel.h b/kmymoney/widgets/ktaglabel.h deleted file mode 100644 index be8b89142..000000000 --- a/kmymoney/widgets/ktaglabel.h +++ /dev/null @@ -1,58 +0,0 @@ -/*************************************************************************** - ktaglabel.h - description - ------------------- - begin : Mon Jan 09 2010 - copyright : (C) 2010 by Thomas Baumgart - Cristian Onet - Alvaro Soliverez - (C) 2017 by Łukasz Wojniłowicz - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef KTAGLABEL_H -#define KTAGLABEL_H - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmm_widgets_export.h" - -/** - * This class implements a tag label. It create a QFrame and inside it a QToolButton - * with a 'X' Icon and a QLabel with the name of the Tag - * - * @author Alessandro Russo - */ -class KTagLabel : public QFrame -{ - Q_OBJECT - Q_DISABLE_COPY(KTagLabel) - -public: - explicit KTagLabel(const QString& id, const QString& name, QWidget* parent = nullptr); - -signals: - void clicked(bool); - -private: - QString m_tagId; -}; - -#endif diff --git a/kmymoney/widgets/ktreewidgetfilterlinewidget.h b/kmymoney/widgets/ktreewidgetfilterlinewidget.h index 77e512e5d..d8f96be10 100644 --- a/kmymoney/widgets/ktreewidgetfilterlinewidget.h +++ b/kmymoney/widgets/ktreewidgetfilterlinewidget.h @@ -1,41 +1,41 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2015 Christian David * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KTREEWIDGETFILTERLINEWIDGET_H #define KTREEWIDGETFILTERLINEWIDGET_H #include #include "kmm_widgets_export.h" class KMM_WIDGETS_EXPORT KTreeWidgetFilterLineWidget : public KTreeWidgetSearchLineWidget { Q_OBJECT public: - explicit KTreeWidgetFilterLineWidget(QWidget* parent = nullptr, QTreeWidget *treeWidget = 0); + explicit KTreeWidgetFilterLineWidget(QWidget *parent = 0, QTreeWidget *treeWidget = 0); protected Q_SLOTS: /** * @copydoc KTreeWidgetSearchLineWidget::createWidgets() * * After widgets are created, this version finds the label and renames it to "Filter" */ virtual void createWidgets(); }; #endif // KTREEWIDGETFILTERLINEWIDGET_H diff --git a/kmymoney/widgets/onlinejobmessagesview.h b/kmymoney/widgets/onlinejobmessagesview.h index cb0dfd688..7645912d4 100644 --- a/kmymoney/widgets/onlinejobmessagesview.h +++ b/kmymoney/widgets/onlinejobmessagesview.h @@ -1,45 +1,45 @@ /* * This file is part of KMyMoney, A Personal Finance Manager by KDE * Copyright (C) 2015 Christian Dávid * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ONLINEJOBMESSAGESVIEW_H #define ONLINEJOBMESSAGESVIEW_H #include #include "kmm_widgets_export.h" class QAbstractItemModel; namespace Ui { class onlineJobMessageView; } class KMM_WIDGETS_EXPORT onlineJobMessagesView : public QWidget { Q_OBJECT public: - onlineJobMessagesView(QWidget* parent = nullptr); + onlineJobMessagesView(QWidget* parent = 0); ~onlineJobMessagesView(); void setModel(QAbstractItemModel* model); protected: Ui::onlineJobMessageView* ui; }; #endif // ONLINEJOBMESSAGESVIEW_H diff --git a/kmymoney/widgets/register.cpp b/kmymoney/widgets/register.cpp index dce471916..ffd65d362 100644 --- a/kmymoney/widgets/register.cpp +++ b/kmymoney/widgets/register.cpp @@ -1,2246 +1,2129 @@ /*************************************************************************** register.cpp - description ------------------- begin : Fri Mar 10 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart - (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include "register.h" #include // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes - -#include "mymoneysplit.h" -#include "mymoneytransaction.h" -#include "mymoneyaccount.h" #include "stdtransactiondownloaded.h" #include "stdtransactionmatched.h" #include "scheduledtransaction.h" #include "kmymoneyglobalsettings.h" -#include "mymoneymoney.h" #include "mymoneyfile.h" -#include "groupmarker.h" -#include "groupmarker_p.h" -#include "fancydategroupmarker.h" -#include "fancydategroupmarker_p.h" #include "mymoneyenums.h" -#include "widgetenums.h" + +static const char * sortOrderText[] = { + I18N_NOOP2("Unknown sort order", "Unknown"), + I18N_NOOP("Post date"), + I18N_NOOP("Date entered"), + I18N_NOOP("Payee"), + I18N_NOOP("Amount"), + I18N_NOOP("Number"), + I18N_NOOP("Entry order"), + I18N_NOOP("Type"), + I18N_NOOP("Category"), + I18N_NOOP("Reconcile state"), + I18N_NOOP("Security") + // add new values above this comment line +}; using namespace KMyMoneyRegister; using namespace eMyMoney; +static unsigned char fancymarker_bg_image[] = { + /* 200x49 */ + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, + 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x31, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x9F, 0xC5, 0xE6, + 0x4F, 0x00, 0x00, 0x00, 0x06, 0x62, 0x4B, 0x47, + 0x44, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xA0, + 0xBD, 0xA7, 0x93, 0x00, 0x00, 0x00, 0x09, 0x70, + 0x48, 0x59, 0x73, 0x00, 0x00, 0x0B, 0x13, 0x00, + 0x00, 0x0B, 0x13, 0x01, 0x00, 0x9A, 0x9C, 0x18, + 0x00, 0x00, 0x00, 0x86, 0x49, 0x44, 0x41, 0x54, + 0x78, 0xDA, 0xED, 0xDD, 0x31, 0x0A, 0x84, 0x40, + 0x10, 0x44, 0xD1, 0x1A, 0x19, 0x10, 0xCF, 0xE6, + 0xFD, 0x4F, 0xB2, 0x88, 0x08, 0x22, 0x9B, 0x18, + 0x4E, 0x1B, 0x2C, 0x1B, 0x18, 0xBC, 0x07, 0x7D, + 0x81, 0x82, 0x1F, 0x77, 0x4B, 0xB2, 0x06, 0x18, + 0xEA, 0x49, 0x3E, 0x66, 0x00, 0x81, 0x80, 0x40, + 0xE0, 0xDF, 0x81, 0x6C, 0x66, 0x80, 0x3A, 0x90, + 0xDD, 0x0C, 0x50, 0x07, 0x72, 0x98, 0x01, 0xEA, + 0x40, 0x4E, 0x33, 0x40, 0x1D, 0xC8, 0x65, 0x06, + 0x18, 0x6B, 0xF7, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x16, 0x3E, + 0x4C, 0xC1, 0x83, 0x9E, 0x64, 0x32, 0x03, 0x08, + 0x04, 0x7E, 0x0A, 0xA4, 0x9B, 0x01, 0xEA, 0x40, + 0x66, 0x33, 0x40, 0x1D, 0xC8, 0x62, 0x06, 0x18, + 0xFB, 0x02, 0x05, 0x87, 0x08, 0x55, 0xFE, 0xDE, + 0xA2, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, + 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 +}; + +QPixmap* GroupMarker::m_bg = 0; +int GroupMarker::m_bgRefCnt = 0; + void ItemPtrVector::sort() { if (count() > 0) { // get rid of 0 pointers in the list KMyMoneyRegister::ItemPtrVector::iterator it_l; RegisterItem *item; for (it_l = begin(); it_l != end(); ++it_l) { if (*it_l == 0) { item = last(); *it_l = item; pop_back(); --it_l; } } std::sort(begin(), end(), item_cmp); } } bool ItemPtrVector::item_cmp(RegisterItem* i1, RegisterItem* i2) { - const QList& sortOrder = i1->getParent()->sortOrder(); - QList::const_iterator it; + const QList& sortOrder = i1->parent()->sortOrder(); + QList::const_iterator it; int rc = 0; bool ok1, ok2; qulonglong n1, n2; for (it = sortOrder.begin(); it != sortOrder.end(); ++it) { - eWidgets::SortField sortField = static_cast(*it); + TransactionSortField sortField = static_cast(*it); switch (qAbs(static_cast(sortField))) { - case (int)eWidgets::SortField::PostDate: + case PostDateSort: rc = i2->sortPostDate().daysTo(i1->sortPostDate()); break; - case (int)eWidgets::SortField::EntryDate: + case EntryDateSort: rc = i2->sortEntryDate().daysTo(i1->sortEntryDate()); break; - case (int)eWidgets::SortField::Payee: + case PayeeSort: rc = QString::localeAwareCompare(i1->sortPayee(), i2->sortPayee()); break; - case (int)eWidgets::SortField::Value: + case ValueSort: if (i1->sortValue() == i2->sortValue()) rc = 0; else if (i1->sortValue() < i2->sortValue()) rc = -1; else rc = 1; break; - case (int)eWidgets::SortField::NoSort: + case NoSort: // convert both values to numbers n1 = i1->sortNumber().toULongLong(&ok1); n2 = i2->sortNumber().toULongLong(&ok2); // the following four cases exist: // a) both are converted correct // compare them directly // b) n1 is numeric, n2 is not // numbers come first, so return -1 // c) n1 is not numeric, n2 is // numbers come first, so return 1 // d) both are non numbers // compare using localeAwareCompare if (ok1 && ok2) { // case a) rc = (n1 > n2) ? 1 : ((n1 == n2) ? 0 : -1); } else if (ok1 && !ok2) { rc = -1; } else if (!ok1 && ok2) { rc = 1; } else rc = QString::localeAwareCompare(i1->sortNumber(), i2->sortNumber()); break; - case (int)eWidgets::SortField::EntryOrder: + case EntryOrderSort: rc = qstrcmp(i1->sortEntryOrder().toLatin1(), i2->sortEntryOrder().toLatin1()); break; - case (int)eWidgets::SortField::Type: + case TypeSort: rc = i1->sortType() - i2->sortType(); break; - case (int)eWidgets::SortField::Category: + case CategorySort: rc = QString::localeAwareCompare(i1->sortCategory(), i2->sortCategory()); break; - case (int)eWidgets::SortField::ReconcileState: + case ReconcileStateSort: rc = static_cast(i1->sortReconcileState()) - static_cast(i2->sortReconcileState()); break; - case (int)eWidgets::SortField::Security: + case SecuritySort: rc = QString::localeAwareCompare(i1->sortSecurity(), i2->sortSecurity()); break; default: - qDebug("Invalid sort key %d", (int)*it); + qDebug("Invalid sort key %d", *it); break; } // take care of group markers, but only first sort item if ((rc == 0) && (it == sortOrder.begin())) { rc = i1->sortSamePostDate() - i2->sortSamePostDate(); if (rc) { return rc < 0; } } // the items differ for this sort key so we can return a result if (rc != 0) { - return ((int)*it < 0) ? rc >= 0 : rc < 0; + return (*it < 0) ? rc >= 0 : rc < 0; } } if (rc == 0) { rc = qstrcmp(i1->sortEntryOrder().toLatin1(), i2->sortEntryOrder().toLatin1()); } return rc < 0; } -namespace KMyMoneyRegister { - class StatementGroupMarkerPrivate : public FancyDateGroupMarkerPrivate - { - public: - CashFlowDirection m_dir; - }; -} - -StatementGroupMarker::StatementGroupMarker(Register* parent, CashFlowDirection dir, const QDate& date, const QString& txt) : - FancyDateGroupMarker(*new StatementGroupMarkerPrivate, parent, date, txt) +GroupMarker::GroupMarker(Register *parent, const QString& txt) : + RegisterItem(parent), + m_txt(txt), + m_showDate(false), + m_erroneous(false) { - Q_D(StatementGroupMarker); - d->m_dir = dir; - d->m_showDate = true; -} + int h; + if (m_parent) { + h = m_parent->rowHeightHint(); + } else { + QFontMetrics fm(KMyMoneyGlobalSettings::listCellFont()); + h = fm.lineSpacing() + 6; + } -StatementGroupMarker::~StatementGroupMarker() -{ + if (m_bg && (m_bg->height() != h)) { + delete m_bg; + m_bg = 0; + } + + // convert the backgroud once + if (m_bg == 0) { + m_bg = new QPixmap; + m_bg->loadFromData(fancymarker_bg_image, sizeof(fancymarker_bg_image)); + *m_bg = m_bg->scaled(m_bg->width(), h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + + ++m_bgRefCnt; } -CashFlowDirection StatementGroupMarker::sortType() const +GroupMarker::~GroupMarker() { - Q_D(const StatementGroupMarker); - return d->m_dir; + --m_bgRefCnt; + if (!m_bgRefCnt) { + delete m_bg; + m_bg = 0; + } } -int StatementGroupMarker::sortSamePostDate() const +void GroupMarker::paintRegisterCell(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) { - return 3; + QRect r(option.rect); + painter->save(); + + // the group marker always uses all cols + r.setX(m_parent->horizontalHeader()->sectionPosition(0)); + r.setWidth(m_parent->viewport()->width()); + painter->translate(r.x(), r.y()); + + QRect cellRect; + cellRect.setX(0); + cellRect.setY(0); + cellRect.setWidth(m_parent->viewport()->width()); + cellRect.setHeight(m_parent->rowHeight(index.row())); + + option.palette.setColor(QPalette::Base, isErroneous() ? KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionErroneous) : KMyMoneyGlobalSettings::schemeColor(SchemeColor::GroupMarker)); + + QBrush backgroundBrush(option.palette.color(QPalette::Base)); + painter->fillRect(cellRect, backgroundBrush); + painter->setPen(KMyMoneyGlobalSettings::schemeColor(SchemeColor::ListGrid)); + painter->drawLine(cellRect.x(), cellRect.height() - 1, cellRect.width(), cellRect.height() - 1); + + // now write the text + painter->setPen(option.palette.color(isErroneous() ? QPalette::HighlightedText : QPalette::Text)); + QFont font = painter->font(); + font.setBold(true); + painter->setFont(font); + + painter->drawText(cellRect, Qt::AlignVCenter | Qt::AlignCenter, m_txt); + + cellRect.setHeight(m_bg->height()); + + // now it's time to draw the background + painter->drawPixmap(cellRect, *m_bg); + + // in case we need to show the date, we just paint it in col 1 + if (m_showDate) { + font.setBold(false); + cellRect.setX(m_parent->horizontalHeader()->sectionPosition(DateColumn)); + cellRect.setWidth(m_parent->horizontalHeader()->sectionSize(DateColumn)); + painter->setFont(font); + painter->drawText(cellRect, Qt::AlignVCenter | Qt::AlignCenter, QLocale().toString(sortPostDate(), QLocale::ShortFormat)); + } + + painter->restore(); } -FiscalYearGroupMarker::FiscalYearGroupMarker(Register* parent, const QDate& date, const QString& txt) : - FancyDateGroupMarker(parent, date, txt) +void GroupMarker::paintFormCell(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) { + Q_UNUSED(painter); + Q_UNUSED(option); + Q_UNUSED(index); } -FiscalYearGroupMarker::~FiscalYearGroupMarker() +int GroupMarker::rowHeightHint() const { + if (!m_visible) + return 0; + + return m_bg->height(); } -const char* FiscalYearGroupMarker::className() +StatementGroupMarker::StatementGroupMarker(Register* parent, CashFlowDirection dir, const QDate& date, const QString& txt) : + FancyDateGroupMarker(parent, date, txt), + m_dir(dir) { - return "FiscalYearGroupMarker"; + m_showDate = true; } -int FiscalYearGroupMarker::sortSamePostDate() const +FancyDateGroupMarker::FancyDateGroupMarker(Register* parent, const QDate& date, const QString& txt) : + GroupMarker(parent, txt), + m_date(date) { - return 1; } -SimpleDateGroupMarker::SimpleDateGroupMarker(Register* parent, const QDate& date, const QString& txt) : +FiscalYearGroupMarker::FiscalYearGroupMarker(Register* parent, const QDate& date, const QString& txt) : FancyDateGroupMarker(parent, date, txt) { } -SimpleDateGroupMarker::~SimpleDateGroupMarker() +SimpleDateGroupMarker::SimpleDateGroupMarker(Register* parent, const QDate& date, const QString& txt) : + FancyDateGroupMarker(parent, date, txt) { } int SimpleDateGroupMarker::rowHeightHint() const { - Q_D(const FancyDateGroupMarker); - if (!d->m_visible) + if (!m_visible) return 0; return RegisterItem::rowHeightHint() / 2; } -const char* SimpleDateGroupMarker::className() -{ - return "SimpleDateGroupMarker"; -} - void SimpleDateGroupMarker::paintRegisterCell(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) { - Q_D(FancyDateGroupMarker); QRect cellRect = option.rect; painter->save(); - cellRect.setWidth(d->m_parent->viewport()->width()); - cellRect.setHeight(d->m_parent->rowHeight(index.row() + d->m_startRow)); + cellRect.setWidth(m_parent->viewport()->width()); + cellRect.setHeight(m_parent->rowHeight(index.row() + m_startRow)); - if (d->m_alternate) + if (m_alternate) option.palette.setColor(QPalette::Base, KMyMoneyGlobalSettings::schemeColor(SchemeColor::ListBackground2)); else option.palette.setColor(QPalette::Base, KMyMoneyGlobalSettings::schemeColor(SchemeColor::ListBackground1)); QBrush backgroundBrush(option.palette.color(QPalette::Base)); backgroundBrush.setStyle(Qt::Dense5Pattern); backgroundBrush.setColor(KMyMoneyGlobalSettings::schemeColor(SchemeColor::ListGrid)); painter->eraseRect(cellRect); painter->fillRect(cellRect, backgroundBrush); painter->setPen(KMyMoneyGlobalSettings::schemeColor(SchemeColor::ListGrid)); painter->restore(); } -namespace KMyMoneyRegister -{ - class TypeGroupMarkerPrivate : public GroupMarkerPrivate - { - public: - CashFlowDirection m_dir; - }; -} - TypeGroupMarker::TypeGroupMarker(Register* parent, CashFlowDirection dir, Account accType) : - GroupMarker(*new TypeGroupMarkerPrivate, parent, QString()) + GroupMarker(parent), + m_dir(dir) { - Q_D(TypeGroupMarker); - d->m_dir = dir; switch (dir) { case Deposit: - d->m_txt = i18nc("Deposits onto account", "Deposits"); + m_txt = i18nc("Deposits onto account", "Deposits"); if (accType == Account::CreditCard) { - d->m_txt = i18nc("Payments towards credit card", "Payments"); + m_txt = i18nc("Payments towards credit card", "Payments"); } break; case Payment: - d->m_txt = i18nc("Payments made from account", "Payments"); + m_txt = i18nc("Payments made from account", "Payments"); if (accType == Account::CreditCard) { - d->m_txt = i18nc("Payments made with credit card", "Charges"); + m_txt = i18nc("Payments made with credit card", "Charges"); } break; default: qDebug("Unknown CashFlowDirection %d for TypeGroupMarker constructor", dir); break; } } -TypeGroupMarker::~TypeGroupMarker() -{ -} - -CashFlowDirection TypeGroupMarker::sortType() const -{ - Q_D(const TypeGroupMarker); - return d->m_dir; -} - PayeeGroupMarker::PayeeGroupMarker(Register* parent, const QString& name) : GroupMarker(parent, name) { } -PayeeGroupMarker::~PayeeGroupMarker() -{ -} - -const QString& PayeeGroupMarker::sortPayee() const -{ - Q_D(const GroupMarker); - return d->m_txt; -} - CategoryGroupMarker::CategoryGroupMarker(Register* parent, const QString& category) : GroupMarker(parent, category) { } -CategoryGroupMarker::~CategoryGroupMarker() -{ -} - -const QString& CategoryGroupMarker::sortCategory() const -{ - Q_D(const GroupMarker); - return d->m_txt; -} -const QString CategoryGroupMarker::sortSecurity() const -{ - Q_D(const GroupMarker); - return d->m_txt; -} - -const char* CategoryGroupMarker::className() -{ - return "CategoryGroupMarker"; -} - -namespace KMyMoneyRegister -{ - class ReconcileGroupMarkerPrivate : public GroupMarkerPrivate - { - public: - eMyMoney::Split::State m_state; - }; -} - ReconcileGroupMarker::ReconcileGroupMarker(Register* parent, eMyMoney::Split::State state) : - GroupMarker(*new ReconcileGroupMarkerPrivate, parent, QString()) + GroupMarker(parent), + m_state(state) { - Q_D(ReconcileGroupMarker); - d->m_state = state; switch (state) { case eMyMoney::Split::State::NotReconciled: - d->m_txt = i18nc("Reconcile state 'Not reconciled'", "Not reconciled"); + m_txt = i18nc("Reconcile state 'Not reconciled'", "Not reconciled"); break; case eMyMoney::Split::State::Cleared: - d->m_txt = i18nc("Reconcile state 'Cleared'", "Cleared"); + m_txt = i18nc("Reconcile state 'Cleared'", "Cleared"); break; case eMyMoney::Split::State::Reconciled: - d->m_txt = i18nc("Reconcile state 'Reconciled'", "Reconciled"); + m_txt = i18nc("Reconcile state 'Reconciled'", "Reconciled"); break; case eMyMoney::Split::State::Frozen: - d->m_txt = i18nc("Reconcile state 'Frozen'", "Frozen"); + m_txt = i18nc("Reconcile state 'Frozen'", "Frozen"); break; default: - d->m_txt = i18nc("Unknown reconcile state", "Unknown"); + m_txt = i18nc("Unknown reconcile state", "Unknown"); break; } } -ReconcileGroupMarker::~ReconcileGroupMarker() -{ -} - -eMyMoney::Split::State ReconcileGroupMarker::sortReconcileState() const -{ - Q_D(const ReconcileGroupMarker); - return d->m_state; -} - RegisterItemDelegate::RegisterItemDelegate(Register *parent) : QStyledItemDelegate(parent), m_register(parent) { } RegisterItemDelegate::~RegisterItemDelegate() { } void RegisterItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { RegisterItem* const item = m_register->itemAtRow(index.row()); if (item && m_register->updatesEnabled()) { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); item->paintRegisterCell(painter, opt, index); } } -namespace KMyMoneyRegister -{ - class RegisterPrivate - { - public: - RegisterPrivate() : - m_selectAnchor(0), - m_focusItem(0), - m_firstItem(0), - m_lastItem(0), - m_firstErroneous(0), - m_lastErroneous(0), - m_rowHeightHint(0), - m_ledgerLensForced(false), - m_selectionMode(QTableWidget::MultiSelection), - m_needResize(true), - m_listsDirty(false), - m_ignoreNextButtonRelease(false), - m_needInitialColumnResize(false), - m_usedWithEditor(false), - m_mouseButton(Qt::MouseButtons(Qt::NoButton)), - m_modifiers(Qt::KeyboardModifiers(Qt::NoModifier)), - m_detailsColumnType(PayeeFirst) - { - } - - ~RegisterPrivate() - { - } - - ItemPtrVector m_items; - QVector m_itemIndex; - RegisterItem* m_selectAnchor; - RegisterItem* m_focusItem; - RegisterItem* m_ensureVisibleItem; - RegisterItem* m_firstItem; - RegisterItem* m_lastItem; - RegisterItem* m_firstErroneous; - RegisterItem* m_lastErroneous; - - int m_markErroneousTransactions; - int m_rowHeightHint; - - MyMoneyAccount m_account; - - bool m_ledgerLensForced; - QAbstractItemView::SelectionMode m_selectionMode; - - bool m_needResize; - bool m_listsDirty; - bool m_ignoreNextButtonRelease; - bool m_needInitialColumnResize; - bool m_usedWithEditor; - Qt::MouseButtons m_mouseButton; - Qt::KeyboardModifiers m_modifiers; - Column m_lastCol; - QList m_sortOrder; - QRect m_lastRepaintRect; - DetailsColumnType m_detailsColumnType; - - }; -} - Register::Register(QWidget *parent) : TransactionEditorContainer(parent), - d_ptr(new RegisterPrivate) + m_selectAnchor(0), + m_focusItem(0), + m_firstItem(0), + m_lastItem(0), + m_firstErroneous(0), + m_lastErroneous(0), + m_rowHeightHint(0), + m_ledgerLensForced(false), + m_selectionMode(QTableWidget::MultiSelection), + m_needResize(true), + m_listsDirty(false), + m_ignoreNextButtonRelease(false), + m_needInitialColumnResize(false), + m_usedWithEditor(false), + m_mouseButton(Qt::MouseButtons(Qt::NoButton)), + m_modifiers(Qt::KeyboardModifiers(Qt::NoModifier)), + m_detailsColumnType(PayeeFirst) { - Q_D(Register); // used for custom coloring with the help of the application's stylesheet setObjectName(QLatin1String("register")); setItemDelegate(new RegisterItemDelegate(this)); setEditTriggers(QAbstractItemView::NoEditTriggers); setColumnCount(MaxColumns); setSelectionBehavior(QAbstractItemView::SelectRows); setAcceptDrops(true); setShowGrid(false); setContextMenuPolicy(Qt::DefaultContextMenu); setHorizontalHeaderItem(NumberColumn, new QTableWidgetItem()); setHorizontalHeaderItem(DateColumn, new QTableWidgetItem()); setHorizontalHeaderItem(AccountColumn, new QTableWidgetItem()); setHorizontalHeaderItem(SecurityColumn, new QTableWidgetItem()); setHorizontalHeaderItem(DetailColumn, new QTableWidgetItem()); setHorizontalHeaderItem(ReconcileFlagColumn, new QTableWidgetItem()); setHorizontalHeaderItem(PaymentColumn, new QTableWidgetItem()); setHorizontalHeaderItem(DepositColumn, new QTableWidgetItem()); setHorizontalHeaderItem(QuantityColumn, new QTableWidgetItem()); setHorizontalHeaderItem(PriceColumn, new QTableWidgetItem()); setHorizontalHeaderItem(ValueColumn, new QTableWidgetItem()); setHorizontalHeaderItem(BalanceColumn, new QTableWidgetItem()); // keep the following list in sync with KMyMoneyRegister::Column in transaction.h horizontalHeaderItem(NumberColumn)->setText(i18nc("Cheque Number", "No.")); horizontalHeaderItem(DateColumn)->setText(i18n("Date")); horizontalHeaderItem(AccountColumn)->setText(i18n("Account")); horizontalHeaderItem(SecurityColumn)->setText(i18n("Security")); horizontalHeaderItem(DetailColumn)->setText(i18n("Details")); horizontalHeaderItem(ReconcileFlagColumn)->setText(i18n("C")); horizontalHeaderItem(PaymentColumn)->setText(i18n("Payment")); horizontalHeaderItem(DepositColumn)->setText(i18n("Deposit")); horizontalHeaderItem(QuantityColumn)->setText(i18n("Quantity")); horizontalHeaderItem(PriceColumn)->setText(i18n("Price")); horizontalHeaderItem(ValueColumn)->setText(i18n("Value")); horizontalHeaderItem(BalanceColumn)->setText(i18n("Balance")); verticalHeader()->hide(); horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); horizontalHeader()->setSortIndicatorShown(false); horizontalHeader()->setSectionsMovable(false); horizontalHeader()->setSectionsClickable(false); horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(cellClicked(int,int)), this, SLOT(selectItem(int,int))); connect(this, SIGNAL(cellDoubleClicked(int,int)), this, SLOT(slotDoubleClicked(int,int))); } Register::~Register() { - Q_D(Register); clear(); - delete d; } bool Register::eventFilter(QObject* o, QEvent* e) { if (o == this && e->type() == QEvent::KeyPress) { QKeyEvent* ke = dynamic_cast(e); if (ke->key() == Qt::Key_Menu) { emit openContextMenu(); return true; } } return QTableWidget::eventFilter(o, e); } void Register::setupRegister(const MyMoneyAccount& account, const QList& cols) { - Q_D(Register); - d->m_account = account; + m_account = account; setUpdatesEnabled(false); - for (auto i = 0; i < MaxColumns; ++i) + for (int i = 0; i < MaxColumns; ++i) hideColumn(i); - d->m_needInitialColumnResize = true; + m_needInitialColumnResize = true; - d->m_lastCol = static_cast(0); + m_lastCol = static_cast(0); QList::const_iterator it_c; for (it_c = cols.begin(); it_c != cols.end(); ++it_c) { if ((*it_c) > MaxColumns) continue; showColumn(*it_c); - if (*it_c > d->m_lastCol) - d->m_lastCol = *it_c; + if (*it_c > m_lastCol) + m_lastCol = *it_c; } setUpdatesEnabled(true); } void Register::setupRegister(const MyMoneyAccount& account, bool showAccountColumn) { - Q_D(Register); - d->m_account = account; + m_account = account; setUpdatesEnabled(false); - for (auto i = 0; i < MaxColumns; ++i) + for (int i = 0; i < MaxColumns; ++i) hideColumn(i); horizontalHeaderItem(PaymentColumn)->setText(i18nc("Payment made from account", "Payment")); horizontalHeaderItem(DepositColumn)->setText(i18nc("Deposit into account", "Deposit")); if (account.id().isEmpty()) { setUpdatesEnabled(true); return; } - d->m_needInitialColumnResize = true; + m_needInitialColumnResize = true; // turn on standard columns showColumn(DateColumn); showColumn(DetailColumn); showColumn(ReconcileFlagColumn); // balance switch (account.accountType()) { case Account::Stock: break; default: showColumn(BalanceColumn); break; } // Number column switch (account.accountType()) { case Account::Savings: case Account::Cash: case Account::Loan: case Account::AssetLoan: case Account::Asset: case Account::Liability: case Account::Equity: if (KMyMoneyGlobalSettings::alwaysShowNrField()) showColumn(NumberColumn); break; case Account::Checkings: case Account::CreditCard: showColumn(NumberColumn); break; default: hideColumn(NumberColumn); break; } switch (account.accountType()) { case Account::Income: case Account::Expense: showAccountColumn = true; break; default: break; } if (showAccountColumn) showColumn(AccountColumn); // Security, activity, payment, deposit, amount, price and value column switch (account.accountType()) { default: showColumn(PaymentColumn); showColumn(DepositColumn); break; case Account::Investment: showColumn(SecurityColumn); showColumn(QuantityColumn); showColumn(PriceColumn); showColumn(ValueColumn); break; } // headings switch (account.accountType()) { case Account::CreditCard: horizontalHeaderItem(PaymentColumn)->setText(i18nc("Payment made with credit card", "Charge")); horizontalHeaderItem(DepositColumn)->setText(i18nc("Payment towards credit card", "Payment")); break; case Account::Asset: case Account::AssetLoan: horizontalHeaderItem(PaymentColumn)->setText(i18nc("Decrease of asset/liability value", "Decrease")); horizontalHeaderItem(DepositColumn)->setText(i18nc("Increase of asset/liability value", "Increase")); break; case Account::Liability: case Account::Loan: horizontalHeaderItem(PaymentColumn)->setText(i18nc("Increase of asset/liability value", "Increase")); horizontalHeaderItem(DepositColumn)->setText(i18nc("Decrease of asset/liability value", "Decrease")); break; case Account::Income: case Account::Expense: horizontalHeaderItem(PaymentColumn)->setText(i18n("Income")); horizontalHeaderItem(DepositColumn)->setText(i18n("Expense")); break; default: break; } - d->m_lastCol = BalanceColumn; + m_lastCol = BalanceColumn; setUpdatesEnabled(true); } bool Register::focusNextPrevChild(bool next) { return QFrame::focusNextPrevChild(next); } void Register::setSortOrder(const QString& order) { - Q_D(Register); const QStringList orderList = order.split(',', QString::SkipEmptyParts); QStringList::const_iterator it; - d->m_sortOrder.clear(); + m_sortOrder.clear(); for (it = orderList.constBegin(); it != orderList.constEnd(); ++it) { - d->m_sortOrder << static_cast((*it).toInt()); + m_sortOrder << static_cast((*it).toInt()); } } -const QList& Register::sortOrder() const -{ - Q_D(const Register); - return d->m_sortOrder; -} - void Register::sortItems() { - Q_D(Register); - if (d->m_items.count() == 0) + if (m_items.count() == 0) return; // sort the array of pointers to the transactions - d->m_items.sort(); + m_items.sort(); // update the next/prev item chains RegisterItem* prev = 0; RegisterItem* item; - d->m_firstItem = d->m_lastItem = 0; - for (QVector::size_type i = 0; i < d->m_items.size(); ++i) { - item = d->m_items[i]; + m_firstItem = m_lastItem = 0; + for (QVector::size_type i = 0; i < m_items.size(); ++i) { + item = m_items[i]; if (!item) continue; - if (!d->m_firstItem) - d->m_firstItem = item; - d->m_lastItem = item; + if (!m_firstItem) + m_firstItem = item; + m_lastItem = item; if (prev) prev->setNextItem(item); item->setPrevItem(prev); item->setNextItem(0); prev = item; } // update the balance visibility settings - item = d->m_lastItem; + item = m_lastItem; bool showBalance = true; while (item) { Transaction* t = dynamic_cast(item); if (t) { t->setShowBalance(showBalance); if (!t->isVisible()) { showBalance = false; } } item = item->prevItem(); } // force update of the item index (row to item array) - d->m_listsDirty = true; + m_listsDirty = true; } -Column Register::lastCol() const +TransactionSortField Register::primarySortKey() const { - Q_D(const Register); - return d->m_lastCol; -} - -eWidgets::SortField Register::primarySortKey() const -{ - Q_D(const Register); - if (!d->m_sortOrder.isEmpty()) - return static_cast(d->m_sortOrder.first()); - return eWidgets::SortField::Unknown; + if (!m_sortOrder.isEmpty()) + return static_cast(m_sortOrder.first()); + return UnknownSort; } void Register::clear() { - Q_D(Register); - d->m_firstErroneous = d->m_lastErroneous = 0; - d->m_ensureVisibleItem = 0; + m_firstErroneous = m_lastErroneous = 0; + m_ensureVisibleItem = 0; - d->m_items.clear(); + m_items.clear(); RegisterItem* p; while ((p = firstItem()) != 0) { delete p; } - d->m_firstItem = d->m_lastItem = 0; + m_firstItem = m_lastItem = 0; - d->m_listsDirty = true; - d->m_selectAnchor = 0; - d->m_focusItem = 0; + m_listsDirty = true; + m_selectAnchor = 0; + m_focusItem = 0; #ifndef KMM_DESIGNER // recalculate row height hint QFontMetrics fm(KMyMoneyGlobalSettings::listCellFont()); - d->m_rowHeightHint = fm.lineSpacing() + 6; + m_rowHeightHint = fm.lineSpacing() + 6; #endif - d->m_needInitialColumnResize = true; - d->m_needResize = true; + m_needInitialColumnResize = true; + m_needResize = true; updateRegister(true); } void Register::insertItemAfter(RegisterItem*p, RegisterItem* prev) { - Q_D(Register); RegisterItem* next = 0; if (!prev) prev = lastItem(); if (prev) { next = prev->nextItem(); prev->setNextItem(p); } if (next) next->setPrevItem(p); p->setPrevItem(prev); p->setNextItem(next); - if (!d->m_firstItem) - d->m_firstItem = p; - if (!d->m_lastItem) - d->m_lastItem = p; + if (!m_firstItem) + m_firstItem = p; + if (!m_lastItem) + m_lastItem = p; - if (prev == d->m_lastItem) - d->m_lastItem = p; + if (prev == m_lastItem) + m_lastItem = p; - d->m_listsDirty = true; - d->m_needResize = true; + m_listsDirty = true; + m_needResize = true; } void Register::addItem(RegisterItem* p) { - Q_D(Register); RegisterItem* q = lastItem(); if (q) q->setNextItem(p); p->setPrevItem(q); p->setNextItem(0); - d->m_items.append(p); + m_items.append(p); - if (!d->m_firstItem) - d->m_firstItem = p; - d->m_lastItem = p; - d->m_listsDirty = true; - d->m_needResize = true; + if (!m_firstItem) + m_firstItem = p; + m_lastItem = p; + m_listsDirty = true; + m_needResize = true; } void Register::removeItem(RegisterItem* p) { - Q_D(Register); // remove item from list if (p->prevItem()) p->prevItem()->setNextItem(p->nextItem()); if (p->nextItem()) p->nextItem()->setPrevItem(p->prevItem()); // update first and last pointer if required - if (p == d->m_firstItem) - d->m_firstItem = p->nextItem(); - if (p == d->m_lastItem) - d->m_lastItem = p->prevItem(); + if (p == m_firstItem) + m_firstItem = p->nextItem(); + if (p == m_lastItem) + m_lastItem = p->prevItem(); // make sure we don't do it twice p->setNextItem(0); p->setPrevItem(0); // remove it from the m_items array - int i = d->m_items.indexOf(p); + int i = m_items.indexOf(p); if (-1 != i) { - d->m_items[i] = 0; + m_items[i] = 0; } - d->m_listsDirty = true; - d->m_needResize = true; + m_listsDirty = true; + m_needResize = true; } RegisterItem* Register::firstItem() const { - Q_D(const Register); - return d->m_firstItem; + return m_firstItem; } RegisterItem* Register::nextItem(RegisterItem* item) const { return item->nextItem(); } RegisterItem* Register::lastItem() const { - Q_D(const Register); - return d->m_lastItem; + return m_lastItem; } void Register::setupItemIndex(int rowCount) { - Q_D(Register); // setup index array - d->m_itemIndex.clear(); - d->m_itemIndex.reserve(rowCount); + m_itemIndex.clear(); + m_itemIndex.reserve(rowCount); // fill index array rowCount = 0; RegisterItem* prev = 0; - d->m_firstItem = d->m_lastItem = 0; - for (QVector::size_type i = 0; i < d->m_items.size(); ++i) { - RegisterItem* item = d->m_items[i]; + m_firstItem = m_lastItem = 0; + for (QVector::size_type i = 0; i < m_items.size(); ++i) { + RegisterItem* item = m_items[i]; if (!item) continue; - if (!d->m_firstItem) - d->m_firstItem = item; - d->m_lastItem = item; + if (!m_firstItem) + m_firstItem = item; + m_lastItem = item; if (prev) prev->setNextItem(item); item->setPrevItem(prev); item->setNextItem(0); prev = item; for (int j = item->numRowsRegister(); j; --j) { - d->m_itemIndex.push_back(item); + m_itemIndex.push_back(item); } } } void Register::updateAlternate() const { - Q_D(const Register); bool alternate = false; - for (QVector::size_type i = 0; i < d->m_items.size(); ++i) { - RegisterItem* item = d->m_items[i]; + for (QVector::size_type i = 0; i < m_items.size(); ++i) { + RegisterItem* item = m_items[i]; if (!item) continue; if (item->isVisible()) { item->setAlternate(alternate); alternate ^= true; } } } void Register::suppressAdjacentMarkers() { bool lastWasGroupMarker = false; KMyMoneyRegister::RegisterItem* p = lastItem(); KMyMoneyRegister::Transaction* t = dynamic_cast(p); if (t && t->transaction().id().isEmpty()) { lastWasGroupMarker = true; p = p->prevItem(); } while (p) { KMyMoneyRegister::GroupMarker* m = dynamic_cast(p); if (m) { // make adjacent group marker invisible except those that show statement information if (lastWasGroupMarker && (dynamic_cast(m) == 0)) { m->setVisible(false); } lastWasGroupMarker = true; } else if (p->isVisible()) lastWasGroupMarker = false; p = p->prevItem(); } } void Register::updateRegister(bool forceUpdateRowHeight) { - Q_D(Register); - if (d->m_listsDirty || forceUpdateRowHeight) { + if (m_listsDirty || forceUpdateRowHeight) { // don't get in here recursively - d->m_listsDirty = false; + m_listsDirty = false; int rowCount = 0; // determine the number of rows we need to display all items // while going through the list, check for erroneous transactions - for (QVector::size_type i = 0; i < d->m_items.size(); ++i) { - RegisterItem* item = d->m_items[i]; + for (QVector::size_type i = 0; i < m_items.size(); ++i) { + RegisterItem* item = m_items[i]; if (!item) continue; item->setStartRow(rowCount); item->setNeedResize(); rowCount += item->numRowsRegister(); if (item->isErroneous()) { - if (!d->m_firstErroneous) - d->m_firstErroneous = item; - d->m_lastErroneous = item; + if (!m_firstErroneous) + m_firstErroneous = item; + m_lastErroneous = item; } } updateAlternate(); // create item index setupItemIndex(rowCount); bool needUpdateHeaders = (QTableWidget::rowCount() != rowCount) | forceUpdateRowHeight; // setup QTable. Make sure to suppress screen updates for now setRowCount(rowCount); // if we need to update the headers, we do it now for all rows // again we make sure to suppress screen updates if (needUpdateHeaders) { - for (auto i = 0; i < rowCount; ++i) { + for (int i = 0; i < rowCount; ++i) { RegisterItem* item = itemAtRow(i); if (item->isVisible()) { showRow(i); } else { hideRow(i); } verticalHeader()->resizeSection(i, item->rowHeightHint()); } verticalHeader()->setUpdatesEnabled(true); } // force resizeing of the columns if necessary - if (d->m_needInitialColumnResize) { + if (m_needInitialColumnResize) { QTimer::singleShot(0, this, SLOT(resize())); - d->m_needInitialColumnResize = false; + m_needInitialColumnResize = false; } else { update(); // if the number of rows changed, we might need to resize the register // to make sure we reflect the current visibility of the scrollbars. if (needUpdateHeaders) QTimer::singleShot(0, this, SLOT(resize())); } } } int Register::rowHeightHint() const { - Q_D(const Register); - if (!d->m_rowHeightHint) { + if (!m_rowHeightHint) { qDebug("Register::rowHeightHint(): m_rowHeightHint is zero!!"); } - return d->m_rowHeightHint; + return m_rowHeightHint; } void Register::focusInEvent(QFocusEvent* ev) { - Q_D(const Register); QTableWidget::focusInEvent(ev); - if (d->m_focusItem) { - d->m_focusItem->setFocus(true, false); + if (m_focusItem) { + m_focusItem->setFocus(true, false); } } bool Register::event(QEvent* event) { if (event->type() == QEvent::ToolTip) { QHelpEvent *helpEvent = static_cast(event); // get the row, if it's the header, then we're done // otherwise, adjust the row to be 0 based. int row = rowAt(helpEvent->y()); if (!row) return true; --row; int col = columnAt(helpEvent->x()); RegisterItem* item = itemAtRow(row); if (!item) return true; row = row - item->startRow(); QString msg; QRect rect; if (!item->maybeTip(helpEvent->pos(), row, col, rect, msg)) return true; if (!msg.isEmpty()) { QToolTip::showText(helpEvent->globalPos(), msg); } else { QToolTip::hideText(); event->ignore(); } return true; } return TransactionEditorContainer::event(event); } void Register::focusOutEvent(QFocusEvent* ev) { - Q_D(Register); - if (d->m_focusItem) { - d->m_focusItem->setFocus(false, false); + if (m_focusItem) { + m_focusItem->setFocus(false, false); } QTableWidget::focusOutEvent(ev); } void Register::resizeEvent(QResizeEvent* ev) { TransactionEditorContainer::resizeEvent(ev); resize(DetailColumn, true); } void Register::resize() { resize(DetailColumn); } void Register::resize(int col, bool force) { - Q_D(Register); - if (!d->m_needResize && !force) + if (!m_needResize && !force) return; - d->m_needResize = false; + m_needResize = false; // resize the register int w = viewport()->width(); // TODO I was playing a bit with manual ledger resizing but could not get // a good solution. I just leave the code around, so that maybe others // pick it up again. So far, it's not clear to me where to store the // size of the sections: // // a) with the account (as it is done now) // b) with the application for the specific account type // c) ???? // // Ideas are welcome (ipwizard: 2007-07-19) // Note: currently there's no way to switch back to automatic // column sizing once the manual sizing option has been saved #if 0 if (m_account.value("kmm-ledger-column-width").isEmpty()) { #endif // check which space we need if (columnWidth(NumberColumn)) adjustColumn(NumberColumn); if (columnWidth(AccountColumn)) adjustColumn(AccountColumn); if (columnWidth(PaymentColumn)) adjustColumn(PaymentColumn); if (columnWidth(DepositColumn)) adjustColumn(DepositColumn); if (columnWidth(QuantityColumn)) adjustColumn(QuantityColumn); if (columnWidth(BalanceColumn)) adjustColumn(BalanceColumn); if (columnWidth(PriceColumn)) adjustColumn(PriceColumn); if (columnWidth(ValueColumn)) adjustColumn(ValueColumn); // make amount columns all the same size // only extend the entry columns to make sure they fit // the widget int dwidth = 0; int ewidth = 0; if (ewidth < columnWidth(PaymentColumn)) ewidth = columnWidth(PaymentColumn); if (ewidth < columnWidth(DepositColumn)) ewidth = columnWidth(DepositColumn); if (ewidth < columnWidth(QuantityColumn)) ewidth = columnWidth(QuantityColumn); if (dwidth < columnWidth(BalanceColumn)) dwidth = columnWidth(BalanceColumn); if (ewidth < columnWidth(PriceColumn)) ewidth = columnWidth(PriceColumn); if (dwidth < columnWidth(ValueColumn)) dwidth = columnWidth(ValueColumn); int swidth = columnWidth(SecurityColumn); if (swidth > 0) { adjustColumn(SecurityColumn); swidth = columnWidth(SecurityColumn); } adjustColumn(DateColumn); #ifndef KMM_DESIGNER // Resize the date and money fields to either // a) the size required by the input widget if no transaction form is shown and the register is used with an editor // b) the adjusted value for the input widget if the transaction form is visible or an editor is not used - if (d->m_usedWithEditor && !KMyMoneyGlobalSettings::transactionForm()) { + if (m_usedWithEditor && !KMyMoneyGlobalSettings::transactionForm()) { QPushButton *pushButton = new QPushButton; const int pushButtonSpacing = pushButton->sizeHint().width() + 5; setColumnWidth(DateColumn, columnWidth(DateColumn) + pushButtonSpacing + 4/* space for the spinbox arrows */); ewidth += pushButtonSpacing; if (swidth > 0) { // extend the security width to make space for the selector arrow swidth = columnWidth(SecurityColumn) + 40; } delete pushButton; } #endif if (columnWidth(PaymentColumn)) setColumnWidth(PaymentColumn, ewidth); if (columnWidth(DepositColumn)) setColumnWidth(DepositColumn, ewidth); if (columnWidth(QuantityColumn)) setColumnWidth(QuantityColumn, ewidth); if (columnWidth(BalanceColumn)) setColumnWidth(BalanceColumn, dwidth); if (columnWidth(PriceColumn)) setColumnWidth(PriceColumn, ewidth); if (columnWidth(ValueColumn)) setColumnWidth(ValueColumn, dwidth); if (columnWidth(ReconcileFlagColumn)) setColumnWidth(ReconcileFlagColumn, 20); if (swidth > 0) setColumnWidth(SecurityColumn, swidth); #if 0 // see comment above } else { QStringList colSizes = QStringList::split(",", m_account.value("kmm-ledger-column-width"), true); for (int i; i < colSizes.count(); ++i) { int colWidth = colSizes[i].toInt(); if (colWidth == 0) continue; setColumnWidth(i, w * colWidth / 100); } } #endif - for (auto i = 0; i < columnCount(); ++i) { + for (int i = 0; i < columnCount(); ++i) { if (i == col) continue; w -= columnWidth(i); } setColumnWidth(col, w); } -void Register::forceUpdateLists() -{ - Q_D(Register); - d->m_listsDirty = true; -} - int Register::minimumColumnWidth(int col) { - Q_D(Register); QHeaderView *topHeader = horizontalHeader(); int w = topHeader->fontMetrics().width(horizontalHeaderItem(col) ? horizontalHeaderItem(col)->text() : QString()) + 10; w = qMax(w, 20); #ifdef KMM_DESIGNER return w; #else int maxWidth = 0; int minWidth = 0; QFontMetrics cellFontMetrics(KMyMoneyGlobalSettings::listCellFont()); switch (col) { case DateColumn: minWidth = cellFontMetrics.width(QLocale().toString(QDate(6999, 12, 29), QLocale::ShortFormat) + " "); break; default: break; } // scan through the transactions - for (auto i = 0; i < d->m_items.size(); ++i) { - RegisterItem* const item = d->m_items[i]; + for (int i = 0; i < m_items.size(); ++i) { + RegisterItem* const item = m_items[i]; if (!item) continue; Transaction* t = dynamic_cast(item); if (t) { int nw = 0; try { nw = t->registerColWidth(col, cellFontMetrics); } catch (const MyMoneyException &) { // This should only be reached if the data in the file disappeared // from under us, such as when the account was deleted from a // different view, then this view is restored. In this case, new // data is about to be loaded into the view anyway, so just remove // the item from the register and swallow the exception. //qDebug("%s", qPrintable(e.what())); removeItem(t); } w = qMax(w, nw); if (maxWidth) { if (w > maxWidth) { w = maxWidth; break; } } if (w < minWidth) { w = minWidth; break; } } } return w; #endif } void Register::adjustColumn(int col) { setColumnWidth(col, minimumColumnWidth(col)); } void Register::clearSelection() { unselectItems(); TransactionEditorContainer::clearSelection(); } void Register::doSelectItems(int from, int to, bool selected) { - Q_D(Register); int start, end; // make sure start is smaller than end if (from <= to) { start = from; end = to; } else { start = to; end = from; } // make sure we stay in bounds if (start < 0) start = 0; - if ((end <= -1) || (end > (d->m_items.size() - 1))) - end = d->m_items.size() - 1; + if ((end <= -1) || (end > (m_items.size() - 1))) + end = m_items.size() - 1; RegisterItem* firstItem; RegisterItem* lastItem; firstItem = lastItem = 0; for (int i = start; i <= end; ++i) { - RegisterItem* const item = d->m_items[i]; + RegisterItem* const item = m_items[i]; if (item) { if (selected != item->isSelected()) { if (!firstItem) firstItem = item; item->setSelected(selected); lastItem = item; } } } } RegisterItem* Register::itemAtRow(int row) const { - Q_D(const Register); - if (row >= 0 && row < d->m_itemIndex.size()) { - return d->m_itemIndex[row]; + if (row >= 0 && row < m_itemIndex.size()) { + return m_itemIndex[row]; } return 0; } int Register::rowToIndex(int row) const { - Q_D(const Register); - for (auto i = 0; i < d->m_items.size(); ++i) { - RegisterItem* const item = d->m_items[i]; + for (int i = 0; i < m_items.size(); ++i) { + RegisterItem* const item = m_items[i]; if (!item) continue; if (row >= item->startRow() && row < (item->startRow() + item->numRowsRegister())) return i; } return -1; } void Register::selectedTransactions(SelectedTransactions& list) const { - Q_D(const Register); - if (d->m_focusItem && d->m_focusItem->isSelected() && d->m_focusItem->isVisible()) { - Transaction* t = dynamic_cast(d->m_focusItem); + if (m_focusItem && m_focusItem->isSelected() && m_focusItem->isVisible()) { + Transaction* t = dynamic_cast(m_focusItem); if (t) { QString id; if (t->isScheduled()) id = t->transaction().id(); SelectedTransaction s(t->transaction(), t->split(), id); list << s; } } - for (auto i = 0; i < d->m_items.size(); ++i) { - RegisterItem* const item = d->m_items[i]; + for (int i = 0; i < m_items.size(); ++i) { + RegisterItem* const item = m_items[i]; // make sure, we don't include the focus item twice - if (item == d->m_focusItem) + if (item == m_focusItem) continue; if (item && item->isSelected() && item->isVisible()) { Transaction* t = dynamic_cast(item); if (t) { QString id; if (t->isScheduled()) id = t->transaction().id(); SelectedTransaction s(t->transaction(), t->split(), id); list << s; } } } } QList Register::selectedItems() const { - Q_D(const Register); QList list; - RegisterItem* item = d->m_firstItem; + RegisterItem* item = m_firstItem; while (item) { if (item && item->isSelected() && item->isVisible()) { list << item; } item = item->nextItem(); } return list; } int Register::selectedItemsCount() const { - Q_D(const Register); int cnt = 0; - RegisterItem* item = d->m_firstItem; + RegisterItem* item = m_firstItem; while (item) { if (item->isSelected() && item->isVisible()) ++cnt; item = item->nextItem(); } return cnt; } void Register::mouseReleaseEvent(QMouseEvent *e) { - Q_D(Register); if (e->button() == Qt::RightButton) { // see the comment in Register::contextMenuEvent // on Linux we never get here but on Windows this // event is fired before the contextMenuEvent which // causes the loss of the multiple selection; to avoid // this just ignore the event and act like on Linux return; } - if (d->m_ignoreNextButtonRelease) { - d->m_ignoreNextButtonRelease = false; + if (m_ignoreNextButtonRelease) { + m_ignoreNextButtonRelease = false; return; } - d->m_mouseButton = e->button(); - d->m_modifiers = QApplication::keyboardModifiers(); + m_mouseButton = e->button(); + m_modifiers = QApplication::keyboardModifiers(); QTableWidget::mouseReleaseEvent(e); } void Register::contextMenuEvent(QContextMenuEvent *e) { - Q_D(Register); if (e->reason() == QContextMenuEvent::Mouse) { // since mouse release event is not called, we need // to reset the mouse button and the modifiers here - d->m_mouseButton = Qt::NoButton; - d->m_modifiers = Qt::NoModifier; + m_mouseButton = Qt::NoButton; + m_modifiers = Qt::NoModifier; // if a selected item is clicked don't change the selection RegisterItem* item = itemAtRow(rowAt(e->y())); if (item && !item->isSelected()) selectItem(rowAt(e->y()), columnAt(e->x())); } openContextMenu(); } -void Register::unselectItems(int from, int to) -{ - doSelectItems(from, to, false); -} - -void Register::selectItems(int from, int to) -{ - doSelectItems(from, to, true); -} - void Register::selectItem(int row, int col) { - Q_D(Register); - if (row >= 0 && row < d->m_itemIndex.size()) { - RegisterItem* item = d->m_itemIndex[row]; + if (row >= 0 && row < m_itemIndex.size()) { + RegisterItem* item = m_itemIndex[row]; // don't support selecting when the item has an editor // or the item itself is not selectable if (item->hasEditorOpen() || !item->isSelectable()) { - d->m_mouseButton = Qt::NoButton; + m_mouseButton = Qt::NoButton; return; } QString id = item->id(); selectItem(item); // selectItem() might have changed the pointers, so we // need to reconstruct it here item = itemById(id); Transaction* t = dynamic_cast(item); if (t) { if (!id.isEmpty()) { if (t && col == ReconcileFlagColumn && selectedItemsCount() == 1 && !t->isScheduled()) emit reconcileStateColumnClicked(t); } else { emit emptyItemSelected(); } } } } void Register::setAnchorItem(RegisterItem* anchorItem) { - Q_D(Register); - d->m_selectAnchor = anchorItem; + m_selectAnchor = anchorItem; } bool Register::setFocusItem(RegisterItem* focusItem) { - Q_D(Register); if (focusItem && focusItem->canHaveFocus()) { - if (d->m_focusItem) { - d->m_focusItem->setFocus(false); + if (m_focusItem) { + m_focusItem->setFocus(false); } Transaction* item = dynamic_cast(focusItem); - if (d->m_focusItem != focusItem && item) { + if (m_focusItem != focusItem && item) { emit focusChanged(item); } - d->m_focusItem = focusItem; - d->m_focusItem->setFocus(true); - if (d->m_listsDirty) + m_focusItem = focusItem; + m_focusItem->setFocus(true); + if (m_listsDirty) updateRegister(KMyMoneyGlobalSettings::ledgerLens() | !KMyMoneyGlobalSettings::transactionForm()); - ensureItemVisible(d->m_focusItem); + ensureItemVisible(m_focusItem); return true; } else return false; } bool Register::setFocusToTop() { - Q_D(Register); - RegisterItem* rgItem = d->m_firstItem; + RegisterItem* rgItem = m_firstItem; while (rgItem) { if (setFocusItem(rgItem)) return true; rgItem = rgItem->nextItem(); } return false; } void Register::selectItem(RegisterItem* item, bool dontChangeSelections) { - Q_D(Register); if (!item) return; - Qt::MouseButtons buttonState = d->m_mouseButton; - Qt::KeyboardModifiers modifiers = d->m_modifiers; - d->m_mouseButton = Qt::NoButton; - d->m_modifiers = Qt::NoModifier; + Qt::MouseButtons buttonState = m_mouseButton; + Qt::KeyboardModifiers modifiers = m_modifiers; + m_mouseButton = Qt::NoButton; + m_modifiers = Qt::NoModifier; - if (d->m_selectionMode == NoSelection) + if (m_selectionMode == NoSelection) return; if (item->isSelectable()) { QString id = item->id(); QList itemList = selectedItems(); bool okToSelect = true; int cnt = itemList.count(); const bool scheduledTransactionSelected = (cnt > 0 && itemList.front() && (typeid(*(itemList.front())) == typeid(StdTransactionScheduled))); if (buttonState & Qt::LeftButton) { if (!(modifiers & (Qt::ShiftModifier | Qt::ControlModifier)) - || (d->m_selectAnchor == 0)) { + || (m_selectAnchor == 0)) { if ((cnt != 1) || ((cnt == 1) && !item->isSelected())) { emit aboutToSelectItem(item, okToSelect); if (okToSelect) { // pointer 'item' might have changed. reconstruct it. item = itemById(id); unselectItems(); item->setSelected(true); setFocusItem(item); } } if (okToSelect) - d->m_selectAnchor = item; + m_selectAnchor = item; } - if (d->m_selectionMode == MultiSelection) { + if (m_selectionMode == MultiSelection) { switch (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)) { case Qt::ControlModifier: if (scheduledTransactionSelected || typeid(*item) == typeid(StdTransactionScheduled)) okToSelect = false; // toggle selection state of current item emit aboutToSelectItem(item, okToSelect); if (okToSelect) { // pointer 'item' might have changed. reconstruct it. item = itemById(id); item->setSelected(!item->isSelected()); setFocusItem(item); } break; case Qt::ShiftModifier: if (scheduledTransactionSelected || typeid(*item) == typeid(StdTransactionScheduled)) okToSelect = false; emit aboutToSelectItem(item, okToSelect); if (okToSelect) { // pointer 'item' might have changed. reconstruct it. item = itemById(id); unselectItems(); - selectItems(rowToIndex(d->m_selectAnchor->startRow()), rowToIndex(item->startRow())); + selectItems(rowToIndex(m_selectAnchor->startRow()), rowToIndex(item->startRow())); setFocusItem(item); } break; } } } else { // we get here when called by application logic emit aboutToSelectItem(item, okToSelect); if (okToSelect) { // pointer 'item' might have changed. reconstruct it. item = itemById(id); if (!dontChangeSelections) unselectItems(); item->setSelected(true); setFocusItem(item); - d->m_selectAnchor = item; + m_selectAnchor = item; } } if (okToSelect) { SelectedTransactions list(this); emit transactionsSelected(list); } } } void Register::ensureItemVisible(RegisterItem* item) { - Q_D(Register); if (!item) return; - d->m_ensureVisibleItem = item; + m_ensureVisibleItem = item; QTimer::singleShot(0, this, SLOT(slotEnsureItemVisible())); } void Register::slotDoubleClicked(int row, int) { - Q_D(Register); - if (row >= 0 && row < d->m_itemIndex.size()) { - RegisterItem* p = d->m_itemIndex[row]; + if (row >= 0 && row < m_itemIndex.size()) { + RegisterItem* p = m_itemIndex[row]; if (p->isSelectable()) { - d->m_ignoreNextButtonRelease = true; + m_ignoreNextButtonRelease = true; // double click to start editing only works if the focus // item is among the selected ones if (!focusItem()) { setFocusItem(p); - if (d->m_selectionMode != NoSelection) + if (m_selectionMode != NoSelection) p->setSelected(true); } - if (d->m_focusItem->isSelected()) { + if (m_focusItem->isSelected()) { // don't emit the signal right away but wait until // we come back to the Qt main loop QTimer::singleShot(0, this, SIGNAL(editTransaction())); } } } } void Register::slotEnsureItemVisible() { - Q_D(Register); // if clear() has been called since the timer was // started, we just ignore the call - if (!d->m_ensureVisibleItem) + if (!m_ensureVisibleItem) return; // make sure to catch latest changes setUpdatesEnabled(false); updateRegister(); setUpdatesEnabled(true); // since the item will be made visible at the top of the viewport make the bottom index visible first to make the whole item visible - scrollTo(model()->index(d->m_ensureVisibleItem->startRow() + d->m_ensureVisibleItem->numRowsRegister() - 1, DetailColumn)); - scrollTo(model()->index(d->m_ensureVisibleItem->startRow(), DetailColumn)); + scrollTo(model()->index(m_ensureVisibleItem->startRow() + m_ensureVisibleItem->numRowsRegister() - 1, DetailColumn)); + scrollTo(model()->index(m_ensureVisibleItem->startRow(), DetailColumn)); } -QString Register::text(int /*row*/, int /*col*/) const +TransactionSortField KMyMoneyRegister::textToSortOrder(const QString& text) { - return QString("a"); + for (int idx = 1; idx < static_cast(MaxSortFields); ++idx) { + if (text == i18n(sortOrderText[idx])) { + return static_cast(idx); + } + } + return UnknownSort; } -QWidget* Register::createEditor(int /*row*/, int /*col*/, bool /*initFromCell*/) const +const QString KMyMoneyRegister::sortOrderToText(TransactionSortField idx) { - return 0; + if (idx < PostDateSort || idx >= MaxSortFields) + idx = UnknownSort; + return i18n(sortOrderText[idx]); } -void Register::setCellContentFromEditor(int /*row*/, int /*col*/) +QString Register::text(int /*row*/, int /*col*/) const { + return QString("a"); } -void Register::endEdit(int /*row*/, int /*col*/, bool /*accept*/, bool /*replace*/) +QWidget* Register::createEditor(int /*row*/, int /*col*/, bool /*initFromCell*/) const { + return 0; } -RegisterItem* Register::focusItem() const +void Register::setCellContentFromEditor(int /*row*/, int /*col*/) { - Q_D(const Register); - return d->m_focusItem; } -RegisterItem* Register::anchorItem() const +void Register::endEdit(int /*row*/, int /*col*/, bool /*accept*/, bool /*replace*/) { - Q_D(const Register); - return d->m_selectAnchor; } void Register::arrangeEditWidgets(QMap& editWidgets, KMyMoneyRegister::Transaction* t) { t->arrangeWidgetsInRegister(editWidgets); ensureItemVisible(t); // updateContents(); } void Register::tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const { t->tabOrderInRegister(tabOrderWidgets); } void Register::removeEditWidgets(QMap& editWidgets) { // remove pointers from map QMap::iterator it; for (it = editWidgets.begin(); it != editWidgets.end();) { if ((*it)->parentWidget() == this) { editWidgets.erase(it); it = editWidgets.begin(); } else ++it; } // now delete the widgets KMyMoneyRegister::Transaction* t = dynamic_cast(focusItem()); for (int row = t->startRow(); row < t->startRow() + t->numRowsRegister(true); ++row) { for (int col = 0; col < columnCount(); ++col) { if (cellWidget(row, col)) { cellWidget(row, col)->hide(); setCellWidget(row, col, 0); } } // make sure to reduce the possibly size to what it was before editing started setRowHeight(row, t->rowHeightHint()); } } RegisterItem* Register::itemById(const QString& id) const { - Q_D(const Register); if (id.isEmpty()) - return d->m_lastItem; + return m_lastItem; - for (QVector::size_type i = 0; i < d->m_items.size(); ++i) { - RegisterItem* item = d->m_items[i]; + for (QVector::size_type i = 0; i < m_items.size(); ++i) { + RegisterItem* item = m_items[i]; if (!item) continue; if (item->id() == id) return item; } return 0; } void Register::handleItemChange(RegisterItem* old, bool shift, bool control) { - Q_D(Register); - if (d->m_selectionMode == MultiSelection) { + if (m_selectionMode == MultiSelection) { if (shift) { - selectRange(d->m_selectAnchor ? d->m_selectAnchor : old, - d->m_focusItem, false, true, (d->m_selectAnchor && !control) ? true : false); + selectRange(m_selectAnchor ? m_selectAnchor : old, + m_focusItem, false, true, (m_selectAnchor && !control) ? true : false); } else if (!control) { - selectItem(d->m_focusItem, false); + selectItem(m_focusItem, false); } } } void Register::selectRange(RegisterItem* from, RegisterItem* to, bool invert, bool includeFirst, bool clearSel) { if (!from || !to) return; if (from == to && !includeFirst) return; bool swap = false; if (to == from->prevItem()) swap = true; RegisterItem* item; if (!swap && from != to && from != to->prevItem()) { bool found = false; for (item = from; item; item = item->nextItem()) { if (item == to) { found = true; break; } } if (!found) swap = true; } if (swap) { item = from; from = to; to = item; if (!includeFirst) to = to->prevItem(); } else if (!includeFirst) { from = from->nextItem(); } if (clearSel) { for (item = firstItem(); item; item = item->nextItem()) { if (item->isSelected() && item->isVisible()) { item->setSelected(false); } } } for (item = from; item; item = item->nextItem()) { if (item->isSelectable()) { if (!invert) { if (!item->isSelected() && item->isVisible()) { item->setSelected(true); } } else { bool sel = !item->isSelected(); if ((item->isSelected() != sel) && item->isVisible()) { item->setSelected(sel); } } } if (item == to) break; } } void Register::scrollPage(int key, Qt::KeyboardModifiers modifiers) { - Q_D(Register); - RegisterItem* oldFocusItem = d->m_focusItem; + RegisterItem* oldFocusItem = m_focusItem; // make sure we have a focus item - if (!d->m_focusItem) - setFocusItem(d->m_firstItem); - if (!d->m_focusItem && d->m_firstItem) - setFocusItem(d->m_firstItem->nextItem()); - if (!d->m_focusItem) + if (!m_focusItem) + setFocusItem(m_firstItem); + if (!m_focusItem && m_firstItem) + setFocusItem(m_firstItem->nextItem()); + if (!m_focusItem) return; - RegisterItem* item = d->m_focusItem; + RegisterItem* item = m_focusItem; int height = 0; switch (key) { case Qt::Key_PageUp: while (height < viewport()->height() && item->prevItem()) { do { item = item->prevItem(); if (item->isVisible()) height += item->rowHeightHint(); } while ((!item->isSelectable() || !item->isVisible()) && item->prevItem()); while ((!item->isSelectable() || !item->isVisible()) && item->nextItem()) item = item->nextItem(); } break; case Qt::Key_PageDown: while (height < viewport()->height() && item->nextItem()) { do { if (item->isVisible()) height += item->rowHeightHint(); item = item->nextItem(); } while ((!item->isSelectable() || !item->isVisible()) && item->nextItem()); while ((!item->isSelectable() || !item->isVisible()) && item->prevItem()) item = item->prevItem(); } break; case Qt::Key_Up: if (item->prevItem()) { do { item = item->prevItem(); } while ((!item->isSelectable() || !item->isVisible()) && item->prevItem()); } break; case Qt::Key_Down: if (item->nextItem()) { do { item = item->nextItem(); } while ((!item->isSelectable() || !item->isVisible()) && item->nextItem()); } break; case Qt::Key_Home: - item = d->m_firstItem; + item = m_firstItem; while ((!item->isSelectable() || !item->isVisible()) && item->nextItem()) item = item->nextItem(); break; case Qt::Key_End: - item = d->m_lastItem; + item = m_lastItem; while ((!item->isSelectable() || !item->isVisible()) && item->prevItem()) item = item->prevItem(); break; } // make sure to avoid selecting a possible empty transaction at the end Transaction* t = dynamic_cast(item); if (t && t->transaction().id().isEmpty()) { if (t->prevItem()) { item = t->prevItem(); } } - if (!(modifiers & Qt::ShiftModifier) || !d->m_selectAnchor) - d->m_selectAnchor = item; + if (!(modifiers & Qt::ShiftModifier) || !m_selectAnchor) + m_selectAnchor = item; setFocusItem(item); if (item->isSelectable()) { handleItemChange(oldFocusItem, modifiers & Qt::ShiftModifier, modifiers & Qt::ControlModifier); // tell the world about the changes in selection SelectedTransactions list(this); emit transactionsSelected(list); } - if (d->m_focusItem && !d->m_focusItem->isSelected() && d->m_selectionMode == SingleSelection) + if (m_focusItem && !m_focusItem->isSelected() && m_selectionMode == SingleSelection) selectItem(item); } void Register::keyPressEvent(QKeyEvent* ev) { - Q_D(Register); switch (ev->key()) { case Qt::Key_Space: - if (d->m_selectionMode != NoSelection) { + if (m_selectionMode != NoSelection) { // get the state out of the event ... - d->m_modifiers = ev->modifiers(); + m_modifiers = ev->modifiers(); // ... and pretend that we have pressed the left mouse button ;) - d->m_mouseButton = Qt::LeftButton; - selectItem(d->m_focusItem); + m_mouseButton = Qt::LeftButton; + selectItem(m_focusItem); } break; case Qt::Key_PageUp: case Qt::Key_PageDown: case Qt::Key_Home: case Qt::Key_End: case Qt::Key_Down: case Qt::Key_Up: scrollPage(ev->key(), ev->modifiers()); break; case Qt::Key_Enter: case Qt::Key_Return: // don't emit the signal right away but wait until // we come back to the Qt main loop QTimer::singleShot(0, this, SIGNAL(editTransaction())); break; default: QTableWidget::keyPressEvent(ev); break; } } Transaction* Register::transactionFactory(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) { Transaction* t = 0; MyMoneySplit s = split; if (parent->account() == MyMoneyAccount()) { t = new KMyMoneyRegister::StdTransaction(parent, transaction, s, uniqueId); return t; } switch (parent->account().accountType()) { case Account::Checkings: case Account::Savings: case Account::Cash: case Account::CreditCard: case Account::Loan: case Account::Asset: case Account::Liability: case Account::Currency: case Account::Income: case Account::Expense: case Account::AssetLoan: case Account::Equity: if (s.accountId().isEmpty()) s.setAccountId(parent->account().id()); if (s.isMatched()) t = new KMyMoneyRegister::StdTransactionMatched(parent, transaction, s, uniqueId); else if (transaction.isImported()) t = new KMyMoneyRegister::StdTransactionDownloaded(parent, transaction, s, uniqueId); else t = new KMyMoneyRegister::StdTransaction(parent, transaction, s, uniqueId); break; case Account::Investment: if (s.isMatched()) t = new KMyMoneyRegister::InvestTransaction/* Matched */(parent, transaction, s, uniqueId); else if (transaction.isImported()) t = new KMyMoneyRegister::InvestTransactionDownloaded(parent, transaction, s, uniqueId); else t = new KMyMoneyRegister::InvestTransaction(parent, transaction, s, uniqueId); break; case Account::CertificateDep: case Account::MoneyMarket: case Account::Stock: default: qDebug("Register::transactionFactory: invalid accountTypeE %d", (int)parent->account().accountType()); break; } return t; } -const MyMoneyAccount& Register::account() const -{ - Q_D(const Register); - return d->m_account; -} - void Register::addGroupMarkers() { - Q_D(Register); QMap list; QMap::const_iterator it; KMyMoneyRegister::RegisterItem* p = firstItem(); KMyMoneyRegister::Transaction* t; QString name; QDate today; QDate yesterday, thisWeek, lastWeek; QDate thisMonth, lastMonth; QDate thisYear; int weekStartOfs; switch (primarySortKey()) { - case eWidgets::SortField::PostDate: - case eWidgets::SortField::EntryDate: + case KMyMoneyRegister::PostDateSort: + case KMyMoneyRegister::EntryDateSort: today = QDate::currentDate(); thisMonth.setDate(today.year(), today.month(), 1); lastMonth = thisMonth.addMonths(-1); yesterday = today.addDays(-1); // a = QDate::dayOfWeek() todays weekday (1 = Monday, 7 = Sunday) // b = QLocale().firstDayOfWeek() first day of week (1 = Monday, 7 = Sunday) weekStartOfs = today.dayOfWeek() - QLocale().firstDayOfWeek(); if (weekStartOfs < 0) { weekStartOfs = 7 + weekStartOfs; } thisWeek = today.addDays(-weekStartOfs); lastWeek = thisWeek.addDays(-7); thisYear.setDate(today.year(), 1, 1); if (KMyMoneyGlobalSettings::startDate().date() != QDate(1900, 1, 1)) new KMyMoneyRegister::FancyDateGroupMarker(this, KMyMoneyGlobalSettings::startDate().date(), i18n("Prior transactions possibly filtered")); if (KMyMoneyGlobalSettings::showFancyMarker()) { - if (d->m_account.lastReconciliationDate().isValid()) - new KMyMoneyRegister::StatementGroupMarker(this, KMyMoneyRegister::Deposit, d->m_account.lastReconciliationDate(), i18n("Last reconciliation")); + if (m_account.lastReconciliationDate().isValid()) + new KMyMoneyRegister::StatementGroupMarker(this, KMyMoneyRegister::Deposit, m_account.lastReconciliationDate(), i18n("Last reconciliation")); - if (!d->m_account.value("lastImportedTransactionDate").isEmpty() - && !d->m_account.value("lastStatementBalance").isEmpty()) { - MyMoneyMoney balance(d->m_account.value("lastStatementBalance")); - if (d->m_account.accountGroup() == Account::Liability) + if (!m_account.value("lastImportedTransactionDate").isEmpty() + && !m_account.value("lastStatementBalance").isEmpty()) { + MyMoneyMoney balance(m_account.value("lastStatementBalance")); + if (m_account.accountGroup() == Account::Liability) balance = -balance; - auto txt = i18n("Online Statement Balance: %1", balance.formatMoney(d->m_account.fraction())); + QString txt = i18n("Online Statement Balance: %1", balance.formatMoney(m_account.fraction())); - KMyMoneyRegister::StatementGroupMarker *p = new KMyMoneyRegister::StatementGroupMarker(this, KMyMoneyRegister::Deposit, QDate::fromString(d->m_account.value("lastImportedTransactionDate"), Qt::ISODate), txt); + KMyMoneyRegister::StatementGroupMarker *p = new KMyMoneyRegister::StatementGroupMarker(this, KMyMoneyRegister::Deposit, QDate::fromString(m_account.value("lastImportedTransactionDate"), Qt::ISODate), txt); - p->setErroneous(!MyMoneyFile::instance()->hasMatchingOnlineBalance(d->m_account)); + p->setErroneous(!MyMoneyFile::instance()->hasMatchingOnlineBalance(m_account)); } new KMyMoneyRegister::FancyDateGroupMarker(this, thisYear, i18n("This year")); new KMyMoneyRegister::FancyDateGroupMarker(this, lastMonth, i18n("Last month")); new KMyMoneyRegister::FancyDateGroupMarker(this, thisMonth, i18n("This month")); new KMyMoneyRegister::FancyDateGroupMarker(this, lastWeek, i18n("Last week")); new KMyMoneyRegister::FancyDateGroupMarker(this, thisWeek, i18n("This week")); new KMyMoneyRegister::FancyDateGroupMarker(this, yesterday, i18n("Yesterday")); new KMyMoneyRegister::FancyDateGroupMarker(this, today, i18n("Today")); new KMyMoneyRegister::FancyDateGroupMarker(this, today.addDays(1), i18n("Future transactions")); new KMyMoneyRegister::FancyDateGroupMarker(this, thisWeek.addDays(7), i18n("Next week")); new KMyMoneyRegister::FancyDateGroupMarker(this, thisMonth.addMonths(1), i18n("Next month")); } else { new KMyMoneyRegister::SimpleDateGroupMarker(this, today.addDays(1), i18n("Future transactions")); } if (KMyMoneyGlobalSettings::showFiscalMarker()) { QDate currentFiscalYear = KMyMoneyGlobalSettings::firstFiscalDate(); new KMyMoneyRegister::FiscalYearGroupMarker(this, currentFiscalYear, i18n("Current fiscal year")); new KMyMoneyRegister::FiscalYearGroupMarker(this, currentFiscalYear.addYears(-1), i18n("Previous fiscal year")); new KMyMoneyRegister::FiscalYearGroupMarker(this, currentFiscalYear.addYears(1), i18n("Next fiscal year")); } break; - case eWidgets::SortField::Type: + case KMyMoneyRegister::TypeSort: if (KMyMoneyGlobalSettings::showFancyMarker()) { - new KMyMoneyRegister::TypeGroupMarker(this, KMyMoneyRegister::Deposit, d->m_account.accountType()); - new KMyMoneyRegister::TypeGroupMarker(this, KMyMoneyRegister::Payment, d->m_account.accountType()); + new KMyMoneyRegister::TypeGroupMarker(this, KMyMoneyRegister::Deposit, m_account.accountType()); + new KMyMoneyRegister::TypeGroupMarker(this, KMyMoneyRegister::Payment, m_account.accountType()); } break; - case eWidgets::SortField::ReconcileState: + case KMyMoneyRegister::ReconcileStateSort: if (KMyMoneyGlobalSettings::showFancyMarker()) { new KMyMoneyRegister::ReconcileGroupMarker(this, eMyMoney::Split::State::NotReconciled); new KMyMoneyRegister::ReconcileGroupMarker(this, eMyMoney::Split::State::Cleared); new KMyMoneyRegister::ReconcileGroupMarker(this, eMyMoney::Split::State::Reconciled); new KMyMoneyRegister::ReconcileGroupMarker(this, eMyMoney::Split::State::Frozen); } break; - case eWidgets::SortField::Payee: + case KMyMoneyRegister::PayeeSort: if (KMyMoneyGlobalSettings::showFancyMarker()) { while (p) { t = dynamic_cast(p); if (t) { list[t->sortPayee()] = 1; } p = p->nextItem(); } for (it = list.constBegin(); it != list.constEnd(); ++it) { name = it.key(); if (name.isEmpty()) { name = i18nc("Unknown payee", "Unknown"); } new KMyMoneyRegister::PayeeGroupMarker(this, name); } } break; - case eWidgets::SortField::Category: + case KMyMoneyRegister::CategorySort: if (KMyMoneyGlobalSettings::showFancyMarker()) { while (p) { t = dynamic_cast(p); if (t) { list[t->sortCategory()] = 1; } p = p->nextItem(); } for (it = list.constBegin(); it != list.constEnd(); ++it) { name = it.key(); if (name.isEmpty()) { name = i18nc("Unknown category", "Unknown"); } new KMyMoneyRegister::CategoryGroupMarker(this, name); } } break; - case eWidgets::SortField::Security: + case KMyMoneyRegister::SecuritySort: if (KMyMoneyGlobalSettings::showFancyMarker()) { while (p) { t = dynamic_cast(p); if (t) { list[t->sortSecurity()] = 1; } p = p->nextItem(); } for (it = list.constBegin(); it != list.constEnd(); ++it) { name = it.key(); if (name.isEmpty()) { name = i18nc("Unknown security", "Unknown"); } new KMyMoneyRegister::CategoryGroupMarker(this, name); } } break; default: // no markers supported break; } } void Register::removeUnwantedGroupMarkers() { // remove all trailing group markers except statement markers KMyMoneyRegister::RegisterItem* q; KMyMoneyRegister::RegisterItem* p = lastItem(); while (p) { q = p; if (dynamic_cast(p) || dynamic_cast(p)) break; p = p->prevItem(); delete q; } // remove all adjacent group markers bool lastWasGroupMarker = false; p = lastItem(); while (p) { q = p; KMyMoneyRegister::GroupMarker* m = dynamic_cast(p); p = p->prevItem(); if (m) { m->markVisible(true); // make adjacent group marker invisible except those that show statement information if (lastWasGroupMarker && (dynamic_cast(m) == 0)) { m->markVisible(false); } lastWasGroupMarker = true; } else if (q->isVisible()) lastWasGroupMarker = false; } } -void Register::setLedgerLensForced(bool forced) -{ - Q_D(Register); - d->m_ledgerLensForced = forced; -} - -bool Register::ledgerLens() const -{ - Q_D(const Register); - return d->m_ledgerLensForced; -} - -void Register::setSelectionMode(SelectionMode mode) -{ - Q_D(Register); - d->m_selectionMode = mode; -} - -void Register::setUsedWithEditor(bool value) -{ - Q_D(Register); - d->m_usedWithEditor = value; -} - DetailsColumnType Register::getDetailsColumnType() const { - Q_D(const Register); - return d->m_detailsColumnType; + return m_detailsColumnType; } void Register::setDetailsColumnType(DetailsColumnType detailsColumnType) { - Q_D(Register); - d->m_detailsColumnType = detailsColumnType; + m_detailsColumnType = detailsColumnType; } diff --git a/kmymoney/widgets/register.h b/kmymoney/widgets/register.h index 1ff83d381..5a1fb1456 100644 --- a/kmymoney/widgets/register.h +++ b/kmymoney/widgets/register.h @@ -1,538 +1,660 @@ /*************************************************************************** register.h ---------- begin : Fri Mar 10 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart - (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef REGISTER_H #define REGISTER_H -#include "kmm_widgets_export.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +#include "mymoneyaccount.h" #include "registeritem.h" #include "transaction.h" #include "transactioneditorcontainer.h" -#include "selectedtransactions.h" -#include "groupmarker.h" -#include "fancydategroupmarker.h" +#include "selectedtransaction.h" -class MyMoneyAccount; class MyMoneyTransaction; -namespace eWidgets { enum class SortField; } -namespace eMyMoney { enum class Account; } - namespace KMyMoneyRegister { +typedef enum { + UnknownSort = 0, ///< unknown sort criteria + PostDateSort = 1, ///< sort by post date + EntryDateSort, ///< sort by entry date + PayeeSort, ///< sort by payee name + ValueSort, ///< sort by value + NoSort, ///< sort by number field + EntryOrderSort, ///< sort by entry order + TypeSort, ///< sort by CashFlowDirection + CategorySort, ///< sort by Category + ReconcileStateSort, ///< sort by reconciliation state + SecuritySort, ///< sort by security (only useful for investment accounts) + // insert new values in front of this line + MaxSortFields +} TransactionSortField; + typedef enum { AscendingOrder = 0, ///< sort in ascending order DescendingOrder ///< sort in descending order } SortDirection; typedef enum { PayeeFirst = 0, ///< show the payee on the first row of the transaction in the details column and the account on the second AccountFirst ///< show the account on the first row of the transaction in the details column and the payee on the second } DetailsColumnType; class Register; class RegisterItem; class ItemPtrVector; +const QString sortOrderToText(TransactionSortField idx); +TransactionSortField textToSortOrder(const QString& text); + + class QWidgetContainer : public QMap { public: QWidgetContainer() {} QWidget* haveWidget(const QString& name) const { QWidgetContainer::const_iterator it_w; it_w = find(name); if (it_w != end()) return *it_w; return 0; } void removeOrphans() { QWidgetContainer::iterator it_w; for (it_w = begin(); it_w != end();) { if ((*it_w) && (*it_w)->parent()) ++it_w; else { delete(*it_w); remove(it_w.key()); it_w = begin(); } } } }; -class StatementGroupMarkerPrivate; -class StatementGroupMarker : public FancyDateGroupMarker +class GroupMarker : public RegisterItem { - Q_DISABLE_COPY(StatementGroupMarker) - public: - explicit StatementGroupMarker(Register* getParent, CashFlowDirection dir, const QDate& date, const QString& txt); - ~StatementGroupMarker() override; + explicit GroupMarker(Register* parent, const QString& txt = QString()); + ~GroupMarker(); + void setText(const QString& txt) { + m_txt = txt; + } + const QString& text() const { + return m_txt; + } + bool isSelectable() const { + return false; + } + bool canHaveFocus() const { + return false; + } + int numRows() const { + return 1; + } - CashFlowDirection sortType() const override; - int sortSamePostDate() const override; + virtual const char* className() { + return "GroupMarker"; + } -private: - Q_DECLARE_PRIVATE(StatementGroupMarker) + bool isErroneous() const { + return m_erroneous; + } + + void paintRegisterCell(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index); + void paintFormCell(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index); + + int rowHeightHint() const; + + bool matches(const RegisterFilter&) const { + return true; + } + virtual int sortSamePostDate() const { + return 0; + } + + void setErroneous(bool condition = true) { + m_erroneous = condition; + } + +protected: + QString m_txt; + bool m_showDate; + + static QPixmap* m_bg; + static int m_bgRefCnt; + + bool m_erroneous; }; -class SimpleDateGroupMarker : public FancyDateGroupMarker +class FancyDateGroupMarker : public GroupMarker { - Q_DISABLE_COPY(SimpleDateGroupMarker) +public: + FancyDateGroupMarker(Register* parent, const QDate& date, const QString& txt); + + QDate sortPostDate() const override { + return m_date; + } + QDate sortEntryDate() const override { + return m_date; + } + virtual const char* className() override { + return "FancyDateGroupMarker"; + } +private: + QDate m_date; +}; +class StatementGroupMarker : public FancyDateGroupMarker +{ public: - explicit SimpleDateGroupMarker(Register* getParent, const QDate& date, const QString& txt); - ~SimpleDateGroupMarker() override; + StatementGroupMarker(Register* parent, CashFlowDirection dir, const QDate& date, const QString& txt); + CashFlowDirection sortType() const { + return m_dir; + } + virtual int sortSamePostDate() const { + return 3; + } +private: + CashFlowDirection m_dir; +}; - void paintRegisterCell(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) override; +class SimpleDateGroupMarker : public FancyDateGroupMarker +{ +public: + SimpleDateGroupMarker(Register* parent, const QDate& date, const QString& txt); + void paintRegisterCell(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index); int rowHeightHint() const; - const char* className() override; + virtual const char* className() { + return "SimpleDateGroupMarker"; + } }; -class TypeGroupMarkerPrivate; class TypeGroupMarker : public GroupMarker { - Q_DISABLE_COPY(TypeGroupMarker) - public: - explicit TypeGroupMarker(Register* getParent, CashFlowDirection dir, eMyMoney::Account accType); - ~TypeGroupMarker() override; - - CashFlowDirection sortType() const override; - + TypeGroupMarker(Register* parent, CashFlowDirection dir, eMyMoney::Account accType); + CashFlowDirection sortType() const { + return m_dir; + } private: - Q_DECLARE_PRIVATE(TypeGroupMarker) + CashFlowDirection m_dir; }; class FiscalYearGroupMarker : public FancyDateGroupMarker { - Q_DISABLE_COPY(FiscalYearGroupMarker) - public: - explicit FiscalYearGroupMarker(Register* getParent, const QDate& date, const QString& txt); - ~FiscalYearGroupMarker() override; - - const char* className() override; - int sortSamePostDate() const override; + FiscalYearGroupMarker(Register* parent, const QDate& date, const QString& txt); + virtual const char* className() { + return "FiscalYearGroupMarker"; + } + virtual int sortSamePostDate() const { + return 1; + } }; class PayeeGroupMarker : public GroupMarker { - Q_DISABLE_COPY(PayeeGroupMarker) - public: - explicit PayeeGroupMarker(Register* getParent, const QString& name); - ~PayeeGroupMarker() override; - - const QString& sortPayee() const override; + PayeeGroupMarker(Register* parent, const QString& name); + const QString& sortPayee() const { + return m_txt; + } }; class CategoryGroupMarker : public GroupMarker { - Q_DISABLE_COPY(CategoryGroupMarker) - public: - explicit CategoryGroupMarker(Register* getParent, const QString& category); - ~CategoryGroupMarker() override; + CategoryGroupMarker(Register* parent, const QString& category); + const QString& sortCategory() const { + return m_txt; + } + const QString sortSecurity() const { + return m_txt; + } - const QString& sortCategory() const override; - const QString sortSecurity() const override; - const char* className() override; + virtual const char* className() { + return "CategoryGroupMarker"; + } }; -class ReconcileGroupMarkerPrivate; class ReconcileGroupMarker : public GroupMarker { - Q_DISABLE_COPY(ReconcileGroupMarker) - public: - explicit ReconcileGroupMarker(Register* getParent, eMyMoney::Split::State state); - ~ReconcileGroupMarker() override; - - eMyMoney::Split::State sortReconcileState() const override; - + ReconcileGroupMarker(Register* parent, eMyMoney::Split::State state); + virtual eMyMoney::Split::State sortReconcileState() const { + return m_state; + } private: - Q_DECLARE_PRIVATE(ReconcileGroupMarker) + eMyMoney::Split::State m_state; }; class ItemPtrVector : public QVector { public: ItemPtrVector() {} void sort(); protected: /** * sorter's compare routine. Returns true if i1 < i2 */ static bool item_cmp(RegisterItem* i1, RegisterItem* i2); }; class RegisterItemDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit RegisterItemDelegate(Register *parent); ~RegisterItemDelegate(); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; private: Register *m_register; }; -class RegisterPrivate; class Register : public TransactionEditorContainer { Q_OBJECT - Q_DISABLE_COPY(Register) friend class Transaction; friend class StdTransaction; friend class InvestTransaction; public: - explicit Register(QWidget* parent = nullptr); + Register(QWidget *parent = 0); virtual ~Register(); /** * add the item @a p to the register */ void addItem(RegisterItem* p); /** * insert the item @a p into the register after item @a q */ void insertItemAfter(RegisterItem* p, RegisterItem* q); /** * remove the item @p from the register */ void removeItem(RegisterItem* p); /** * This method returns a list of pointers to all selected items * in the register * * @retval QList */ QList selectedItems() const; /** * Construct a list of all currently selected transactions in the register. * If the current item carrying the focus (see focusItem() ) is selected * it will be the first one contained in the list. * * @param list reference to QList receiving the SelectedTransaction()'s */ void selectedTransactions(SelectedTransactions& list) const; QString text(int row, int col) const; QWidget* createEditor(int row, int col, bool initFromCell) const; void setCellContentFromEditor(int row, int col); void endEdit(int row, int col, bool accept, bool replace); - RegisterItem* focusItem() const; - RegisterItem* anchorItem() const; + RegisterItem* focusItem() const { + return m_focusItem; + } + RegisterItem* anchorItem() const { + return m_selectAnchor; + } /** * set focus to specific item. * @return true if the item got focus */ bool setFocusItem(RegisterItem* focusItem); void setAnchorItem(RegisterItem* anchorItem); /** * Set focus to the first focussable item * @return true if a focussable item was found */ bool setFocusToTop(); /** * Select @a item and unselect all others if @a dontChangeSelections * is @a false. If m_buttonState differs from Qt::NoButton (method is * called as a result of a mouse button press), then the setting of * @a dontChangeSelections has no effect. */ void selectItem(RegisterItem* item, bool dontChangeSelections = false); /** * Clears all items in the register. All objects * added to the register will be deleted. */ void clear(); void updateRegister(bool forceUpdateRowHeight = false); /** * Assign all visible items an alternate background color */ void updateAlternate() const; /** * make sure, we only show a single marker in a row * through hiding unused ones */ void suppressAdjacentMarkers(); /** * Adjusts column @a col so that all data fits in width. */ void adjustColumn(int col); /** * Convenience method to setup the register to show the columns * based on the account type of @a account. If @a showAccountColumn * is @a true then the account column is shown independent of the * account type. If @a account does not have an @a id, all columns * will be hidden. */ void setupRegister(const MyMoneyAccount& account, bool showAccountColumn = false); /** * Show the columns contained in @a cols for @a account. @a account * can be left empty ( MyMoneyAccount() ) e.g. for the search dialog. */ void setupRegister(const MyMoneyAccount& account, const QList& cols); void setSortOrder(const QString& order); - const QList& sortOrder() const; - eWidgets::SortField primarySortKey() const; + const QList& sortOrder() const { + return m_sortOrder; + } + TransactionSortField primarySortKey() const; void sortItems(); /** * This member returns the last visible column that is used by the register * after it has been setup using setupRegister(). * * @return last actively used column (base 0) */ - Column lastCol() const; + Column lastCol() const { + return m_lastCol; + } RegisterItem* firstItem() const; RegisterItem* firstVisibleItem() const; RegisterItem* nextItem(RegisterItem*) const; RegisterItem* lastItem() const; RegisterItem* lastVisibleItem() const; RegisterItem* prevItem(RegisterItem*) const; RegisterItem* itemAtRow(int row) const; void resize(int col, bool force = false); - void forceUpdateLists(); + void forceUpdateLists() { + m_listsDirty = true; + } void ensureItemVisible(RegisterItem* item); - void arrangeEditWidgets(QMap& editWidgets, Transaction* t) override; - void removeEditWidgets(QMap& editWidgets) override; + void arrangeEditWidgets(QMap& editWidgets, Transaction* t); + void removeEditWidgets(QMap& editWidgets); void tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const; int rowHeightHint() const; void clearSelection(); /** * This method creates a specific transaction according to the * transaction passed in @a transaction. * * @param parent pointer to register where the created object should be added * @param transaction the transaction which should be used to create the object * @param split the split of the transaction which should be used to create the object * @param uniqueId an int that will be used to construct the id of the item * * @return pointer to created object (0 upon failure) */ static Transaction* transactionFactory(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - const MyMoneyAccount& account() const; + const MyMoneyAccount& account() const { + return m_account; + } /** * This method creates group marker items and adds them to the register */ void addGroupMarkers(); /** * This method removes all trailing group markers and in a second * run reduces all adjacent group markers to show only one. In that * case the last one will remain. */ void removeUnwantedGroupMarkers(); - void setLedgerLensForced(bool forced = true); - bool ledgerLens() const; + void setLedgerLensForced(bool forced = true) { + m_ledgerLensForced = forced; + } /** * Sets the selection mode to @a mode. Supported modes are QTable::Single and * QTable::Multi. QTable::Multi is the default when the object is created. */ - void setSelectionMode(SelectionMode mode); + void setSelectionMode(SelectionMode mode) { + m_selectionMode = mode; + } /** * This method sets a hint that the register instance will be used * with a transaction editor. This information is used while the column * sizes are being auto adjusted. If a transaction editor is used then * it's possible that it will need some extra space. */ - void setUsedWithEditor(bool value); + void setUsedWithEditor(bool value) { + m_usedWithEditor = value; + } DetailsColumnType getDetailsColumnType() const; void setDetailsColumnType(DetailsColumnType detailsColumnType); protected: - void mouseReleaseEvent(QMouseEvent *e) override; - void contextMenuEvent(QContextMenuEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e); + void contextMenuEvent(QContextMenuEvent *e); - void unselectItems(int from = -1, int to = -1); - void selectItems(int from, int to); + void unselectItems(int from = -1, int to = -1) { + doSelectItems(from, to, false); + } + void selectItems(int from, int to) { + doSelectItems(from, to, true); + } void doSelectItems(int from, int to, bool selected); int selectedItemsCount() const; - bool event(QEvent*) override; - void focusOutEvent(QFocusEvent*) override; - void focusInEvent(QFocusEvent*) override; - void keyPressEvent(QKeyEvent*) override; - void resizeEvent(QResizeEvent* ev) override; + bool event(QEvent*); + void focusOutEvent(QFocusEvent*); + void focusInEvent(QFocusEvent*); + void keyPressEvent(QKeyEvent*); + virtual void resizeEvent(QResizeEvent* ev); int rowToIndex(int row) const; void setupItemIndex(int rowCount); /** * This method determines the register item that is one page * further down or up in the ledger from the previous focus item. * The height to scroll is determined by visibleHeight() * * @param key Qt::Page_Up or Qt::Page_Down depending on the direction to scroll * @param modifiers state of Qt::ShiftModifier, Qt::ControlModifier, Qt::AltModifier and * Qt::MetaModifier. */ void scrollPage(int key, Qt::KeyboardModifiers modifiers); /** * This method determines the pointer to a RegisterItem * based on the item's @a id. If @a id is empty, this method * returns @a m_lastItem. * * @param id id of the item to be searched * @return pointer to RegisterItem or 0 if not found */ RegisterItem* itemById(const QString& id) const; /** * Override logic and use standard QFrame behaviour */ - bool focusNextPrevChild(bool next) override; + bool focusNextPrevChild(bool next); - bool eventFilter(QObject* o, QEvent* e) override; + bool eventFilter(QObject* o, QEvent* e); void handleItemChange(RegisterItem* old, bool shift, bool control); void selectRange(RegisterItem* from, RegisterItem* to, bool invert, bool includeFirst, bool clearSel); /** * Returns the minimum column width based on the data in the header and the transactions. */ int minimumColumnWidth(int col); protected slots: void resize(); void selectItem(int row, int col); void slotEnsureItemVisible(); void slotDoubleClicked(int row, int); signals: void transactionsSelected(const KMyMoneyRegister::SelectedTransactions& list); /** * This signal is emitted when the focus and selection changes to @p item. * * @param item pointer to transaction that received the focus and was selected */ void focusChanged(KMyMoneyRegister::Transaction* item); /** * This signal is emitted when the focus changes but the selection remains * the same. This usually happens when the focus is changed using the keyboard. */ void focusChanged(); /** * This signal is emitted when an @p item is about to be selected. The boolean * @p okToSelect is preset to @c true. If the @p item should not be selected * for whatever reason, the boolean @p okToSelect should be reset to @c false * by the connected slot. */ void aboutToSelectItem(KMyMoneyRegister::RegisterItem* item, bool& okToSelect); void editTransaction(); /** * This signal is sent out when the user clicks on the ReconcileStateColumn and * only a single transaction is selected. */ void reconcileStateColumnClicked(KMyMoneyRegister::Transaction* item); /** * This signal is sent out, if an item without a transaction id has been selected. */ void emptyItemSelected(); /** * This signal is sent out, if the user selects an item with the right mouse button */ void openContextMenu(); /** * This signal is sent out when a new item has been added to the register */ void itemAdded(RegisterItem* item); +protected: + ItemPtrVector m_items; + QVector m_itemIndex; + RegisterItem* m_selectAnchor; + RegisterItem* m_focusItem; + RegisterItem* m_ensureVisibleItem; + RegisterItem* m_firstItem; + RegisterItem* m_lastItem; + RegisterItem* m_firstErroneous; + RegisterItem* m_lastErroneous; + + int m_markErroneousTransactions; + int m_rowHeightHint; + + MyMoneyAccount m_account; + + bool m_ledgerLensForced; + SelectionMode m_selectionMode; + private: - RegisterPrivate * const d_ptr; - Q_DECLARE_PRIVATE(Register) + bool m_needResize; + bool m_listsDirty; + bool m_ignoreNextButtonRelease; + bool m_needInitialColumnResize; + bool m_usedWithEditor; + Qt::MouseButtons m_mouseButton; + Qt::KeyboardModifiers m_modifiers; + Column m_lastCol; + QList m_sortOrder; + QRect m_lastRepaintRect; + DetailsColumnType m_detailsColumnType; }; } // namespace #endif diff --git a/kmymoney/widgets/registeritem.cpp b/kmymoney/widgets/registeritem.cpp index 9b7014ca2..553cb616b 100644 --- a/kmymoney/widgets/registeritem.cpp +++ b/kmymoney/widgets/registeritem.cpp @@ -1,315 +1,128 @@ /*************************************************************************** registeritem.cpp - description ------------------- begin : Tue Jun 13 2006 copyright : (C) 2000-2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "registeritem.h" -#include "registeritem_p.h" // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "register.h" #include "kmymoneyglobalsettings.h" -#include "mymoneyobject.h" -#include "mymoneymoney.h" -#include "mymoneyenums.h" using namespace KMyMoneyRegister; -QDate RegisterItemPrivate::nullDate; -QString RegisterItemPrivate::nullString; -MyMoneyMoney RegisterItemPrivate::nullValue; +QDate RegisterItem::nullDate; +QString RegisterItem::nullString; +MyMoneyMoney RegisterItem::nullValue; RegisterItem::RegisterItem() : - d_ptr(new RegisterItemPrivate) + m_parent(0), + m_prev(0), + m_next(0), + m_alternate(false), + m_needResize(false), + m_visible(false) { - Q_D(RegisterItem); - d->m_parent = nullptr; + init(); } RegisterItem::RegisterItem(Register* parent) : - d_ptr(new RegisterItemPrivate) -{ - Q_D(RegisterItem); - d->m_parent = parent; + m_parent(parent), + m_prev(0), + m_next(0), + m_alternate(false), + m_needResize(false), + m_visible(false) +{ + init(); parent->addItem(this); } -RegisterItem::RegisterItem(RegisterItemPrivate &dd, Register* parent) : - d_ptr(&dd) -{ - Q_D(RegisterItem); - d->m_parent = parent; - parent->addItem(this); -} - -RegisterItem::RegisterItem(RegisterItemPrivate &dd) : - d_ptr(&dd) -{ -} - -RegisterItem::RegisterItem(const RegisterItem& other) : - d_ptr(new RegisterItemPrivate(*other.d_func())) +void RegisterItem::init() { + m_startRow = 0; + m_rowsRegister = 1; + m_rowsForm = 1; + m_visible = true; } RegisterItem::~RegisterItem() { - Q_D(RegisterItem); - d->m_parent->removeItem(this); - delete d; -} - -bool RegisterItem::isSelected() const -{ - return false; -} - -void RegisterItem::setSelected(bool /* selected*/) -{ -} - -bool RegisterItem::hasFocus() const { - return false; -} - -bool RegisterItem::hasEditorOpen() const -{ - return false; -} - -void RegisterItem::setFocus(bool /*focus*/, bool updateLens) -{ - Q_UNUSED(updateLens); -} - -QDate RegisterItem::sortPostDate() const -{ - Q_D(const RegisterItem); - return d->nullDate; -} - -QDate RegisterItem::sortEntryDate() const -{ - Q_D(const RegisterItem); - return d->nullDate; -} - -const QString& RegisterItem::sortPayee() const -{ - Q_D(const RegisterItem); - return d->nullString; -} - -MyMoneyMoney RegisterItem::sortValue() const -{ - Q_D(const RegisterItem); - return d->nullValue; -} - -QString RegisterItem::sortNumber() const -{ - Q_D(const RegisterItem); - return d->nullString; -} - -const QString& RegisterItem::sortEntryOrder() const -{ - Q_D(const RegisterItem); - return d->nullString; -} - -CashFlowDirection RegisterItem::sortType() const -{ - return Deposit; -} - -const QString& RegisterItem::sortCategory() const -{ - Q_D(const RegisterItem); - return d->nullString; -} - -eMyMoney::Split::State RegisterItem::sortReconcileState() const -{ - return eMyMoney::Split::State::MaxReconcileState; -} - -const QString RegisterItem::sortSecurity() const -{ - Q_D(const RegisterItem); - return d->nullString; -} - -void RegisterItem::setStartRow(int row) -{ - Q_D(RegisterItem); - d->m_startRow = row; -} - -int RegisterItem::startRow() const -{ - Q_D(const RegisterItem); - return d->m_startRow; -} - -const QString& RegisterItem::id() const -{ - return MyMoneyObject::emptyId(); + m_parent->removeItem(this); } void RegisterItem::setParent(Register* parent) { - Q_D(RegisterItem); - d->m_parent = parent; -} - -Register* RegisterItem::getParent() const -{ - Q_D(const RegisterItem); - return d->m_parent; -} - -void RegisterItem::setNeedResize() -{ - Q_D(RegisterItem); - d->m_needResize = true; -} - -bool RegisterItem::isVisible() const -{ - Q_D(const RegisterItem); - return d->m_visible; + m_parent = parent; } void RegisterItem::setNumRowsRegister(int rows) { - Q_D(RegisterItem); - if (rows != d->m_rowsRegister) { - d->m_rowsRegister = rows; - if (d->m_parent) - d->m_parent->forceUpdateLists(); + if (rows != m_rowsRegister) { + m_rowsRegister = rows; + if (m_parent) + m_parent->forceUpdateLists(); } } -void RegisterItem::setNumRowsForm(int rows) -{ - Q_D(RegisterItem); - d->m_rowsForm = rows; -} - -int RegisterItem::numRowsRegister() const -{ - Q_D(const RegisterItem); - return d->m_rowsRegister; -} - -int RegisterItem::numRowsForm() const -{ - Q_D(const RegisterItem); - return d->m_rowsForm; -} - -int RegisterItem::numColsForm() const -{ - return 1; -} - -void RegisterItem::setAlternate(bool alternate) -{ - Q_D(RegisterItem); - d->m_alternate = alternate; -} - bool RegisterItem::markVisible(bool visible) { - Q_D(RegisterItem); - if (d->m_visible == visible) + if (m_visible == visible) return false; - d->m_visible = visible; + m_visible = visible; return true; } -void RegisterItem::setNextItem(RegisterItem* p) -{ - Q_D(RegisterItem); - d->m_next = p; -} - -void RegisterItem::setPrevItem(RegisterItem* p) -{ - Q_D(RegisterItem); - d->m_prev = p; -} - -RegisterItem* RegisterItem::nextItem() const -{ - Q_D(const RegisterItem); - return d->m_next; -} - -RegisterItem* RegisterItem::prevItem() const -{ - Q_D(const RegisterItem); - return d->m_prev; -} - -bool RegisterItem::maybeTip(const QPoint& /* relpos */, int /* row */, int /* col */, QRect& /* r */, QString& /* msg */) -{ - return false; -} - void RegisterItem::setVisible(bool visible) { - Q_D(RegisterItem); - if (markVisible(visible) && d->m_parent) { - int numRows = d->m_parent->rowCount(); + if (markVisible(visible) && m_parent) { + int numRows = m_parent->rowCount(); if (visible) { for (int i = startRow(); i < startRow() + numRowsRegister(); ++i) { if (numRows > i) { - d->m_parent->showRow(i); - d->m_parent->setRowHeight(i, rowHeightHint()); + m_parent->showRow(i); + m_parent->setRowHeight(i, rowHeightHint()); } } } else { for (int i = startRow(); i < startRow() + numRowsRegister(); ++i) { if (numRows > i) { - d->m_parent->hideRow(i); + m_parent->hideRow(i); } } } } } int RegisterItem::rowHeightHint() const { - Q_D(const RegisterItem); - if (!d->m_visible) + if (!m_visible) return 0; - if (d->m_parent) { - return d->m_parent->rowHeightHint(); + if (m_parent) { + return m_parent->rowHeightHint(); } QFontMetrics fm(KMyMoneyGlobalSettings::listCellFont()); return fm.lineSpacing() + 6; } diff --git a/kmymoney/widgets/registeritem.h b/kmymoney/widgets/registeritem.h index cf8f3621c..adea42ccc 100644 --- a/kmymoney/widgets/registeritem.h +++ b/kmymoney/widgets/registeritem.h @@ -1,255 +1,306 @@ /*************************************************************************** registeritem.h - description ------------------- begin : Tue Jun 13 2006 copyright : (C) 2000-2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef REGISTERITEM_H #define REGISTERITEM_H // ---------------------------------------------------------------------------- // QT Includes -#include #include +#include +#include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -class QDate; -class QPainter; -class QPoint; -class QRect; -class QStyleOptionViewItem; -class QModelIndex; +#include "mymoneyobject.h" +#include "mymoneymoney.h" +#include "mymoneyenums.h" class MyMoneyMoney; - -namespace eMyMoney { namespace Split { enum class State; } } - namespace KMyMoneyRegister { enum CashFlowDirection : int { Deposit = 0, //< transaction is deposit Payment, //< transaction is payment Unknown //< transaction cashflow is unknown }; enum Action : int { ActionNone = -1, ActionCheck = 0, /* these should be values which qt 3.3 never uses for QTab: * qt starts upwards from 0 */ ActionDeposit = 12201, ActionTransfer = 12202, ActionWithdrawal = 12203, ActionAtm, // insert new values above this line MaxAction }; /** * Used to filter items from the register. */ struct RegisterFilter { enum ItemState { Any, Imported, Matched, Erroneous, NotMarked, NotReconciled, Cleared }; RegisterFilter(const QString &t, ItemState s) : state(s), text(t) { } ItemState state; QString text; }; class Register; /** * @author Thomas Baumgart */ -class RegisterItemPrivate; class RegisterItem { public: RegisterItem(); - explicit RegisterItem(Register* getParent); - RegisterItem(const RegisterItem & other); - RegisterItem(RegisterItem && other); - friend void swap(RegisterItem& first, RegisterItem& second); - + RegisterItem(Register* parent); virtual ~RegisterItem(); virtual const char* className() = 0; virtual bool isSelectable() const = 0; - virtual bool isSelected() const; - virtual void setSelected(bool /* selected*/); + virtual bool isSelected() const { + return false; + } + virtual void setSelected(bool /* selected*/) {} virtual bool canHaveFocus() const = 0; - virtual bool hasFocus() const; - virtual bool hasEditorOpen() const; + virtual bool hasFocus() const { + return false; + } + virtual bool hasEditorOpen() const { + return false; + } - virtual void setFocus(bool /*focus*/, bool updateLens = true); + virtual void setFocus(bool /*focus*/, bool updateLens = true) { + Q_UNUSED(updateLens); + } virtual bool isErroneous() const = 0; // helper functions used for sorting - virtual QDate sortPostDate() const; + virtual QDate sortPostDate() const { + return nullDate; + } virtual int sortSamePostDate() const = 0; - virtual QDate sortEntryDate() const; - virtual const QString& sortPayee() const; - virtual MyMoneyMoney sortValue() const; - virtual QString sortNumber() const; - virtual const QString& sortEntryOrder() const; - virtual CashFlowDirection sortType() const; - virtual const QString& sortCategory() const; - virtual eMyMoney::Split::State sortReconcileState() const; - virtual const QString sortSecurity() const; + virtual QDate sortEntryDate() const { + return nullDate; + } + virtual const QString& sortPayee() const { + return nullString; + } + virtual MyMoneyMoney sortValue() const { + return nullValue; + } + virtual QString sortNumber() const { + return nullString; + } + virtual const QString& sortEntryOrder() const { + return nullString; + } + virtual CashFlowDirection sortType() const { + return Deposit; + } + virtual const QString& sortCategory() const { + return nullString; + } + virtual eMyMoney::Split::State sortReconcileState() const { + return eMyMoney::Split::State::MaxReconcileState; + } + virtual const QString sortSecurity() const { + return nullString; + } /** * This method sets the row offset of the item in the register * to row. * * @param row row offset * * @note The row offset is based on QTable rows, not register * items. */ - virtual void setStartRow(int row); - int startRow() const; + virtual void setStartRow(int row) { + m_startRow = row; + } + int startRow() const { + return m_startRow; + } virtual int rowHeightHint() const; /** * This method modifies the number of rows required to display this item * in a Register. * It calls Register::forceUpdateLists() when the number differs. */ virtual void setNumRowsRegister(int rows); /** * This method modifies the number of rows required to display this item * in a Form. */ - virtual void setNumRowsForm(int rows); + virtual void setNumRowsForm(int rows) { + m_rowsForm = rows; + } /** * This method returns the number of rows required to display this item * in a Register */ - virtual int numRowsRegister() const; + virtual int numRowsRegister() const { + return m_rowsRegister; + } /** * This method returns the number of rows required to display this item * in a Form */ - virtual int numRowsForm() const; - virtual int numColsForm() const; + virtual int numRowsForm() const { + return m_rowsForm; + } + virtual int numColsForm() const { + return 1; + } /** * This method sets up the register item to be shown in normal (@p alternate = @p false) * or alternate (@p alternate = @p true) background. * * @param alternate selects normal or alternate background */ - virtual void setAlternate(bool alternate); + virtual void setAlternate(bool alternate) { + m_alternate = alternate; + } virtual void paintRegisterCell(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) = 0; virtual void paintFormCell(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) = 0; - virtual const QString& id() const; + virtual const QString& id() const { + return MyMoneyObject::emptyId(); + } /** * Sets the parent of this item to be the register @p parent * * @param parent pointer to register */ - void setParent(Register* getParent); + void setParent(Register* parent); /** * This member returns a pointer to the parent object * * @retval pointer to Register */ - Register* getParent() const; + Register* parent() const { + return m_parent; + } - void setNeedResize(); + void setNeedResize() { + m_needResize = true; + } - bool isVisible() const; + bool isVisible() const { + return m_visible; + } /** * Marks the item visible depending on @a visible and * updates the underlying register object */ virtual void setVisible(bool visible); /** * Marks the item visible depending on @a visible but * does not update the underlying register object. Returns * true, if visibility has changed. */ virtual bool markVisible(bool visible); - void setNextItem(RegisterItem* p); - void setPrevItem(RegisterItem* p); - RegisterItem* nextItem() const; - RegisterItem* prevItem() const; + void setNextItem(RegisterItem* p) { + m_next = p; + } + void setPrevItem(RegisterItem* p) { + m_prev = p; + } + RegisterItem* nextItem() const { + return m_next; + } + RegisterItem* prevItem() const { + return m_prev; + } virtual bool matches(const RegisterFilter&) const = 0; /** * Checks if the mouse hovered over an area that has a tooltip associated with it. * The mouse position is given in relative coordinates to the @a startRow and the * @a row and @a col of the item are also passed as relative values. * * If a tooltip shall be shown, this method presets the rectangle @a r with the * area in register coordinates and @a msg with the string that will be passed * to QToolTip::tip. @a true is returned in this case. * * If no tooltip is available, @a false will be returned. */ - virtual bool maybeTip(const QPoint& /* relpos */, int /* row */, int /* col */, QRect& /* r */, QString& /* msg */); + virtual bool maybeTip(const QPoint& /* relpos */, int /* row */, int /* col */, QRect& /* r */, QString& /* msg */) { + return false; + } + +protected: + /// This method serves as helper for all constructors + void init(); protected: - RegisterItemPrivate *d_ptr; - RegisterItem(RegisterItemPrivate &dd, Register *parent); - RegisterItem(RegisterItemPrivate &dd); //for copy-constructor of derived class + Register* m_parent; + RegisterItem* m_prev; + RegisterItem* m_next; + int m_startRow; + int m_rowsRegister; + int m_rowsForm; + bool m_alternate; + bool m_needResize; + bool m_visible; private: - Q_DECLARE_PRIVATE(RegisterItem) + static QDate nullDate; + static QString nullString; + static MyMoneyMoney nullValue; }; -inline void swap(RegisterItem& first, RegisterItem& second) // krazy:exclude=inline -{ - using std::swap; - swap(first.d_ptr, second.d_ptr); -} - -inline RegisterItem::RegisterItem(RegisterItem && other) : RegisterItem() // krazy:exclude=inline -{ - swap(*this, other); -} - } // namespace #endif diff --git a/kmymoney/widgets/registeritem_p.h b/kmymoney/widgets/registeritem_p.h deleted file mode 100644 index 816e19102..000000000 --- a/kmymoney/widgets/registeritem_p.h +++ /dev/null @@ -1,78 +0,0 @@ -/*************************************************************************** - registeritem_p.h - description - ------------------- - begin : Tue Jun 13 2006 - copyright : (C) 2000-2006 by Thomas Baumgart - email : Thomas Baumgart - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef REGISTERITEM_P_H -#define REGISTERITEM_P_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -class MyMoneyMoney; -class QDate; -class QString; - -namespace KMyMoneyRegister { class Register; } -namespace KMyMoneyRegister { class RegisterItem; } - -using namespace KMyMoneyRegister; - -namespace KMyMoneyRegister -{ - class RegisterItemPrivate - { - public: - RegisterItemPrivate() : - m_parent(nullptr), - m_prev(nullptr), - m_next(nullptr), - m_startRow(0), - m_rowsRegister(1), - m_rowsForm(1), - m_alternate(false), - m_needResize(false), - m_visible(true) - { - } - - virtual ~RegisterItemPrivate() - { - } - - Register* m_parent; - RegisterItem* m_prev; - RegisterItem* m_next; - int m_startRow; - int m_rowsRegister; - int m_rowsForm; - bool m_alternate; - bool m_needResize; - bool m_visible; - - static QDate nullDate; - static QString nullString; - static MyMoneyMoney nullValue; - - }; -} - -#endif diff --git a/kmymoney/widgets/registersearchline.h b/kmymoney/widgets/registersearchline.h index 370b71ce5..4a434bebb 100644 --- a/kmymoney/widgets/registersearchline.h +++ b/kmymoney/widgets/registersearchline.h @@ -1,140 +1,140 @@ /*************************************************************************** registersearchline.h ------------------- begin : Sun Jan 14 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef REGISTERSEARCHLINE_H #define REGISTERSEARCHLINE_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes namespace KMyMoneyRegister { class Register; } namespace KMyMoneyRegister { class RegisterItem; } namespace KMyMoneyRegister { /** * This class makes it easy to add a search line for filtering the items * in a register based on simple text. Inspired by the idea of the kdelibs * class KListViewSearchLine. * * @author Thomas Baumgart */ class RegisterSearchLine : public KLineEdit { Q_OBJECT public: /** * Constructs a RegisterSearchLine with @a reg being the register to be * filtered. * * If @a reg is null then the widget will be disabled until a register * is set with setRegister(). */ - explicit RegisterSearchLine(QWidget* parent = nullptr, Register* reg = 0); + explicit RegisterSearchLine(QWidget* parent = 0, Register* reg = 0); /** * Destroys the object */ ~RegisterSearchLine(); /** * Sets the KMyMoneyRegister that is filtered by this search line. * If @a reg is null then the widget will be disabled. * * @see KMyMoneyRegister() */ void setRegister(Register* reg); public slots: virtual void updateSearch(const QString& s = QString()); virtual void reset(); protected slots: void queueSearch(const QString& search); void activateSearch(); void slotStatusChanged(int); private slots: void itemAdded(RegisterItem* item) const; void registerDestroyed(); private: void init(Register* reg); private: class RegisterSearchLinePrivate; RegisterSearchLinePrivate* const d; }; /** * Creates a widget containing a RegisterSearchLine, a label with the text * "Search" and a button to clear the search. Modelled after KListViewSearchLineWidget. * * @author Thomas Baumgart */ class RegisterSearchLineWidget : public QWidget { Q_OBJECT public: /** * Creates a RegisterSearchLineWidget for @a reg with @a parent as the * parent and with @a name. */ - explicit RegisterSearchLineWidget(Register* reg = 0, QWidget* parent = nullptr); + explicit RegisterSearchLineWidget(Register* reg = 0, QWidget* parent = 0); /** * Destroys the object */ ~RegisterSearchLineWidget(); /** * Returns a pointer to the searchline */ RegisterSearchLine* searchLine() const; /** * Creates the search line. This can be useful to reimplement in cases where * a RegisterSearchLine subclass is used. */ virtual RegisterSearchLine* createSearchLine(Register* reg); protected slots: /** * Creates the widgets inside of the widget. This is called from the * constructor via a single shot timer so that it it guaranteed to run * after construction is complete. This makes it suitable for overriding in * subclasses. */ virtual void createWidgets(); private: class RegisterSearchLineWidgetPrivate; RegisterSearchLineWidgetPrivate* const d; }; } // namespace #endif diff --git a/kmymoney/widgets/reporttabchart.ui b/kmymoney/widgets/reporttabchart.ui index 77c4392ab..0539510d9 100644 --- a/kmymoney/widgets/reporttabchart.ui +++ b/kmymoney/widgets/reporttabchart.ui @@ -1,199 +1,199 @@ ReportTabChart 0 0 600 234 Chart Tab <p>On this tab, you configure the chart drawn for this report.</p> 0 0 <p>Select what form you would like the chart to be drawn as.</p> Chart Type false Qt::Horizontal QSizePolicy::Expanding 121 20 <p>Select this option to draw the numeric values for data points next to their plot location.</p> Draw values on chart <p>Select this option to cause the report to be shown as a chart when you first open the report. Otherwise, it will come up as a text report.</p> Show as chart by default SV grid lines <p>Select this option to show horizontal and vertical grid lines on the chart.</p> CH grid lines Logarithmic vertical axis 0 0 <p>Select what width should be used to draw the line on the chart</p> Line width false 1 10 Qt::Horizontal QSizePolicy::Expanding 121 20 Qt::Vertical QSizePolicy::Expanding 20 20 KMyMoneyGeneralCombo QWidget -
kmymoneygeneralcombo.h
+
kmymoneymvccombo.h
1
m_checkCHGridLines m_checkSVGridLines m_checkValues m_checkShowChart m_logYaxis m_lineWidth
diff --git a/kmymoney/widgets/reporttabrowcolpivot.ui b/kmymoney/widgets/reporttabrowcolpivot.ui index e1f22ba38..9cf6e4fae 100644 --- a/kmymoney/widgets/reporttabrowcolpivot.ui +++ b/kmymoney/widgets/reporttabrowcolpivot.ui @@ -1,253 +1,253 @@ ReportTabRowColPivot 0 0 600 294 Rows/Columns Tab <p>On this tab, you configure how you would like the rows and columns to be selected and organized.</p> Detail false <p>Choose what kind of accounts to display as the rows of this report.</p> Income & Expenses Assets & Liabilities Rows false false Show totals column Average days false 1 366 <p>Choose what kind of accounts to display as the rows of this report.</p> All Top-Level Groups Totals false QFrame::NoFrame QFrame::Raised 0 0 0 0 Budget false Show totals row Qt::Horizontal QSizePolicy::Expanding 94 21 Include scheduled transactions Include transfers Include unused accounts/categories Qt::Vertical QSizePolicy::Expanding 20 40 KComboBox QComboBox
kcombobox.h
KMyMoneyGeneralCombo QWidget -
kmymoneygeneralcombo.h
+
kmymoneymvccombo.h
1
m_checkTotalColumn m_checkTotalRow m_comboRows m_comboDetail m_movingAverageDays m_checkScheduled m_checkTransfers m_checkUnused
diff --git a/kmymoney/widgets/scheduledtransaction.cpp b/kmymoney/widgets/scheduledtransaction.cpp index 457cf6caf..fe711c57e 100644 --- a/kmymoney/widgets/scheduledtransaction.cpp +++ b/kmymoney/widgets/scheduledtransaction.cpp @@ -1,79 +1,49 @@ /*************************************************************************** scheduledtransaction.cpp ------------------- begin : Tue Aug 19 2008 copyright : (C) 2008 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "scheduledtransaction.h" // ---------------------------------------------------------------------------- // QT Includes -#include - // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyglobalsettings.h" +#include "transaction.h" using namespace KMyMoneyRegister; using namespace KMyMoneyTransactionForm; StdTransactionScheduled::StdTransactionScheduled(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : - StdTransaction(parent, transaction, split, uniqueId) + StdTransaction(parent, transaction, split, uniqueId) { // setup initial size setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); } -StdTransactionScheduled::~StdTransactionScheduled() -{ -} - -const char* StdTransactionScheduled::className() -{ - return "StdTransactionScheduled"; -} - bool StdTransactionScheduled::paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) { - auto rc = Transaction::paintRegisterCellSetup(painter, option, index); + bool rc = Transaction::paintRegisterCellSetup(painter, option, index); option.palette.setCurrentColorGroup(QPalette::Disabled); return rc; } -bool StdTransactionScheduled::isSelectable() const -{ - return true; -} - -bool StdTransactionScheduled::canHaveFocus() const -{ - return true; -} - -bool StdTransactionScheduled::isScheduled() const -{ - return true; -} - -int StdTransactionScheduled::sortSamePostDate() const -{ - return 4; -} - diff --git a/kmymoney/widgets/scheduledtransaction.h b/kmymoney/widgets/scheduledtransaction.h index 07c0e75c7..ec9dfb254 100644 --- a/kmymoney/widgets/scheduledtransaction.h +++ b/kmymoney/widgets/scheduledtransaction.h @@ -1,57 +1,73 @@ /*************************************************************************** scheduledtransaction.h ------------------- begin : Tue Aug 19 2008 copyright : (C) 2008 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef SCHEDULEDTRANSACTION_H #define SCHEDULEDTRANSACTION_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "stdtransaction.h" +#include "transaction.h" + +namespace KMyMoneyTransactionForm +{ +class TransactionForm; +} // namespace namespace KMyMoneyRegister { - class StdTransactionScheduled : public StdTransaction - { - public: - StdTransactionScheduled(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - ~StdTransactionScheduled() override; +class StdTransactionScheduled : public StdTransaction +{ +public: + StdTransactionScheduled(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~StdTransactionScheduled() {} + + virtual const char* className() { + return "StdTransactionScheduled"; + } - const char* className(); + virtual bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index); - bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) override; + bool isSelectable() const { + return true; + } + bool canHaveFocus() const { + return true; + } + virtual bool isScheduled() const { + return true; + } - bool isSelectable() const override; - bool canHaveFocus() const override; - bool isScheduled() const override; - int sortSamePostDate() const override; + virtual int sortSamePostDate() const { + return 4; + } - // virtual void paintRegisterGrid(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& cg) const; +// virtual void paintRegisterGrid(QPainter* painter, int row, int col, const QRect& r, const QColorGroup& cg) const; - // void registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter = 0); - }; +// void registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter = 0); +}; } // namespace #endif diff --git a/kmymoney/widgets/selectedtransaction.cpp b/kmymoney/widgets/selectedtransaction.cpp index fae1976ae..f436500fd 100644 --- a/kmymoney/widgets/selectedtransaction.cpp +++ b/kmymoney/widgets/selectedtransaction.cpp @@ -1,128 +1,85 @@ /*************************************************************************** selectedtransaction.cpp - description ------------------- begin : Fri Jun 2008 copyright : (C) 2000-2008 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "selectedtransaction.h" // ---------------------------------------------------------------------------- // QT Includes +#include + // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "mymoneyfile.h" -#include "mymoneyaccount.h" +#include "register.h" #include "mymoneysplit.h" -#include "mymoneytransaction.h" -#include "mymoneyexception.h" - -using namespace KMyMoneyRegister; +#include "mymoneyfile.h" namespace KMyMoneyRegister { - class SelectedTransactionPrivate - { - public: - MyMoneyTransaction m_transaction; - MyMoneySplit m_split; - QString m_scheduleId; - }; -} - -SelectedTransaction::SelectedTransaction() : - d_ptr(new SelectedTransactionPrivate) -{ -} - -SelectedTransaction::SelectedTransaction(const MyMoneyTransaction& t, const MyMoneySplit& s, const QString& scheduleId = QString()) : - d_ptr(new SelectedTransactionPrivate) -{ - Q_D(SelectedTransaction); - d->m_transaction = t; - d->m_split = s; - d->m_scheduleId = scheduleId; -} - -SelectedTransaction::SelectedTransaction(const SelectedTransaction& other) : - d_ptr(new SelectedTransactionPrivate(*other.d_func())) -{ -} - -SelectedTransaction::~SelectedTransaction() -{ - Q_D(SelectedTransaction); - delete d; -} - -MyMoneyTransaction& SelectedTransaction::transaction() -{ - Q_D(SelectedTransaction); - return d->m_transaction; -} - -const MyMoneyTransaction& SelectedTransaction::transaction() const -{ - Q_D(const SelectedTransaction); - return d->m_transaction; -} - -MyMoneySplit& SelectedTransaction::split() -{ - Q_D(SelectedTransaction); - return d->m_split; -} - -const MyMoneySplit& SelectedTransaction::split() const -{ - Q_D(const SelectedTransaction); - return d->m_split; -} - -bool SelectedTransaction::isScheduled() const -{ - Q_D(const SelectedTransaction); - return !d->m_scheduleId.isEmpty(); -} - -const QString& SelectedTransaction::scheduleId() const -{ - Q_D(const SelectedTransaction); - return d->m_scheduleId; -} int SelectedTransaction::warnLevel() const { auto warnLevel = 0; foreach (const auto split, transaction().splits()) { try { auto acc = MyMoneyFile::instance()->account(split.accountId()); if (acc.isClosed()) warnLevel = 3; else if (split.reconcileFlag() == eMyMoney::Split::State::Frozen) warnLevel = 2; else if (split.reconcileFlag() == eMyMoney::Split::State::Reconciled && warnLevel < 1) warnLevel = 1; } catch (const MyMoneyException &) { //qDebug("Exception in SelectedTransaction::warnLevel(): %s", qPrintable(e.what())); warnLevel = 0; } } return warnLevel; } +SelectedTransactions::SelectedTransactions(const Register* r) +{ + r->selectedTransactions(*this); +} + +int SelectedTransactions::warnLevel() const +{ + int warnLevel = 0; + SelectedTransactions::const_iterator it_t; + for (it_t = begin(); warnLevel < 3 && it_t != end(); ++it_t) { + int thisLevel = (*it_t).warnLevel(); + if (thisLevel > warnLevel) + warnLevel = thisLevel; + } + return warnLevel; +} + +bool SelectedTransactions::canModify() const +{ + return warnLevel() < 2; +} + +bool SelectedTransactions::canDuplicate() const +{ + return warnLevel() < 3; +} + +} // namespace diff --git a/kmymoney/widgets/selectedtransaction.h b/kmymoney/widgets/selectedtransaction.h index 2567ce2e0..332da4680 100644 --- a/kmymoney/widgets/selectedtransaction.h +++ b/kmymoney/widgets/selectedtransaction.h @@ -1,93 +1,101 @@ /*************************************************************************** selectedtransaction.h - description ------------------- begin : Tue Jun 13 2006 copyright : (C) 2000-2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef SELECTEDTRANSACTION_H #define SELECTEDTRANSACTION_H // ---------------------------------------------------------------------------- // QT Includes -#include +#include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -class QString; -class MyMoneySplit; -class MyMoneyTransaction; +#include "mymoneytransaction.h" +#include "mymoneysplit.h" namespace KMyMoneyRegister { - class SelectedTransactionPrivate; - class SelectedTransaction - { - public: - SelectedTransaction(); - SelectedTransaction(const MyMoneyTransaction& t, const MyMoneySplit& s, const QString& scheduleId); - SelectedTransaction(const SelectedTransaction & other); - SelectedTransaction(SelectedTransaction && other); - SelectedTransaction & operator=(SelectedTransaction other); - friend void swap(SelectedTransaction& first, SelectedTransaction& second); - ~SelectedTransaction(); - - MyMoneyTransaction& transaction(); - const MyMoneyTransaction& transaction() const; - - MyMoneySplit& split(); - const MyMoneySplit& split() const; - - bool isScheduled() const; - const QString& scheduleId() const; - - /** + +class SelectedTransaction +{ +public: + SelectedTransaction() {} + SelectedTransaction(const MyMoneyTransaction& t, const MyMoneySplit& s, const QString& scheduleId = QString()) : + m_transaction(t), m_split(s), m_scheduleId(scheduleId) {} + + MyMoneyTransaction& transaction() { + return m_transaction; + } + const MyMoneyTransaction& transaction() const { + return m_transaction; + } + MyMoneySplit& split() { + return m_split; + } + const MyMoneySplit& split() const { + return m_split; + } + + bool isScheduled() const { + return !m_scheduleId.isEmpty(); + } + const QString& scheduleId() const { + return m_scheduleId; + } + + /** * checks the transaction for specific reasons which would * speak against editing/modifying it. * @retval 0 no sweat, user can modify * @retval 1 at least one split has been reconciled already * @retval 2 some transactions cannot be changed anymore - parts of them are frozen * @retval 3 some transactions cannot be changed anymore - they touch closed accounts */ - int warnLevel() const; + int warnLevel() const; - private: - SelectedTransactionPrivate* d_ptr; - Q_DECLARE_PRIVATE(SelectedTransaction) - }; +private: + MyMoneyTransaction m_transaction; + MyMoneySplit m_split; + QString m_scheduleId; +}; - inline void swap(SelectedTransaction& first, SelectedTransaction& second) // krazy:exclude=inline - { - using std::swap; - swap(first.d_ptr, second.d_ptr); - } +class Register; - inline SelectedTransaction::SelectedTransaction(SelectedTransaction && other) : SelectedTransaction() // krazy:exclude=inline - { - swap(*this, other); - } +class SelectedTransactions: public QList +{ +public: + SelectedTransactions() {} + SelectedTransactions(const Register* r); + + /** + * @return the highest warnLevel of all transactions in the list + */ + int warnLevel() const; + + bool canModify() const; + bool canDuplicate() const; +}; - inline SelectedTransaction & SelectedTransaction::operator=(SelectedTransaction other) // krazy:exclude=inline - { - swap(*this, other); - return *this; - } } // namespace #endif diff --git a/kmymoney/widgets/selectedtransactions.cpp b/kmymoney/widgets/selectedtransactions.cpp deleted file mode 100644 index 53cdb1e1b..000000000 --- a/kmymoney/widgets/selectedtransactions.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/*************************************************************************** - selectedtransaction.cpp - description - ------------------- - begin : Fri Jun 2008 - copyright : (C) 2000-2008 by Thomas Baumgart - email : Thomas Baumgart - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "selectedtransactions.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "register.h" - -using namespace KMyMoneyRegister; - -SelectedTransactions::SelectedTransactions(const Register* r) -{ - r->selectedTransactions(*this); -} - -int SelectedTransactions::warnLevel() const -{ - int warnLevel = 0; - SelectedTransactions::const_iterator it_t; - for (it_t = begin(); warnLevel < 3 && it_t != end(); ++it_t) { - int thisLevel = (*it_t).warnLevel(); - if (thisLevel > warnLevel) - warnLevel = thisLevel; - } - return warnLevel; -} - -bool SelectedTransactions::canModify() const -{ - return warnLevel() < 2; -} - -bool SelectedTransactions::canDuplicate() const -{ - return warnLevel() < 3; -} diff --git a/kmymoney/widgets/selectedtransactions.h b/kmymoney/widgets/selectedtransactions.h deleted file mode 100644 index 0f742067a..000000000 --- a/kmymoney/widgets/selectedtransactions.h +++ /dev/null @@ -1,56 +0,0 @@ -/*************************************************************************** - selectedtransactions.h - description - ------------------- - begin : Tue Jun 13 2006 - copyright : (C) 2000-2006 by Thomas Baumgart - email : Thomas Baumgart - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef SELECTEDTRANSACTIONS_H -#define SELECTEDTRANSACTIONS_H - -// ---------------------------------------------------------------------------- -// QT Includes - -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "selectedtransaction.h" - -namespace KMyMoneyRegister -{ - class Register; - - class SelectedTransactions: public QList - { - public: - SelectedTransactions() {} // TODO: find out how to move this ctor out of header - explicit SelectedTransactions(const Register* r); - - /** - * @return the highest warnLevel of all transactions in the list - */ - int warnLevel() const; - - bool canModify() const; - bool canDuplicate() const; - }; - -} // namespace - -#endif - diff --git a/kmymoney/widgets/stdtransaction.cpp b/kmymoney/widgets/stdtransaction.cpp deleted file mode 100644 index 525bba2a6..000000000 --- a/kmymoney/widgets/stdtransaction.cpp +++ /dev/null @@ -1,720 +0,0 @@ -/*************************************************************************** - stdtransaction.cpp - description - ------------------- - begin : Tue Jun 13 2006 - copyright : (C) 2000-2006 by Thomas Baumgart - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#include "stdtransaction.h" -#include "stdtransaction_p.h" -#include "transaction_p.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -#include - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "kmymoneypayeecombo.h" -#include "kmymoneytagcombo.h" -#include "ktagcontainer.h" -#include "mymoneyutils.h" -#include "mymoneytransaction.h" -#include "mymoneysplit.h" -#include "mymoneyfile.h" -#include "mymoneypayee.h" -#include "mymoneytag.h" -#include "register.h" -#include "kmymoneycategory.h" -#include "kmymoneydateinput.h" -#include "transactionform.h" -#include "kmymoneylineedit.h" -#include "kmymoneyedit.h" -#include "transactioneditor.h" -#include "investtransactioneditor.h" -#include "kmymoneyutils.h" -#include "kmymoneymvccombo.h" -#ifndef KMM_DESIGNER -#include "stdtransactioneditor.h" -#endif - -#include "kmymoneyglobalsettings.h" - -using namespace KMyMoneyRegister; -using namespace KMyMoneyTransactionForm; - -StdTransaction::StdTransaction() : - Transaction(*new StdTransactionPrivate, nullptr, MyMoneyTransaction(), MyMoneySplit(), 0) -{ -} - -StdTransaction::StdTransaction(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : - Transaction(*new StdTransactionPrivate, parent, transaction, split, uniqueId) -{ - Q_D(StdTransaction); - d->m_showAccountRow = false; - try { - d->m_categoryHeader = i18n("Category"); - switch (transaction.splitCount()) { - default: - d->m_category = i18nc("Split transaction (category replacement)", "Split transaction"); - break; - - case 0: // the empty transaction - case 1: - break; - - case 2: - setupFormHeader(d->m_transaction.splitByAccount(d->m_split.accountId(), false).accountId()); - break; - } - } catch (const MyMoneyException &e) { - qDebug() << "Problem determining the category for transaction '" << d->m_transaction.id() << "'. Reason: " << e.what() << "\n"; - } - d->m_rowsForm = 6; - - if (KMyMoneyUtils::transactionType(d->m_transaction) == KMyMoneyUtils::InvestmentTransaction) { - MyMoneySplit split = KMyMoneyUtils::stockSplit(d->m_transaction); - d->m_payee = MyMoneyFile::instance()->account(split.accountId()).name(); - QString addon; - if (split.action() == MyMoneySplit::ActionBuyShares) { - if (split.value().isNegative()) { - addon = i18n("Sell"); - } else { - addon = i18n("Buy"); - } - } else if (split.action() == MyMoneySplit::ActionDividend) { - addon = i18n("Dividend"); - } else if (split.action() == MyMoneySplit::ActionYield) { - addon = i18n("Yield"); - } else if (split.action() == MyMoneySplit::ActionInterestIncome) { - addon = i18n("Interest Income"); - } - if (!addon.isEmpty()) { - d->m_payee += QString(" (%1)").arg(addon); - } - d->m_payeeHeader = i18n("Activity"); - d->m_category = i18n("Investment transaction"); - } - - // setup initial size - setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); - - emit parent->itemAdded(this); -} - -StdTransaction::StdTransaction(const StdTransaction& other) : - Transaction(*new StdTransactionPrivate(*other.d_func())) -{ -} - -StdTransaction::~StdTransaction() -{ -} - -const char* StdTransaction::className() -{ - return "StdTransaction"; -} - -void StdTransaction::setupFormHeader(const QString& id) -{ - Q_D(StdTransaction); - d->m_category = MyMoneyFile::instance()->accountToCategory(id); - switch (MyMoneyFile::instance()->account(id).accountGroup()) { - case eMyMoney::Account::Asset: - case eMyMoney::Account::Liability: - d->m_categoryHeader = d->m_split.shares().isNegative() ? i18n("Transfer to") : i18n("Transfer from"); - break; - - default: - d->m_categoryHeader = i18n("Category"); - break; - } -} - -KMyMoneyRegister::Action StdTransaction::actionType() const -{ - Q_D(const StdTransaction); - KMyMoneyRegister::Action action = ActionNone; - - // if at least one split is referencing an income or - // expense account, we will not call it a transfer - QList::const_iterator it_s; - - for (it_s = d->m_transaction.splits().begin(); it_s != d->m_transaction.splits().end(); ++it_s) { - if ((*it_s).accountId() == d->m_split.accountId()) - continue; - MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId()); - if (acc.accountGroup() == eMyMoney::Account::Income - || acc.accountGroup() == eMyMoney::Account::Expense) { - // otherwise, we have to determine between deposit and withdrawal - action = d->m_split.shares().isNegative() ? ActionWithdrawal : ActionDeposit; - break; - } - } - // otherwise, it's a transfer - if (it_s == d->m_transaction.splits().end()) - action = ActionTransfer; - - return action; -} - -void StdTransaction::loadTab(TransactionForm* form) -{ - Q_D(StdTransaction); - TabBar* bar = form->tabBar(); - bar->setSignalEmission(TabBar::SignalNever); - for (auto i = 0; i < bar->count(); ++i) { - bar->setTabEnabled(i, true); - } - - if (d->m_transaction.splitCount() > 0) { - bar->setCurrentIndex(actionType()); - } - bar->setSignalEmission(TabBar::SignalAlways); -} - -int StdTransaction::numColsForm() const -{ - return 4; -} - -void StdTransaction::setupForm(TransactionForm* form) -{ - Transaction::setupForm(form); - form->setSpan(4, ValueColumn1, 3, 1); -} - -bool StdTransaction::showRowInForm(int row) const -{ - Q_D(const StdTransaction); - return row == 0 ? d->m_showAccountRow : true; -} - -void StdTransaction::setShowRowInForm(int row, bool show) -{ - Q_D(StdTransaction); - if (row == 0) - d->m_showAccountRow = show; -} - -bool StdTransaction::formCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* /* painter */) -{ - Q_D(const StdTransaction); - // if(m_transaction != MyMoneyTransaction()) { - switch (row) { - case 0: - switch (col) { - case LabelColumn1: - align |= Qt::AlignLeft; - txt = i18n("Account"); - break; - } - break; - - case 1: - switch (col) { - case LabelColumn1: - align |= Qt::AlignLeft; - txt = d->m_payeeHeader; - break; - - case ValueColumn1: - align |= Qt::AlignLeft; - txt = d->m_payee; - break; - - case LabelColumn2: - align |= Qt::AlignLeft; - if (haveNumberField()) - txt = i18n("Number"); - break; - - case ValueColumn2: - align |= Qt::AlignRight; - if (haveNumberField()) - txt = d->m_split.number(); - break; - } - break; - - case 2: - switch (col) { - case LabelColumn1: - align |= Qt::AlignLeft; - txt = d->m_categoryHeader; - break; - - case ValueColumn1: - align |= Qt::AlignLeft; - txt = d->m_category; - if (d->m_transaction != MyMoneyTransaction()) { - if (txt.isEmpty() && !d->m_split.value().isZero()) - txt = i18n("*** UNASSIGNED ***"); - } - break; - - case LabelColumn2: - align |= Qt::AlignLeft; - txt = i18n("Date"); - break; - - case ValueColumn2: - align |= Qt::AlignRight; - if (d->m_transaction != MyMoneyTransaction()) - txt = QLocale().toString(d->m_transaction.postDate(), QLocale::ShortFormat); - break; - } - break; - - case 3: - switch (col) { - case LabelColumn1: - align |= Qt::AlignLeft; - txt = i18n("Tags"); - break; - - case ValueColumn1: - align |= Qt::AlignLeft; - if (!d->m_tagList.isEmpty()) { - for (auto i = 0; i < d->m_tagList.size() - 1; i++) - txt += d->m_tagList[i] + ", "; - txt += d->m_tagList.last(); - } - //if (m_transaction != MyMoneyTransaction()) - // txt = m_split.tagId(); - break; - - case LabelColumn2: - align |= Qt::AlignLeft; - txt = i18n("Amount"); - break; - - case ValueColumn2: - align |= Qt::AlignRight; - if (d->m_transaction != MyMoneyTransaction()) { - txt = (d->m_split.value(d->m_transaction.commodity(), d->m_splitCurrencyId).abs()).formatMoney(d->m_account.fraction()); - } - break; - } - break; - - case 4: - switch (col) { - case LabelColumn1: - align |= Qt::AlignLeft; - txt = i18n("Memo"); - break; - - case ValueColumn1: - align &= ~Qt::AlignVCenter; - align |= Qt::AlignTop; - align |= Qt::AlignLeft; - if (d->m_transaction != MyMoneyTransaction()) - txt = d->m_split.memo().section('\n', 0, 2); - break; - } - break; - - case 5: - switch (col) { - case LabelColumn2: - align |= Qt::AlignLeft; - txt = i18n("Status"); - break; - - case ValueColumn2: - align |= Qt::AlignRight; - txt = reconcileState(); - break; - } - } - - // } - if (col == ValueColumn2 && row == 1) { - return haveNumberField(); - } - return (col == ValueColumn1 && row < 5) || (col == ValueColumn2 && row > 0 && row != 4); -} - -void StdTransaction::registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter) -{ - Q_D(const StdTransaction); - switch (row) { - case 0: - switch (col) { - case NumberColumn: - align |= Qt::AlignLeft; - if (haveNumberField()) - txt = d->m_split.number(); - break; - - case DateColumn: - align |= Qt::AlignLeft; - txt = QLocale().toString(d->m_transaction.postDate(), QLocale::ShortFormat); - break; - - case DetailColumn: - switch (d->m_parent->getDetailsColumnType()) { - case PayeeFirst: - txt = d->m_payee; - break; - case AccountFirst: - txt = d->m_category; - if (!d->m_tagList.isEmpty()) { - txt += " ( "; - for (auto i = 0; i < d->m_tagList.size() - 1; i++) { - txt += " " + d->m_tagList[i] + ", "; - } - txt += " " + d->m_tagList.last() + " )"; - } - break; - } - align |= Qt::AlignLeft; - if (txt.isEmpty() && d->m_rowsRegister < 3) { - singleLineMemo(txt, d->m_split); - } - if (txt.isEmpty() && d->m_rowsRegister < 2) { - if (d->m_account.accountType() != eMyMoney::Account::Income - && d->m_account.accountType() != eMyMoney::Account::Expense) { - txt = d->m_category; - if (txt.isEmpty() && !d->m_split.value().isZero()) { - txt = i18n("*** UNASSIGNED ***"); - if (painter) - painter->setPen(KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionErroneous)); - } - } - } - break; - - case ReconcileFlagColumn: - align |= Qt::AlignHCenter; - txt = reconcileState(false); - break; - - case PaymentColumn: - align |= Qt::AlignRight; - if (d->m_split.value().isNegative()) { - txt = (-d->m_split.value(d->m_transaction.commodity(), d->m_splitCurrencyId)).formatMoney(d->m_account.fraction()); - } - break; - - case DepositColumn: - align |= Qt::AlignRight; - if (!d->m_split.value().isNegative()) { - txt = d->m_split.value(d->m_transaction.commodity(), d->m_splitCurrencyId).formatMoney(d->m_account.fraction()); - } - break; - - case BalanceColumn: - align |= Qt::AlignRight; - if (d->m_showBalance) - txt = d->m_balance.formatMoney(d->m_account.fraction()); - else - txt = "----"; - break; - - case AccountColumn: - // txt = m_objects->account(m_transaction.splits()[0].accountId()).name(); - txt = MyMoneyFile::instance()->account(d->m_split.accountId()).name(); - break; - - default: - break; - } - break; - - case 1: - switch (col) { - case DetailColumn: - switch (d->m_parent->getDetailsColumnType()) { - case PayeeFirst: - txt = d->m_category; - if (!d->m_tagList.isEmpty()) { - txt += " ( "; - for (auto i = 0; i < d->m_tagList.size() - 1; i++) { - txt += " " + d->m_tagList[i] + ", "; - } - txt += " " + d->m_tagList.last() + " )"; - } - break; - case AccountFirst: - txt = d->m_payee; - break; - } - align |= Qt::AlignLeft; - if (txt.isEmpty() && !d->m_split.value().isZero()) { - txt = i18n("*** UNASSIGNED ***"); - if (painter) - painter->setPen(KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionErroneous)); - } - break; - - default: - break; - } - break; - - case 2: - switch (col) { - case DetailColumn: - align |= Qt::AlignLeft; - singleLineMemo(txt, d->m_split); - break; - - default: - break; - } - break; - } -} - -int StdTransaction::registerColWidth(int col, const QFontMetrics& cellFontMetrics) -{ - QString txt; - int firstRow = 0, lastRow = numRowsRegister(); - - int nw = 0; - for (int i = firstRow; i <= lastRow; ++i) { - Qt::Alignment align; - registerCellText(txt, align, i, col, 0); - int w = cellFontMetrics.width(txt + " "); - if (w > nw) - nw = w; - } - return nw; -} - -void StdTransaction::arrangeWidgetsInForm(QMap& editWidgets) -{ - Q_D(StdTransaction); - if (!d->m_form || !d->m_parent) - return; - - setupFormPalette(editWidgets); - - arrangeWidget(d->m_form, 0, LabelColumn1, editWidgets["account-label"]); - arrangeWidget(d->m_form, 0, ValueColumn1, editWidgets["account"]); - arrangeWidget(d->m_form, 1, LabelColumn1, editWidgets["cashflow"]); - arrangeWidget(d->m_form, 1, ValueColumn1, editWidgets["payee"]); - arrangeWidget(d->m_form, 2, LabelColumn1, editWidgets["category-label"]); - arrangeWidget(d->m_form, 2, ValueColumn1, editWidgets["category"]->parentWidget()); - arrangeWidget(d->m_form, 3, LabelColumn1, editWidgets["tag-label"]); - arrangeWidget(d->m_form, 3, ValueColumn1, editWidgets["tag"]); - arrangeWidget(d->m_form, 4, LabelColumn1, editWidgets["memo-label"]); - arrangeWidget(d->m_form, 4, ValueColumn1, editWidgets["memo"]); - if (haveNumberField()) { - arrangeWidget(d->m_form, 1, LabelColumn2, editWidgets["number-label"]); - arrangeWidget(d->m_form, 1, ValueColumn2, editWidgets["number"]); - } - arrangeWidget(d->m_form, 2, LabelColumn2, editWidgets["date-label"]); - arrangeWidget(d->m_form, 2, ValueColumn2, editWidgets["postdate"]); - arrangeWidget(d->m_form, 3, LabelColumn2, editWidgets["amount-label"]); - arrangeWidget(d->m_form, 3, ValueColumn2, editWidgets["amount"]); - arrangeWidget(d->m_form, 5, LabelColumn2, editWidgets["status-label"]); - arrangeWidget(d->m_form, 5, ValueColumn2, editWidgets["status"]); - - // get rid of the hints. we don't need them for the form - QMap::iterator it; - for (it = editWidgets.begin(); it != editWidgets.end(); ++it) { - KMyMoneyCombo* combo = dynamic_cast(*it); - kMyMoneyLineEdit* edit = dynamic_cast(*it); - KMyMoneyPayeeCombo* payee = dynamic_cast(*it); - KTagContainer* tag = dynamic_cast(*it); - if (combo) - combo->setPlaceholderText(QString()); - if (edit) - edit->setPlaceholderText(QString()); - if (payee) - payee->setPlaceholderText(QString()); - if (tag) - tag->tagCombo()->setPlaceholderText(QString()); - } - - KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast(d->m_form); - TabBar* w = dynamic_cast(editWidgets["tabbar"]); - if (w) { - // insert the tabbar in the boxlayout so it will take the place of the original tabbar which was hidden - QBoxLayout* boxLayout = dynamic_cast(form->tabBar()->parentWidget()->layout()); - boxLayout->insertWidget(0, w); - } -} - -void StdTransaction::tabOrderInForm(QWidgetList& tabOrderWidgets) const -{ - Q_D(const StdTransaction); - QStringList taborder = KMyMoneyGlobalSettings::stdTransactionFormTabOrder().split(',', QString::SkipEmptyParts); - QStringList::const_iterator it_s = taborder.constBegin(); - QWidget* w; - while (it_s != taborder.constEnd()) { - if (*it_s == "account") { - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(0, ValueColumn1))); - } else if (*it_s == "cashflow") { - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(1, LabelColumn1))); - } else if (*it_s == "payee") { - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(1, ValueColumn1))); - } else if (*it_s == "category") { - // make sure to have the category field and the split button as separate tab order widgets - // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but - // it's one of our own widgets, so we actually don't care. Just make sure, that we don't - // go haywire when someone changes the KMyMoneyCategory object ... - QWidget* w = d->m_form->cellWidget(2, ValueColumn1); - tabOrderWidgets.append(focusWidget(w)); - w = w->findChild("splitButton"); - if (w) - tabOrderWidgets.append(w); - } else if (*it_s == "tag") { - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(3, ValueColumn1))); - } else if (*it_s == "memo") { - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(4, ValueColumn1))); - } else if (*it_s == "number") { - if (haveNumberField()) { - if ((w = focusWidget(d->m_form->cellWidget(1, ValueColumn2)))) - tabOrderWidgets.append(w); - } - } else if (*it_s == "date") { - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(2, ValueColumn2))); - } else if (*it_s == "amount") { - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(3, ValueColumn2))); - } else if (*it_s == "state") { - tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(5, ValueColumn2))); - } - ++it_s; - } -} - -void StdTransaction::arrangeWidgetsInRegister(QMap& editWidgets) -{ - Q_D(StdTransaction); - if (!d->m_parent) - return; - - setupRegisterPalette(editWidgets); - - if (haveNumberField()) - arrangeWidget(d->m_parent, d->m_startRow + 0, NumberColumn, editWidgets["number"]); - arrangeWidget(d->m_parent, d->m_startRow + 0, DateColumn, editWidgets["postdate"]); - arrangeWidget(d->m_parent, d->m_startRow + 1, DateColumn, editWidgets["status"]); - arrangeWidget(d->m_parent, d->m_startRow + 0, DetailColumn, editWidgets["payee"]); - arrangeWidget(d->m_parent, d->m_startRow + 1, DetailColumn, editWidgets["category"]->parentWidget()); - arrangeWidget(d->m_parent, d->m_startRow + 2, DetailColumn, editWidgets["tag"]); - arrangeWidget(d->m_parent, d->m_startRow + 3, DetailColumn, editWidgets["memo"]); - arrangeWidget(d->m_parent, d->m_startRow + 0, PaymentColumn, editWidgets["payment"]); - arrangeWidget(d->m_parent, d->m_startRow + 0, DepositColumn, editWidgets["deposit"]); - - // increase the height of the row containing the memo widget - d->m_parent->setRowHeight(d->m_startRow + 3, d->m_parent->rowHeightHint() * 3); -} - -void StdTransaction::tabOrderInRegister(QWidgetList& tabOrderWidgets) const -{ - Q_D(const StdTransaction); - QStringList taborder = KMyMoneyGlobalSettings::stdTransactionRegisterTabOrder().split(',', QString::SkipEmptyParts); - QStringList::const_iterator it_s = taborder.constBegin(); - QWidget* w; - while (it_s != taborder.constEnd()) { - if (*it_s == "number") { - if (haveNumberField()) { - if ((w = focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, NumberColumn)))) - tabOrderWidgets.append(w); - } - } else if (*it_s == "date") { - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, DateColumn))); - } else if (*it_s == "payee") { - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, DetailColumn))); - } else if (*it_s == "category") { - // make sure to have the category field and the split button as separate tab order widgets - // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but - // it's one of our own widgets, so we actually don't care. Just make sure, that we don't - // go haywire when someone changes the KMyMoneyCategory object ... - w = d->m_parent->cellWidget(d->m_startRow + 1, DetailColumn); - tabOrderWidgets.append(focusWidget(w)); - w = w->findChild("splitButton"); - if (w) - tabOrderWidgets.append(w); - } else if (*it_s == "tag") { - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 2, DetailColumn))); - } else if (*it_s == "memo") { - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 3, DetailColumn))); - } else if (*it_s == "payment") { - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, PaymentColumn))); - } else if (*it_s == "deposit") { - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, DepositColumn))); - } else if (*it_s == "state") { - tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 1, DateColumn))); - } - ++it_s; - } -} - -int StdTransaction::numRowsRegister(bool expanded) const -{ - Q_D(const StdTransaction); - int numRows = 1; - if (expanded) { - numRows = 4; - if (!d->m_inEdit) { - //When not in edit Tags haven't a separate row; - numRows--; - if (d->m_payee.isEmpty()) { - numRows--; - } - if (d->m_split.memo().isEmpty()) { - numRows--; - } - // For income and expense accounts that only have - // two splits we only show one line, because the - // account name is already contained in the account column. - if (d->m_account.accountType() == eMyMoney::Account::Income - || d->m_account.accountType() == eMyMoney::Account::Expense) { - if (numRows > 2 && d->m_transaction.splitCount() == 2) - numRows = 1; - } - } - } - return numRows; -} - -int StdTransaction::numRowsRegister() const -{ - return RegisterItem::numRowsRegister(); -} - -TransactionEditor* StdTransaction::createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) -{ -#ifndef KMM_DESIGNER - Q_D(StdTransaction); - d->m_inRegisterEdit = regForm == d->m_parent; - return new StdTransactionEditor(regForm, this, list, lastPostDate); -#else - return NULL; -#endif -} diff --git a/kmymoney/widgets/stdtransaction.h b/kmymoney/widgets/stdtransaction.h deleted file mode 100644 index eb9b9de65..000000000 --- a/kmymoney/widgets/stdtransaction.h +++ /dev/null @@ -1,110 +0,0 @@ -/*************************************************************************** - stdtransaction.h - description - ------------------- - begin : Tue Jun 13 2006 - copyright : (C) 2000-2006 by Thomas Baumgart - email : Thomas Baumgart - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef STDTRANSACTION_H -#define STDTRANSACTION_H - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "transaction.h" - -namespace KMyMoneyRegister -{ - class StdTransactionPrivate; - class StdTransaction : public Transaction - { - public: - explicit StdTransaction(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - StdTransaction(const StdTransaction & other); - StdTransaction(StdTransaction && other); - StdTransaction & operator=(StdTransaction other); - friend void swap(StdTransaction& first, StdTransaction& second); - ~StdTransaction() override; - - virtual const char* className(); - - bool formCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter = 0); - void registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter = 0); - - int registerColWidth(int col, const QFontMetrics& cellFontMetrics); - void setupForm(KMyMoneyTransactionForm::TransactionForm* form); - void loadTab(KMyMoneyTransactionForm::TransactionForm* form); - - int numColsForm() const; - - void arrangeWidgetsInForm(QMap& editWidgets); - void arrangeWidgetsInRegister(QMap& editWidgets); - void tabOrderInForm(QWidgetList& tabOrderWidgets) const; - void tabOrderInRegister(QWidgetList& tabOrderWidgets) const; - KMyMoneyRegister::Action actionType() const; - - int numRowsRegister(bool expanded) const; - - /** - * Provided for internal reasons. No API change. See RegisterItem::numRowsRegister() - */ - int numRowsRegister() const; - - TransactionEditor* createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate); - - /** - * Return information if @a row should be shown (@a true ) - * or hidden (@a false ) in the form. Default is true. - */ - virtual bool showRowInForm(int row) const; - - /** - * Control visibility of @a row in the transaction form. - * Only row 0 has an effect, others return @a true. - */ - virtual void setShowRowInForm(int row, bool show); - - protected: - Q_DECLARE_PRIVATE(StdTransaction) - void setupFormHeader(const QString& id); - - private: - StdTransaction(); - }; - - inline void swap(StdTransaction& first, StdTransaction& second) // krazy:exclude=inline - { - using std::swap; - swap(first.d_ptr, second.d_ptr); - } - - inline StdTransaction::StdTransaction(StdTransaction && other) : StdTransaction() // krazy:exclude=inline - { - swap(*this, other); - } - - inline StdTransaction & StdTransaction::operator=(StdTransaction other) // krazy:exclude=inline - { - swap(*this, other); - return *this; - } - -} // namespace - -#endif diff --git a/kmymoney/widgets/stdtransaction_p.h b/kmymoney/widgets/stdtransaction_p.h deleted file mode 100644 index 7d2ed0620..000000000 --- a/kmymoney/widgets/stdtransaction_p.h +++ /dev/null @@ -1,41 +0,0 @@ -/*************************************************************************** - stdtransaction_p.h - description - ------------------- - begin : Tue Jun 13 2006 - copyright : (C) 2000-2006 by Thomas Baumgart - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef STDTRANSACTION_P_H -#define STDTRANSACTION_P_H - -#include "transaction_p.h" - -// ---------------------------------------------------------------------------- -// QT Includes - -// ---------------------------------------------------------------------------- -// KDE Includes - -// ---------------------------------------------------------------------------- -// Project Includes - -namespace KMyMoneyRegister -{ - class StdTransactionPrivate : public TransactionPrivate - { - public: - bool m_showAccountRow; - }; -} - -#endif diff --git a/kmymoney/widgets/stdtransactiondownloaded.cpp b/kmymoney/widgets/stdtransactiondownloaded.cpp index 4b3e86ba3..22dca1a64 100644 --- a/kmymoney/widgets/stdtransactiondownloaded.cpp +++ b/kmymoney/widgets/stdtransactiondownloaded.cpp @@ -1,86 +1,67 @@ /*************************************************************************** stdtransactiondownloaded.cpp ------------------- begin : Sun May 11 2008 copyright : (C) 2008 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "stdtransactiondownloaded.h" // ---------------------------------------------------------------------------- // QT Includes -#include - // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyglobalsettings.h" using namespace KMyMoneyRegister; using namespace KMyMoneyTransactionForm; StdTransactionDownloaded::StdTransactionDownloaded(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : - StdTransaction(parent, transaction, split, uniqueId) -{ -} - -StdTransactionDownloaded::~StdTransactionDownloaded() -{ -} - -const char* StdTransactionDownloaded::className() + StdTransaction(parent, transaction, split, uniqueId) { - return "StdTransactionDownloaded"; } bool StdTransactionDownloaded::paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) { - auto rc = Transaction::paintRegisterCellSetup(painter, option, index); + bool rc = Transaction::paintRegisterCellSetup(painter, option, index); // if not selected paint in selected background color if (!isSelected()) { option.palette.setColor(QPalette::Base, KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionImported)); option.palette.setColor(QPalette::AlternateBase, KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionImported)); } return rc; } InvestTransactionDownloaded::InvestTransactionDownloaded(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : - InvestTransaction(parent, transaction, split, uniqueId) + InvestTransaction(parent, transaction, split, uniqueId) { } -InvestTransactionDownloaded::~InvestTransactionDownloaded() -{ -} - -const char* InvestTransactionDownloaded::className() -{ - return "InvestTransactionDownloaded"; -} - bool InvestTransactionDownloaded::paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) + { - auto rc = Transaction::paintRegisterCellSetup(painter, option, index); + bool rc = Transaction::paintRegisterCellSetup(painter, option, index); // if not selected paint in selected background color if (!isSelected()) { option.palette.setColor(QPalette::Base, KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionImported)); option.palette.setColor(QPalette::AlternateBase, KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionImported)); } return rc; } diff --git a/kmymoney/widgets/stdtransactiondownloaded.h b/kmymoney/widgets/stdtransactiondownloaded.h index f7d0ed90d..b0cd377fd 100644 --- a/kmymoney/widgets/stdtransactiondownloaded.h +++ b/kmymoney/widgets/stdtransactiondownloaded.h @@ -1,61 +1,69 @@ /*************************************************************************** stdtransactiondownloaded.h ------------------- begin : Sun May 11 2008 copyright : (C) 2008 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef STDTRANSACTIONDOWNLOADED_H #define STDTRANSACTIONDOWNLOADED_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "stdtransaction.h" -#include "investtransaction.h" +#include "transaction.h" + +namespace KMyMoneyTransactionForm +{ +class TransactionForm; +} // namespace namespace KMyMoneyRegister { - class StdTransactionDownloaded : public StdTransaction - { - public: - explicit StdTransactionDownloaded(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - ~StdTransactionDownloaded() override; +class StdTransactionDownloaded : public StdTransaction +{ +public: + StdTransactionDownloaded(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~StdTransactionDownloaded() {} - const char* className() override; + virtual const char* className() { + return "StdTransactionDownloaded"; + } - bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) override; - }; + virtual bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index); +}; - class InvestTransactionDownloaded : public InvestTransaction - { - public: - explicit InvestTransactionDownloaded(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - ~InvestTransactionDownloaded() override; +class InvestTransactionDownloaded : public InvestTransaction +{ +public: + InvestTransactionDownloaded(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~InvestTransactionDownloaded() {} - const char* className() override; + virtual const char* className() { + return "InvestTransactionDownloaded"; + } - bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) override; - }; + virtual bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index); +}; } // namespace #endif diff --git a/kmymoney/widgets/stdtransactionmatched.cpp b/kmymoney/widgets/stdtransactionmatched.cpp index 46d90bf1a..311559aa7 100644 --- a/kmymoney/widgets/stdtransactionmatched.cpp +++ b/kmymoney/widgets/stdtransactionmatched.cpp @@ -1,204 +1,183 @@ /*************************************************************************** stdtransactionmatched.cpp ------------------- begin : Sat May 11 2008 copyright : (C) 2008 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "stdtransactionmatched.h" -#include "stdtransaction_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneyglobalsettings.h" #include "mymoneyaccount.h" #include "mymoneymoney.h" #include "mymoneysplit.h" #include "mymoneytransaction.h" using namespace KMyMoneyRegister; using namespace KMyMoneyTransactionForm; StdTransactionMatched::StdTransactionMatched(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : - StdTransaction(parent, transaction, split, uniqueId) + StdTransaction(parent, transaction, split, uniqueId) { // setup initial size setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); } -StdTransactionMatched::~StdTransactionMatched() -{ -} - -const char* StdTransactionMatched::className() -{ - return "StdTransactionMatched"; -} - bool StdTransactionMatched::paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) { - auto rc = Transaction::paintRegisterCellSetup(painter, option, index); + bool rc = Transaction::paintRegisterCellSetup(painter, option, index); // if not selected paint in matched background color if (!isSelected()) { option.palette.setColor(QPalette::Base, KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionMatched)); option.palette.setColor(QPalette::AlternateBase, KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionMatched)); } //TODO: the first line needs to be painted across all columns return rc; } void StdTransactionMatched::registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter) { - Q_D(StdTransaction); // run through the standard StdTransaction::registerCellText(txt, align, row, col, painter); // we only cover the additional rows - if (row >= RegisterItem::numRowsRegister() - m_additionalRows) { + if (row >= m_rowsRegister - m_additionalRows) { // make row relative to the last three rows - row += m_additionalRows - RegisterItem::numRowsRegister(); + row += m_additionalRows - m_rowsRegister; // remove anything that had been added by the standard method - txt = QString(); + txt = ""; // and we draw this information in italics if (painter) { QFont font = painter->font(); font.setItalic(true); painter->setFont(font); } - MyMoneyTransaction matchedTransaction = d->m_split.matchedTransaction(); + MyMoneyTransaction matchedTransaction = m_split.matchedTransaction(); MyMoneySplit matchedSplit; try { - matchedSplit = matchedTransaction.splitById(d->m_split.value("kmm-match-split")); + matchedSplit = matchedTransaction.splitById(m_split.value("kmm-match-split")); } catch (const MyMoneyException &) { } QList::const_iterator it_s; const QList& list = matchedTransaction.splits(); MyMoneyMoney importedValue; for (it_s = list.begin(); it_s != list.end(); ++it_s) { - if ((*it_s).accountId() == d->m_account.id()) { + if ((*it_s).accountId() == m_account.id()) { importedValue += (*it_s).shares(); } } QDate postDate; QString memo; switch (row) { case 0: if (painter && col == DetailColumn) txt = QString(" ") + i18n("KMyMoney has matched the two selected transactions (result above)"); // return true for the first visible column only break; case 1: switch (col) { case DateColumn: align |= Qt::AlignLeft; txt = i18n("Bank entry:"); break; case DetailColumn: align |= Qt::AlignLeft; memo = matchedTransaction.memo(); memo.replace("\n\n", "\n"); memo.replace('\n', ", "); txt = QString("%1 %2").arg(matchedTransaction.postDate().toString(Qt::ISODate)).arg(memo); break; case PaymentColumn: align |= Qt::AlignRight; if (importedValue.isNegative()) { - txt = (-importedValue).formatMoney(d->m_account.fraction()); + txt = (-importedValue).formatMoney(m_account.fraction()); } break; case DepositColumn: align |= Qt::AlignRight; if (!importedValue.isNegative()) { - txt = importedValue.formatMoney(d->m_account.fraction()); + txt = importedValue.formatMoney(m_account.fraction()); } break; } break; case 2: switch (col) { case DateColumn: align |= Qt::AlignLeft; txt = i18n("Your entry:"); break; case DetailColumn: align |= Qt::AlignLeft; - postDate = d->m_transaction.postDate(); - if (!d->m_split.value("kmm-orig-postdate").isEmpty()) { - postDate = QDate::fromString(d->m_split.value("kmm-orig-postdate"), Qt::ISODate); + postDate = m_transaction.postDate(); + if (!m_split.value("kmm-orig-postdate").isEmpty()) { + postDate = QDate::fromString(m_split.value("kmm-orig-postdate"), Qt::ISODate); } - memo = d->m_split.memo(); + memo = m_split.memo(); if (!matchedSplit.memo().isEmpty() && memo != matchedSplit.memo()) { int pos = memo.lastIndexOf(matchedSplit.memo()); if (pos != -1) { memo = memo.left(pos); // replace all new line characters because we only have one line available for the displayed data } } memo.replace("\n\n", "\n"); memo.replace('\n', ", "); txt = QString("%1 %2").arg(postDate.toString(Qt::ISODate)).arg(memo); break; case PaymentColumn: align |= Qt::AlignRight; - if (d->m_split.value().isNegative()) { - txt = (-d->m_split.value(d->m_transaction.commodity(), d->m_splitCurrencyId)).formatMoney(d->m_account.fraction()); + if (m_split.value().isNegative()) { + txt = (-m_split.value(m_transaction.commodity(), m_splitCurrencyId)).formatMoney(m_account.fraction()); } break; case DepositColumn: align |= Qt::AlignRight; - if (!d->m_split.value().isNegative()) { - txt = d->m_split.value(d->m_transaction.commodity(), d->m_splitCurrencyId).formatMoney(d->m_account.fraction()); + if (!m_split.value().isNegative()) { + txt = m_split.value(m_transaction.commodity(), m_splitCurrencyId).formatMoney(m_account.fraction()); } break; } break; } } } - -int StdTransactionMatched::numRowsRegister(bool expanded) const -{ - return StdTransaction::numRowsRegister(expanded) + m_additionalRows; -} - -int StdTransactionMatched::numRowsRegister() const -{ - return StdTransaction::numRowsRegister(); -} diff --git a/kmymoney/widgets/stdtransactionmatched.h b/kmymoney/widgets/stdtransactionmatched.h index 13259c46b..58813ebec 100644 --- a/kmymoney/widgets/stdtransactionmatched.h +++ b/kmymoney/widgets/stdtransactionmatched.h @@ -1,66 +1,72 @@ /*************************************************************************** stdtransactionmatched.h ------------------- begin : Sat May 31 2008 copyright : (C) 2008 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef STDTRANSACTIONMATCHED_H #define STDTRANSACTIONMATCHED_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -#include "stdtransaction.h" +#include "transaction.h" class MyMoneySplit; class MyMoneyTransaction; namespace KMyMoneyRegister { - class Register; - class StdTransactionMatched : public StdTransaction - { - static const int m_additionalRows = 3; +class Register; +class StdTransactionMatched : public StdTransaction +{ + static const int m_additionalRows = 3; - public: - explicit StdTransactionMatched(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - ~StdTransactionMatched() override; +public: + StdTransactionMatched(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~StdTransactionMatched() {} - const char* className() override; + virtual const char* className() override { + return "StdTransactionMatched"; + } - bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) override; + virtual bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) override; - void registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter = 0) override; + void registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter = 0) override; - /** + /** * Provided for internal reasons. No API change. See RegisterItem::numRowsRegister(bool) */ - int numRowsRegister(bool expanded) const override; + int numRowsRegister(bool expanded) const override { + return StdTransaction::numRowsRegister(expanded) + m_additionalRows; + } - /** + /** * Provided for internal reasons. No API change. See RegisterItem::numRowsRegister() */ - int numRowsRegister() const override; - }; + int numRowsRegister() const override { + return StdTransaction::numRowsRegister(); + } +}; } // namespace #endif diff --git a/kmymoney/widgets/transaction.cpp b/kmymoney/widgets/transaction.cpp index 38fadf238..664461028 100644 --- a/kmymoney/widgets/transaction.cpp +++ b/kmymoney/widgets/transaction.cpp @@ -1,953 +1,2201 @@ /*************************************************************************** transaction.cpp - description ------------------- begin : Tue Jun 13 2006 copyright : (C) 2000-2006 by Thomas Baumgart (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "transaction.h" -#include "transaction_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyutils.h" #include "mymoneytransaction.h" #include "mymoneysplit.h" #include "mymoneyfile.h" #include "mymoneypayee.h" #include "mymoneytag.h" #include "register.h" #include "kmymoneycategory.h" #include "kmymoneydateinput.h" #include "transactionform.h" #include "kmymoneylineedit.h" #include "kmymoneyedit.h" #include "transactioneditor.h" #include "investtransactioneditor.h" #include "kmymoneyutils.h" #include "kmymoneymvccombo.h" #ifndef KMM_DESIGNER #include "stdtransactioneditor.h" #endif #include "kmymoneyglobalsettings.h" using namespace KMyMoneyRegister; using namespace KMyMoneyTransactionForm; static unsigned char attentionSign[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x08, 0x06, 0x00, 0x00, 0x00, 0x8D, 0x89, 0x1D, 0x0D, 0x00, 0x00, 0x00, 0x04, 0x73, 0x42, 0x49, 0x54, 0x08, 0x08, 0x08, 0x08, 0x7C, 0x08, 0x64, 0x88, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x00, 0x77, 0x77, 0x77, 0x2E, 0x69, 0x6E, 0x6B, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2E, 0x6F, 0x72, 0x67, 0x9B, 0xEE, 0x3C, 0x1A, 0x00, 0x00, 0x02, 0x05, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8D, 0xAD, 0x95, 0xBF, 0x4B, 0x5B, 0x51, 0x14, 0xC7, 0x3F, 0x2F, 0xBC, 0x97, 0x97, 0x97, 0x97, 0x77, 0xF3, 0xF2, 0x1C, 0xA4, 0x54, 0x6B, 0x70, 0x10, 0x44, 0x70, 0x2A, 0x91, 0x2E, 0x52, 0x02, 0x55, 0x8A, 0xB5, 0xA3, 0xAB, 0x38, 0x08, 0x66, 0xCC, 0xEE, 0xE0, 0xE2, 0x20, 0xB8, 0x38, 0xB8, 0xB8, 0xF8, 0x1F, 0x38, 0x29, 0xA5, 0x29, 0x74, 0x90, 0x0E, 0x0D, 0x0E, 0x22, 0x1D, 0x44, 0xA8, 0xD0, 0xD4, 0xB4, 0x58, 0x4B, 0x09, 0xF9, 0xF1, 0x4A, 0x3B, 0xD4, 0xD3, 0xE1, 0x55, 0xD3, 0x34, 0xAF, 0x49, 0x6C, 0x3D, 0xF0, 0x85, 0x7B, 0xCF, 0xFD, 0x9E, 0xEF, 0x3D, 0xE7, 0xFE, 0xD4, 0x44, 0x84, 0xDB, 0xB4, 0x48, 0x2F, 0xA4, 0x94, 0xAB, 0xE5, 0x52, 0xAE, 0x96, 0xEB, 0x49, 0x51, 0x44, 0x3A, 0x02, 0x18, 0x88, 0xC7, 0xF1, 0xE3, 0x71, 0x7C, 0x60, 0xA0, 0x1B, 0xBF, 0x6B, 0x86, 0x49, 0xC5, 0x46, 0x3E, 0x47, 0x34, 0x9F, 0x23, 0x9A, 0x54, 0x6C, 0xFC, 0x57, 0x86, 0x40, 0xC6, 0x4B, 0xE1, 0x37, 0xCA, 0x48, 0xA3, 0x8C, 0x78, 0x29, 0x7C, 0x20, 0xD3, 0x31, 0xA6, 0xD3, 0xA0, 0x52, 0x1C, 0x6D, 0x6F, 0x72, 0xD9, 0x28, 0x23, 0xFE, 0x07, 0x64, 0x7B, 0x93, 0x4B, 0xA5, 0x38, 0xFA, 0x27, 0x41, 0x60, 0x6E, 0x74, 0x84, 0x7A, 0xE5, 0x1D, 0x92, 0x54, 0x88, 0xE7, 0x22, 0xD5, 0x12, 0x32, 0x3A, 0x42, 0x1D, 0x98, 0xBB, 0x91, 0x20, 0x60, 0xDA, 0x36, 0x17, 0xFB, 0x7B, 0xC8, 0xC1, 0x4B, 0x04, 0x02, 0xBC, 0x7E, 0x81, 0xEC, 0xEF, 0x21, 0xB6, 0xCD, 0x05, 0x60, 0xF6, 0x2C, 0x68, 0x9A, 0x2C, 0xCF, 0x4C, 0xE1, 0x4B, 0x05, 0x39, 0x3F, 0x69, 0x0A, 0xBE, 0x7F, 0x83, 0x48, 0x05, 0x99, 0x99, 0xC2, 0x37, 0x4D, 0x96, 0x7B, 0x12, 0x04, 0xFA, 0x2D, 0x8B, 0xC6, 0xE9, 0x61, 0x10, 0x2C, 0x15, 0xC4, 0x8A, 0x21, 0x86, 0x8E, 0xFC, 0xF8, 0x12, 0xF4, 0x4F, 0x0F, 0x11, 0xCB, 0xA2, 0x01, 0xF4, 0x77, 0x3D, 0x36, 0x4E, 0x82, 0xF5, 0xA5, 0x05, 0x8C, 0xE1, 0x74, 0xD3, 0x37, 0x34, 0x18, 0x20, 0xF2, 0x8B, 0x3D, 0x9C, 0x86, 0xA5, 0x05, 0x0C, 0x27, 0xC1, 0x7A, 0xC7, 0x63, 0x03, 0x8C, 0x2B, 0x07, 0xBF, 0x5A, 0x6A, 0x66, 0x27, 0x15, 0x64, 0x3A, 0x8B, 0x3C, 0x7A, 0xD8, 0xEA, 0xAB, 0x96, 0x10, 0xE5, 0xE0, 0x03, 0xE3, 0x7F, 0xCD, 0x50, 0x39, 0x6C, 0xAD, 0xAD, 0x10, 0x53, 0xAA, 0x75, 0xD2, 0xF4, 0xBD, 0x00, 0x2D, 0x5C, 0x05, 0x6B, 0x2B, 0xC4, 0x94, 0xC3, 0xD6, 0xEF, 0xFE, 0x6B, 0x41, 0x4D, 0xD3, 0x66, 0xFB, 0x3C, 0xC6, 0x16, 0xE7, 0xDB, 0x97, 0x61, 0xE2, 0x3E, 0x3C, 0xC8, 0xB4, 0x15, 0xC7, 0xE2, 0x3C, 0x91, 0x3E, 0x8F, 0x31, 0x4D, 0xD3, 0x66, 0x5B, 0x4A, 0x06, 0x8C, 0x84, 0xCD, 0x59, 0x61, 0xA7, 0xB5, 0xAC, 0x2B, 0x9C, 0x1C, 0x04, 0x08, 0x1B, 0x2B, 0xEC, 0x20, 0x09, 0x9B, 0x33, 0xC0, 0xB8, 0xDE, 0x65, 0x43, 0x27, 0x9F, 0x9D, 0xA4, 0x1E, 0x16, 0xF0, 0xF9, 0x6D, 0xB0, 0xC3, 0x86, 0x1E, 0xB4, 0xC3, 0x38, 0xD9, 0x49, 0xEA, 0x86, 0x4E, 0xFE, 0xEA, 0x29, 0xF4, 0x2C, 0x8B, 0xDA, 0x71, 0x31, 0x9C, 0xFC, 0xF5, 0x23, 0x32, 0x34, 0x88, 0xDC, 0xBD, 0x13, 0x5C, 0xBF, 0x30, 0xCE, 0x71, 0x11, 0xB1, 0x2C, 0x6A, 0x80, 0xA7, 0xDB, 0x36, 0xAB, 0x4F, 0xA6, 0x89, 0xBA, 0x49, 0x38, 0xFF, 0xD4, 0xBE, 0x4E, 0x00, 0xAF, 0x9E, 0x81, 0x08, 0xD4, 0xEA, 0x01, 0xFE, 0x34, 0x37, 0x09, 0x4F, 0x1F, 0x13, 0xDD, 0x7D, 0xCE, 0xAA, 0x96, 0x72, 0x29, 0x7C, 0xFB, 0xCE, 0x44, 0xB8, 0xD4, 0xCD, 0x2C, 0x66, 0x52, 0xD4, 0x6E, 0xFB, 0x0B, 0xF8, 0x09, 0x63, 0x63, 0x31, 0xE4, 0x85, 0x76, 0x2E, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 }; -Transaction::Transaction() : - RegisterItem(*new TransactionPrivate, nullptr) -{ -} - Transaction::Transaction(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : - RegisterItem(*new TransactionPrivate, parent) -{ - Q_D(Transaction); - d->m_transaction = transaction; - d->m_split = split; - d->m_form = nullptr; - d->m_uniqueId = d->m_transaction.id(); - d->init(uniqueId); -} - -Transaction::Transaction(TransactionPrivate &dd, Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : - RegisterItem(dd, parent), - d_ptr(&dd) -{ - Q_D(Transaction); - d->m_form = nullptr; - d->m_transaction = transaction; - d->m_split = split; - d->m_uniqueId = d->m_transaction.id(); - d->init(uniqueId); -} - - -Transaction::Transaction(TransactionPrivate &dd) : - RegisterItem(dd) -{ -} - -Transaction::Transaction(const Transaction& other) : - RegisterItem(*new TransactionPrivate(*other.d_func())) + RegisterItem(parent), + m_transaction(transaction), + m_split(split), + m_form(0), + m_uniqueId(m_transaction.id()), + m_formRowHeight(-1), + m_selected(false), + m_focus(false), + m_erroneous(false), + m_inEdit(false), + m_inRegisterEdit(false), + m_showBalance(true), + m_reducedIntensity(false) { -} + MyMoneyFile* file = MyMoneyFile::instance(); -Transaction::~Transaction() -{ -} + // load the account + if (!m_split.accountId().isEmpty()) + m_account = file->account(m_split.accountId()); -const char* Transaction::className() -{ - return "Transaction"; -} - -bool Transaction::isSelectable() const -{ - return true; -} - -bool Transaction::isSelected() const -{ - Q_D(const Transaction); - return d->m_selected; -} - -void Transaction::setSelected(bool selected) -{ - Q_D(Transaction); - if (!selected || (selected && isVisible())) - d->m_selected = selected; -} + // load the payee + if (!m_split.payeeId().isEmpty()) { + m_payee = file->payee(m_split.payeeId()).name(); + } + if (parent->account().isIncomeExpense()) { + m_payeeHeader = m_split.shares().isNegative() ? i18n("From") : i18n("Pay to"); + } else { + m_payeeHeader = m_split.shares().isNegative() ? i18n("Pay to") : i18n("From"); + } -bool Transaction::canHaveFocus() const -{ - return true; -} + // load the tag + if (!m_split.tagIdList().isEmpty()) { + const QList t = m_split.tagIdList(); + for (int i = 0; i < t.count(); i++) { + m_tagList << file->tag(t[i]).name(); + m_tagColorList << file->tag(t[i]).tagColor(); + } + } -bool Transaction::hasFocus() const -{ - Q_D(const Transaction); - return d->m_focus; -} + // load the currency + if (!m_transaction.id().isEmpty()) + m_splitCurrencyId = m_account.currencyId(); -bool Transaction::hasEditorOpen() const -{ - Q_D(const Transaction); - return d->m_inEdit; -} + // check if transaction is erroneous or not + m_erroneous = !m_transaction.splitSum().isZero(); -bool Transaction::isScheduled() const -{ - return false; + if (!m_uniqueId.isEmpty()) { + m_uniqueId += '-'; + QString id; + id.setNum(uniqueId); + m_uniqueId += id.rightJustified(3, '0'); + } } void Transaction::setFocus(bool focus, bool updateLens) { - Q_D(Transaction); - if (focus != d->m_focus) { - d->m_focus = focus; + if (focus != m_focus) { + m_focus = focus; } if (updateLens) { if (KMyMoneyGlobalSettings::ledgerLens() || !KMyMoneyGlobalSettings::transactionForm() || KMyMoneyGlobalSettings::showRegisterDetailed() - || d->m_parent->ledgerLens()) { + || m_parent->m_ledgerLensForced) { if (focus) setNumRowsRegister(numRowsRegister(true)); else setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); } } } -bool Transaction::isErroneous() const -{ - Q_D(const Transaction); - return d->m_erroneous; -} - -QDate Transaction::sortPostDate() const -{ - Q_D(const Transaction); - return d->m_transaction.postDate(); -} - -int Transaction::sortSamePostDate() const -{ - return 2; -} - -QDate Transaction::sortEntryDate() const -{ - Q_D(const Transaction); - return d->m_transaction.entryDate(); -} - -const QString& Transaction::sortPayee() const -{ - Q_D(const Transaction); - return d->m_payee; -} - -const QList& Transaction::sortTagList() const -{ - Q_D(const Transaction); - return d->m_tagList; -} - -MyMoneyMoney Transaction::sortValue() const -{ - Q_D(const Transaction); - return d->m_split.shares(); -} - -QString Transaction::sortNumber() const -{ - Q_D(const Transaction); - return d->m_split.number(); -} - -const QString& Transaction::sortEntryOrder() const -{ - Q_D(const Transaction); - return d->m_uniqueId; -} - -CashFlowDirection Transaction::sortType() const -{ - Q_D(const Transaction); - return d->m_split.shares().isNegative() ? Payment : Deposit; -} - -const QString& Transaction::sortCategory() const -{ - Q_D(const Transaction); - return d->m_category; -} - -eMyMoney::Split::State Transaction::sortReconcileState() const -{ - Q_D(const Transaction); - return d->m_split.reconcileFlag(); -} - -const QString& Transaction::id() const -{ - Q_D(const Transaction); - return d->m_uniqueId; -} - -const MyMoneyTransaction& Transaction::transaction() const -{ - Q_D(const Transaction); - return d->m_transaction; -} - -const MyMoneySplit& Transaction::split() const -{ - Q_D(const Transaction); - return d->m_split; -} - -void Transaction::setBalance(const MyMoneyMoney& balance) -{ - Q_D(Transaction); - d->m_balance = balance; -} - -const MyMoneyMoney& Transaction::balance() const -{ - Q_D(const Transaction); - return d->m_balance; -} - bool Transaction::paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) { - Q_D(Transaction); Q_UNUSED(painter) - if (d->m_reducedIntensity) { + if (m_reducedIntensity) { option.palette.setColor(QPalette::Text, option.palette.color(QPalette::Disabled, QPalette::Text)); } - if (d->m_selected) { + if (m_selected) { option.state |= QStyle::State_Selected; } else { option.state &= ~QStyle::State_Selected; } - if (d->m_focus) { + if (m_focus) { option.state |= QStyle::State_HasFocus; } else { option.state &= ~QStyle::State_HasFocus; } if (option.widget && option.widget->hasFocus()) { option.palette.setCurrentColorGroup(QPalette::Active); } else { option.palette.setCurrentColorGroup(QPalette::Inactive); } if (index.column() == 0) { option.viewItemPosition = QStyleOptionViewItem::Beginning; } else if (index.column() == MaxColumns - 1) { option.viewItemPosition = QStyleOptionViewItem::End; } else { option.viewItemPosition = QStyleOptionViewItem::Middle; } // do we need to switch to the error color? - if (d->m_erroneous) { + if (m_erroneous) { option.palette.setColor(QPalette::Text, KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionErroneous)); } // do we need to switch to the negative balance color? if (index.column() == BalanceColumn) { - bool showNegative = d->m_balance.isNegative(); - if (d->m_account.accountGroup() == eMyMoney::Account::Liability && !d->m_balance.isZero()) + bool showNegative = m_balance.isNegative(); + if (m_account.accountGroup() == eMyMoney::Account::Liability && !m_balance.isZero()) showNegative = !showNegative; if (showNegative) option.palette.setColor(QPalette::Text, KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionErroneous)); } return true; } void Transaction::registerCellText(QString& txt, int row, int col) { Qt::Alignment align; registerCellText(txt, align, row, col, 0); } void Transaction::paintRegisterCell(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index) { - Q_D(Transaction); painter->save(); if (paintRegisterCellSetup(painter, option, index)) { const QStyle *style = option.widget ? option.widget->style() : QApplication::style(); const QWidget* widget = option.widget; // clear the mouse over state before painting the background option.state &= ~QStyle::State_MouseOver; // the background if (option.state & QStyle::State_Selected || option.state & QStyle::State_HasFocus) { // if this is not the first row of the transaction paint the previous rows // since the selection background is painted from the first row of the transaction if (index.row() > startRow()) { QStyleOptionViewItem optionSibling = option; QModelIndex previousRowItem = index.sibling(index.row() - 1, index.column()); - optionSibling.rect = d->m_parent->visualRect(previousRowItem); + optionSibling.rect = m_parent->visualRect(previousRowItem); paintRegisterCell(painter, optionSibling, previousRowItem); } // paint the selection background only from the first row on to the last row at once if (index.row() == startRow()) { QRect old = option.rect; int extraHeight = 0; - if (d->m_inRegisterEdit) { + if (m_inRegisterEdit) { // since, when editing a transaction inside the register (without the transaction form), // row heights can have various sizes (the memo row is larger than the rest) we have // to iterate over all the items of the transaction to compute the size of the selection rectangle // of course we start with the item after this one because it's size is already in the rectangle for (int i = startRow() + 1; i < startRow() + numRowsRegister(); ++i) { - extraHeight += d->m_parent->visualRect(index.sibling(i, index.column())).height(); + extraHeight += m_parent->visualRect(index.sibling(i, index.column())).height(); } } else { // we are not editing in the register so all rows have the same sizes just compute the extra height extraHeight = (numRowsRegister() - 1) * option.rect.height(); } option.rect.setBottom(option.rect.bottom() + extraHeight); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, widget); - if (d->m_focus && index.column() == DetailColumn) { + if (m_focus && index.column() == DetailColumn) { option.state |= QStyle::State_HasFocus; style->drawPrimitive(QStyle::PE_FrameFocusRect, &option, painter, widget); } option.rect = old; } } else { - if (d->m_alternate) { + if (m_alternate) { painter->fillRect(option.rect, option.palette.alternateBase()); } else { painter->fillRect(option.rect, option.palette.base()); } } // the text // construct the text for the cell QString txt; option.displayAlignment = Qt::AlignVCenter; - if (d->m_transaction != MyMoneyTransaction() && !d->m_inRegisterEdit) { + if (m_transaction != MyMoneyTransaction() && !m_inRegisterEdit) { registerCellText(txt, option.displayAlignment, index.row() - startRow(), index.column(), painter); } if (Qt::mightBeRichText(txt)) { QTextDocument document; // this should set the alignment of the html but it does not work so htmls will be left aligned document.setDefaultTextOption(QTextOption(option.displayAlignment)); document.setDocumentMargin(2); document.setHtml(txt); painter->translate(option.rect.topLeft()); QAbstractTextDocumentLayout::PaintContext ctx; ctx.palette = option.palette; // Highlighting text if item is selected - if (d->m_selected) + if (m_selected) ctx.palette.setColor(QPalette::Text, option.palette.color(QPalette::HighlightedText)); document.documentLayout()->draw(painter, ctx); painter->translate(-option.rect.topLeft()); } else { // draw plain text properly aligned - style->drawItemText(painter, option.rect.adjusted(2, 0, -2, 0), option.displayAlignment, option.palette, true, txt, d->m_selected ? QPalette::HighlightedText : QPalette::Text); + style->drawItemText(painter, option.rect.adjusted(2, 0, -2, 0), option.displayAlignment, option.palette, true, txt, m_selected ? QPalette::HighlightedText : QPalette::Text); } // draw the grid if it's needed if (KMyMoneySettings::showGrid()) { const int gridHint = style->styleHint(QStyle::SH_Table_GridLineColor, &option, widget); const QPen gridPen = QPen(QColor(static_cast(gridHint)), 0); QPen old = painter->pen(); painter->setPen(gridPen); if (index.row() == startRow()) painter->drawLine(option.rect.topLeft(), option.rect.topRight()); painter->drawLine(option.rect.topLeft(), option.rect.bottomLeft()); painter->setPen(old); } // possible icons if (index.row() == startRow() && index.column() == DetailColumn) { - if (d->m_erroneous) { + if (m_erroneous) { QPixmap attention; attention.loadFromData(attentionSign, sizeof(attentionSign), 0, 0); style->drawItemPixmap(painter, option.rect, Qt::AlignRight | Qt::AlignVCenter, attention); } } } painter->restore(); } -bool Transaction::formCellText(QString& /* txt */, Qt::Alignment& /* align */, int /* row */, int /* col */, QPainter* /* painter */) -{ - return false; -} - -void Transaction::registerCellText(QString& /* txt */, Qt::Alignment& /* align */, int /* row */, int /* col */, QPainter* /* painter */) -{ -} - -int Transaction::registerColWidth(int /* col */, const QFontMetrics& /* cellFontMetrics */) -{ - return 0; -} - int Transaction::formRowHeight(int /*row*/) { - Q_D(Transaction); - if (d->m_formRowHeight < 0) { - d->m_formRowHeight = formRowHeight(); + if (m_formRowHeight < 0) { + m_formRowHeight = formRowHeight(); } - return d->m_formRowHeight; + return m_formRowHeight; } int Transaction::formRowHeight() const { - Q_D(const Transaction); - if (d->m_formRowHeight < 0) { + if (m_formRowHeight < 0) { // determine the height of the objects in the table kMyMoneyDateInput dateInput; - KMyMoneyCategory category(true, nullptr); + KMyMoneyCategory category(0, true); return qMax(dateInput.sizeHint().height(), category.sizeHint().height()); } - return d->m_formRowHeight; + return m_formRowHeight; } void Transaction::setupForm(TransactionForm* form) { - Q_D(Transaction); - d->m_form = form; + m_form = form; form->verticalHeader()->setUpdatesEnabled(false); form->horizontalHeader()->setUpdatesEnabled(false); form->setRowCount(numRowsForm()); form->setColumnCount(numColsForm()); // Force all cells to have some text (so that paintCell is called for each cell) for (int r = 0; r < numRowsForm(); ++r) { for (int c = 0; c < numColsForm(); ++c) { if (r == 0 && form->columnWidth(c) == 0) { form->setColumnWidth(c, 10); } } } form->horizontalHeader()->setUpdatesEnabled(true); form->verticalHeader()->setUpdatesEnabled(true); loadTab(form); } void Transaction::paintFormCell(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) { - Q_D(Transaction); - if (!d->m_form) + if (!m_form) return; QRect cellRect = option.rect; QRect textRect(cellRect); textRect.setWidth(textRect.width() - 2); textRect.setHeight(textRect.height() - 2); painter->setPen(option.palette.text().color()); QString txt; Qt::Alignment align = Qt::AlignVCenter; bool editField = formCellText(txt, align, index.row(), index.column(), painter); // if we have an editable field and don't currently edit the transaction // show the background in a different color - if (editField && !d->m_inEdit) { + if (editField && !m_inEdit) { painter->fillRect(textRect, option.palette.alternateBase()); } - if (!d->m_inEdit) + if (!m_inEdit) painter->drawText(textRect, align, txt); } void Transaction::setupPalette(const QPalette& palette, QMap& editWidgets) { QMap::iterator it_w; for (it_w = editWidgets.begin(); it_w != editWidgets.end(); ++it_w) { if (*it_w) { (*it_w)->setPalette(palette); } } } void Transaction::setupFormPalette(QMap& editWidgets) { - Q_D(Transaction); - QPalette palette = d->m_parent->palette(); + QPalette palette = m_parent->palette(); palette.setColor(QPalette::Active, QPalette::Base, palette.color(QPalette::Active, QPalette::Base)); setupPalette(palette, editWidgets); } void Transaction::setupRegisterPalette(QMap& editWidgets) { - Q_D(Transaction); // make sure, we're using the right palette - QPalette palette = d->m_parent->palette(); + QPalette palette = m_parent->palette(); // use the highlight color as background palette.setColor(QPalette::Active, QPalette::Background, palette.color(QPalette::Active, QPalette::Highlight)); setupPalette(palette, editWidgets); } QWidget* Transaction::focusWidget(QWidget* w) const { if (w) { while (w->focusProxy()) w = w->focusProxy(); } return w; } void Transaction::arrangeWidget(QTableWidget* tbl, int row, int col, QWidget* w) const { if (w) { tbl->setCellWidget(row, col, w); // remove the widget from the QTable's eventFilter so that all // events will be directed to the edit widget w->removeEventFilter(tbl); } } bool Transaction::haveNumberField() const { - Q_D(const Transaction); - auto rc = true; - switch (d->m_account.accountType()) { + bool rc = true; + switch (m_account.accountType()) { case eMyMoney::Account::Savings: case eMyMoney::Account::Cash: case eMyMoney::Account::Loan: case eMyMoney::Account::AssetLoan: case eMyMoney::Account::Asset: case eMyMoney::Account::Liability: case eMyMoney::Account::Equity: rc = KMyMoneyGlobalSettings::alwaysShowNrField(); break; case eMyMoney::Account::Checkings: case eMyMoney::Account::CreditCard: // the next case is used for the editor when the account // is unknown (eg. when creating new schedules) case eMyMoney::Account::Unknown: break; default: rc = false; break; } return rc; } bool Transaction::maybeTip(const QPoint& cpos, int row, int col, QRect& r, QString& msg) { - Q_D(Transaction); if (col != DetailColumn) return false; - if (!d->m_erroneous && d->m_transaction.splitCount() < 3) + if (!m_erroneous && m_transaction.splitCount() < 3) return false; // check for detail column in row 0 of the transaction for a possible // exclamation mark. m_startRow is based 0, whereas the row to obtain // the modelindex is based 1, so we need to add one here - r = d->m_parent->visualRect(d->m_parent->model()->index(d->m_startRow + 1, col)); + r = m_parent->visualRect(m_parent->model()->index(m_startRow + 1, col)); r.setBottom(r.bottom() + (numRowsRegister() - 1)*r.height()); - if (r.contains(cpos) && d->m_erroneous) { - if (d->m_transaction.splits().count() < 2) { + if (r.contains(cpos) && m_erroneous) { + if (m_transaction.splits().count() < 2) { msg = QString("%1").arg(i18n("Transaction is missing a category assignment.")); } else { - const MyMoneySecurity& sec = MyMoneyFile::instance()->security(d->m_account.currencyId()); - msg = QString("%1").arg(i18n("The transaction has a missing assignment of %1.", MyMoneyUtils::formatMoney(d->m_transaction.splitSum().abs(), d->m_account, sec))); + const MyMoneySecurity& sec = MyMoneyFile::instance()->security(m_account.currencyId()); + msg = QString("%1").arg(i18n("The transaction has a missing assignment of %1.", MyMoneyUtils::formatMoney(m_transaction.splitSum().abs(), m_account, sec))); } return true; } // check if the mouse cursor is located on row 1 of the transaction // and display the details of a split transaction if it is one - if (row == 1 && r.contains(cpos) && d->m_transaction.splitCount() > 2) { - auto file = MyMoneyFile::instance(); + if (row == 1 && r.contains(cpos) && m_transaction.splitCount() > 2) { + MyMoneyFile* file = MyMoneyFile::instance(); QList::const_iterator it_s; QString txt; - const MyMoneySecurity& sec = file->security(d->m_transaction.commodity()); + const MyMoneySecurity& sec = file->security(m_transaction.commodity()); MyMoneyMoney factor(1, 1); - if (!d->m_split.value().isNegative()) + if (!m_split.value().isNegative()) factor = -factor; - for (it_s = d->m_transaction.splits().constBegin(); it_s != d->m_transaction.splits().constEnd(); ++it_s) { - if (*it_s == d->m_split) + for (it_s = m_transaction.splits().constBegin(); it_s != m_transaction.splits().constEnd(); ++it_s) { + if (*it_s == m_split) continue; const MyMoneyAccount& acc = file->account((*it_s).accountId()); QString category = file->accountToCategory(acc.id()); QString amount = MyMoneyUtils::formatMoney(((*it_s).value() * factor), acc, sec); txt += QString("%1%2").arg(category, amount); } msg = QString("%1
").arg(txt); return true; } return false; } QString Transaction::reconcileState(bool text) const { - Q_D(const Transaction); - auto txt = KMyMoneyUtils::reconcileStateToString(d->m_split.reconcileFlag(), text); + QString txt = KMyMoneyUtils::reconcileStateToString(m_split.reconcileFlag(), text); if ((text == true) && (txt == i18nc("Unknown reconciliation state", "Unknown")) - && (d->m_transaction == MyMoneyTransaction())) + && (m_transaction == MyMoneyTransaction())) txt.clear(); return txt; } void Transaction::startEditMode() { - Q_D(Transaction); - d->m_inEdit = true; + m_inEdit = true; // hide the original tabbar since the edit tabbar will be added - KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast(d->m_form); + KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast(m_form); form->tabBar()->setVisible(false); // only update the number of lines displayed if we edit inside the register - if (d->m_inRegisterEdit) + if (m_inRegisterEdit) setNumRowsRegister(numRowsRegister(true)); } -int Transaction::numRowsRegister() const -{ - return RegisterItem::numRowsRegister(); -} - void Transaction::leaveEditMode() { - Q_D(Transaction); // show the original tabbar since the edit tabbar was removed - KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast(d->m_form); + KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast(m_form); form->tabBar()->setVisible(true); // make sure we reset the row height of all the transaction's rows because it could have been changed during edit - if (d->m_parent) { - for (auto i = 0; i < numRowsRegister(); ++i) - d->m_parent->setRowHeight(d->m_startRow + i, d->m_parent->rowHeightHint()); + if (m_parent) { + for (int i = 0; i < numRowsRegister(); ++i) + m_parent->setRowHeight(m_startRow + i, m_parent->rowHeightHint()); } - d->m_inEdit = false; - d->m_inRegisterEdit = false; + m_inEdit = false; + m_inRegisterEdit = false; setFocus(hasFocus(), true); } void Transaction::singleLineMemo(QString& txt, const MyMoneySplit& split) const { txt = split.memo(); // remove empty lines txt.replace("\n\n", "\n"); // replace '\n' with ", " txt.replace('\n', ", "); } int Transaction::rowHeightHint() const { - Q_D(const Transaction); - return d->m_inEdit ? formRowHeight() : RegisterItem::rowHeightHint(); + return m_inEdit ? formRowHeight() : RegisterItem::rowHeightHint(); } bool Transaction::matches(const RegisterFilter& filter) const { - Q_D(const Transaction); // check if the state matches if (!transaction().id().isEmpty()) { switch (filter.state) { default: break; case RegisterFilter::Imported: if (!transaction().isImported()) return false; break; case RegisterFilter::Matched: if (!split().isMatched()) return false; break; case RegisterFilter::Erroneous: if (transaction().splitSum().isZero()) return false; break; case RegisterFilter::NotMarked: if (split().reconcileFlag() != eMyMoney::Split::State::NotReconciled) return false; break; case RegisterFilter::NotReconciled: if (split().reconcileFlag() != eMyMoney::Split::State::NotReconciled && split().reconcileFlag() != eMyMoney::Split::State::Cleared) return false; break; case RegisterFilter::Cleared: if (split().reconcileFlag() != eMyMoney::Split::State::Cleared) return false; break; } } // check if the text matches - if (filter.text.isEmpty() || d->m_transaction.splitCount() == 0) + if (filter.text.isEmpty() || m_transaction.splitCount() == 0) return true; - auto file = MyMoneyFile::instance(); + MyMoneyFile* file = MyMoneyFile::instance(); - const QList&list = d->m_transaction.splits(); + const QList&list = m_transaction.splits(); QList::const_iterator it_s; for (it_s = list.begin(); it_s != list.end(); ++it_s) { // check if the text is contained in one of the fields // memo, number, payee, tag, account if ((*it_s).memo().contains(filter.text, Qt::CaseInsensitive) || (*it_s).number().contains(filter.text, Qt::CaseInsensitive)) return true; if (!(*it_s).payeeId().isEmpty()) { const MyMoneyPayee& payee = file->payee((*it_s).payeeId()); if (payee.name().contains(filter.text, Qt::CaseInsensitive)) return true; } if (!(*it_s).tagIdList().isEmpty()) { const QList& t = (*it_s).tagIdList(); - for (auto i = 0; i < t.count(); i++) { + for (int i = 0; i < t.count(); i++) { if ((file->tag(t[i])).name().contains(filter.text, Qt::CaseInsensitive)) return true; } } const MyMoneyAccount& acc = file->account((*it_s).accountId()); if (acc.name().contains(filter.text, Qt::CaseInsensitive)) return true; QString s(filter.text); s.replace(MyMoneyMoney::thousandSeparator(), QChar()); if (!s.isEmpty()) { // check if any of the value field matches if a value has been entered - QString r = (*it_s).value().formatMoney(d->m_account.fraction(), false); + QString r = (*it_s).value().formatMoney(m_account.fraction(), false); if (r.contains(s, Qt::CaseInsensitive)) return true; const MyMoneyAccount& acc = file->account((*it_s).accountId()); r = (*it_s).shares().formatMoney(acc.fraction(), false); if (r.contains(s, Qt::CaseInsensitive)) return true; } } return false; } void Transaction::setShowBalance(bool showBalance) { - Q_D(Transaction); - d->m_showBalance = showBalance; -} - -bool Transaction::showRowInForm(int row) const -{ - Q_UNUSED(row) return true; -} - -void Transaction::setShowRowInForm(int row, bool show) -{ - Q_UNUSED(row); Q_UNUSED(show) -} - -void Transaction::setReducedIntensity(bool reduced) -{ - Q_D(Transaction); - d->m_reducedIntensity = reduced; + m_showBalance = showBalance; } void Transaction::setVisible(bool visible) { - Q_D(Transaction); if (visible != isVisible()) { RegisterItem::setVisible(visible); RegisterItem* p; Transaction* t; if (!visible) { // if we are hidden, we need to inform all previous transactions // about it so that they don't show the balance p = prevItem(); while (p) { t = dynamic_cast(p); if (t) { - if (!t->d_func()->m_showBalance) + if (!t->m_showBalance) break; - t->d_func()->m_showBalance = false; + t->m_showBalance = false; } p = p->prevItem(); } } else { // if we are shown, we need to check if the next transaction // is visible and change the display of the balance p = this; do { p = p->nextItem(); t = dynamic_cast(p); } while (!t && p); // if the next transaction is visible or I am the last one - if ((t && t->d_func()->m_showBalance) || !t) { - d->m_showBalance = true; + if ((t && t->m_showBalance) || !t) { + m_showBalance = true; p = prevItem(); while (p && p->isVisible()) { t = dynamic_cast(p); if (t) { - if (t->d_func()->m_showBalance) + if (t->m_showBalance) break; - t->d_func()->m_showBalance = true; + t->m_showBalance = true; } p = p->prevItem(); } } } } } +void Transaction::setSelected(bool selected) +{ + if (!selected || (selected && isVisible())) + m_selected = selected; +} + +StdTransaction::StdTransaction(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : + Transaction(parent, transaction, split, uniqueId), + m_showAccountRow(false) +{ + try { + m_categoryHeader = i18n("Category"); + switch (transaction.splitCount()) { + default: + m_category = i18nc("Split transaction (category replacement)", "Split transaction"); + break; + + case 0: // the empty transaction + case 1: + break; + + case 2: + setupFormHeader(m_transaction.splitByAccount(m_split.accountId(), false).accountId()); + break; + } + } catch (const MyMoneyException &e) { + qDebug() << "Problem determining the category for transaction '" << m_transaction.id() << "'. Reason: " << e.what() << "\n"; + } + m_rowsForm = 6; + + if (KMyMoneyUtils::transactionType(m_transaction) == KMyMoneyUtils::InvestmentTransaction) { + MyMoneySplit split = KMyMoneyUtils::stockSplit(m_transaction); + m_payee = MyMoneyFile::instance()->account(split.accountId()).name(); + QString addon; + if (split.action() == MyMoneySplit::ActionBuyShares) { + if (split.value().isNegative()) { + addon = i18n("Sell"); + } else { + addon = i18n("Buy"); + } + } else if (split.action() == MyMoneySplit::ActionDividend) { + addon = i18n("Dividend"); + } else if (split.action() == MyMoneySplit::ActionYield) { + addon = i18n("Yield"); + } else if (split.action() == MyMoneySplit::ActionInterestIncome) { + addon = i18n("Interest Income"); + } + if (!addon.isEmpty()) { + m_payee += QString(" (%1)").arg(addon); + } + m_payeeHeader = i18n("Activity"); + m_category = i18n("Investment transaction"); + } + + // setup initial size + setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); + + emit parent->itemAdded(this); +} + +void StdTransaction::setupFormHeader(const QString& id) +{ + m_category = MyMoneyFile::instance()->accountToCategory(id); + switch (MyMoneyFile::instance()->account(id).accountGroup()) { + case eMyMoney::Account::Asset: + case eMyMoney::Account::Liability: + m_categoryHeader = m_split.shares().isNegative() ? i18n("Transfer to") : i18n("Transfer from"); + break; + + default: + m_categoryHeader = i18n("Category"); + break; + } +} + +KMyMoneyRegister::Action StdTransaction::actionType() const +{ + KMyMoneyRegister::Action action = ActionNone; + + // if at least one split is referencing an income or + // expense account, we will not call it a transfer + QList::const_iterator it_s; + + for (it_s = m_transaction.splits().begin(); it_s != m_transaction.splits().end(); ++it_s) { + if ((*it_s).accountId() == m_split.accountId()) + continue; + MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId()); + if (acc.accountGroup() == eMyMoney::Account::Income + || acc.accountGroup() == eMyMoney::Account::Expense) { + // otherwise, we have to determine between deposit and withdrawal + action = m_split.shares().isNegative() ? ActionWithdrawal : ActionDeposit; + break; + } + } + // otherwise, it's a transfer + if (it_s == m_transaction.splits().end()) + action = ActionTransfer; + + return action; +} + +void StdTransaction::loadTab(TransactionForm* form) +{ + TabBar* bar = form->tabBar(); + bar->setSignalEmission(TabBar::SignalNever); + for (int i = 0; i < bar->count(); ++i) { + bar->setTabEnabled(i, true); + } + + if (m_transaction.splitCount() > 0) { + bar->setCurrentIndex(actionType()); + } + bar->setSignalEmission(TabBar::SignalAlways); +} + +void StdTransaction::setupForm(TransactionForm* form) +{ + Transaction::setupForm(form); + form->setSpan(4, ValueColumn1, 3, 1); +} + +bool StdTransaction::showRowInForm(int row) const +{ + return row == 0 ? m_showAccountRow : true; +} + +void StdTransaction::setShowRowInForm(int row, bool show) +{ + if (row == 0) + m_showAccountRow = show; +} + +bool StdTransaction::formCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* /* painter */) +{ + // if(m_transaction != MyMoneyTransaction()) { + switch (row) { + case 0: + switch (col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Account"); + break; + } + break; + + case 1: + switch (col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = m_payeeHeader; + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + txt = m_payee; + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if (haveNumberField()) + txt = i18n("Number"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if (haveNumberField()) + txt = m_split.number(); + break; + } + break; + + case 2: + switch (col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = m_categoryHeader; + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + txt = m_category; + if (m_transaction != MyMoneyTransaction()) { + if (txt.isEmpty() && !m_split.value().isZero()) + txt = i18n("*** UNASSIGNED ***"); + } + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Date"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if (m_transaction != MyMoneyTransaction()) + txt = QLocale().toString(m_transaction.postDate(), QLocale::ShortFormat); + break; + } + break; + + case 3: + switch (col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Tags"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + if (!m_tagList.isEmpty()) { + for (int i = 0; i < m_tagList.size() - 1; i++) + txt += m_tagList[i] + ", "; + txt += m_tagList.last(); + } + //if (m_transaction != MyMoneyTransaction()) + // txt = m_split.tagId(); + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Amount"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if (m_transaction != MyMoneyTransaction()) { + txt = (m_split.value(m_transaction.commodity(), m_splitCurrencyId).abs()).formatMoney(m_account.fraction()); + } + break; + } + break; + + case 4: + switch (col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Memo"); + break; + + case ValueColumn1: + align &= ~Qt::AlignVCenter; + align |= Qt::AlignTop; + align |= Qt::AlignLeft; + if (m_transaction != MyMoneyTransaction()) + txt = m_split.memo().section('\n', 0, 2); + break; + } + break; + + case 5: + switch (col) { + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Status"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + txt = reconcileState(); + break; + } + } + + // } + if (col == ValueColumn2 && row == 1) { + return haveNumberField(); + } + return (col == ValueColumn1 && row < 5) || (col == ValueColumn2 && row > 0 && row != 4); +} + +void StdTransaction::registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter) +{ + switch (row) { + case 0: + switch (col) { + case NumberColumn: + align |= Qt::AlignLeft; + if (haveNumberField()) + txt = m_split.number(); + break; + + case DateColumn: + align |= Qt::AlignLeft; + txt = QLocale().toString(m_transaction.postDate(), QLocale::ShortFormat); + break; + + case DetailColumn: + switch (m_parent->getDetailsColumnType()) { + case PayeeFirst: + txt = m_payee; + break; + case AccountFirst: + txt = m_category; + if (!m_tagList.isEmpty()) { + txt += " ( "; + for (int i = 0; i < m_tagList.size() - 1; i++) { + txt += " " + m_tagList[i] + ", "; + } + txt += " " + m_tagList.last() + " )"; + } + break; + } + align |= Qt::AlignLeft; + if (txt.isEmpty() && m_rowsRegister < 3) { + singleLineMemo(txt, m_split); + } + if (txt.isEmpty() && m_rowsRegister < 2) { + if (m_account.accountType() != eMyMoney::Account::Income + && m_account.accountType() != eMyMoney::Account::Expense) { + txt = m_category; + if (txt.isEmpty() && !m_split.value().isZero()) { + txt = i18n("*** UNASSIGNED ***"); + if (painter) + painter->setPen(KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionErroneous)); + } + } + } + break; + + case ReconcileFlagColumn: + align |= Qt::AlignHCenter; + txt = reconcileState(false); + break; + + case PaymentColumn: + align |= Qt::AlignRight; + if (m_split.value().isNegative()) { + txt = (-m_split.value(m_transaction.commodity(), m_splitCurrencyId)).formatMoney(m_account.fraction()); + } + break; + + case DepositColumn: + align |= Qt::AlignRight; + if (!m_split.value().isNegative()) { + txt = m_split.value(m_transaction.commodity(), m_splitCurrencyId).formatMoney(m_account.fraction()); + } + break; + + case BalanceColumn: + align |= Qt::AlignRight; + if (m_showBalance) + txt = m_balance.formatMoney(m_account.fraction()); + else + txt = "----"; + break; + + case AccountColumn: + // txt = m_objects->account(m_transaction.splits()[0].accountId()).name(); + txt = MyMoneyFile::instance()->account(m_split.accountId()).name(); + break; + + default: + break; + } + break; + + case 1: + switch (col) { + case DetailColumn: + switch (m_parent->getDetailsColumnType()) { + case PayeeFirst: + txt = m_category; + if (!m_tagList.isEmpty()) { + txt += " ( "; + for (int i = 0; i < m_tagList.size() - 1; i++) { + txt += " " + m_tagList[i] + ", "; + } + txt += " " + m_tagList.last() + " )"; + } + break; + case AccountFirst: + txt = m_payee; + break; + } + align |= Qt::AlignLeft; + if (txt.isEmpty() && !m_split.value().isZero()) { + txt = i18n("*** UNASSIGNED ***"); + if (painter) + painter->setPen(KMyMoneyGlobalSettings::schemeColor(SchemeColor::TransactionErroneous)); + } + break; + + default: + break; + } + break; + + case 2: + switch (col) { + case DetailColumn: + align |= Qt::AlignLeft; + singleLineMemo(txt, m_split); + break; + + default: + break; + } + break; + } +} + +int StdTransaction::registerColWidth(int col, const QFontMetrics& cellFontMetrics) +{ + QString txt; + int firstRow = 0, lastRow = numRowsRegister(); + + int nw = 0; + for (int i = firstRow; i <= lastRow; ++i) { + Qt::Alignment align; + registerCellText(txt, align, i, col, 0); + int w = cellFontMetrics.width(txt + " "); + if (w > nw) + nw = w; + } + return nw; +} + +void StdTransaction::arrangeWidgetsInForm(QMap& editWidgets) +{ + if (!m_form || !m_parent) + return; + + setupFormPalette(editWidgets); + + arrangeWidget(m_form, 0, LabelColumn1, editWidgets["account-label"]); + arrangeWidget(m_form, 0, ValueColumn1, editWidgets["account"]); + arrangeWidget(m_form, 1, LabelColumn1, editWidgets["cashflow"]); + arrangeWidget(m_form, 1, ValueColumn1, editWidgets["payee"]); + arrangeWidget(m_form, 2, LabelColumn1, editWidgets["category-label"]); + arrangeWidget(m_form, 2, ValueColumn1, editWidgets["category"]->parentWidget()); + arrangeWidget(m_form, 3, LabelColumn1, editWidgets["tag-label"]); + arrangeWidget(m_form, 3, ValueColumn1, editWidgets["tag"]); + arrangeWidget(m_form, 4, LabelColumn1, editWidgets["memo-label"]); + arrangeWidget(m_form, 4, ValueColumn1, editWidgets["memo"]); + if (haveNumberField()) { + arrangeWidget(m_form, 1, LabelColumn2, editWidgets["number-label"]); + arrangeWidget(m_form, 1, ValueColumn2, editWidgets["number"]); + } + arrangeWidget(m_form, 2, LabelColumn2, editWidgets["date-label"]); + arrangeWidget(m_form, 2, ValueColumn2, editWidgets["postdate"]); + arrangeWidget(m_form, 3, LabelColumn2, editWidgets["amount-label"]); + arrangeWidget(m_form, 3, ValueColumn2, editWidgets["amount"]); + arrangeWidget(m_form, 5, LabelColumn2, editWidgets["status-label"]); + arrangeWidget(m_form, 5, ValueColumn2, editWidgets["status"]); + + // get rid of the hints. we don't need them for the form + QMap::iterator it; + for (it = editWidgets.begin(); it != editWidgets.end(); ++it) { + KMyMoneyCombo* combo = dynamic_cast(*it); + kMyMoneyLineEdit* edit = dynamic_cast(*it); + KMyMoneyPayeeCombo* payee = dynamic_cast(*it); + KTagContainer* tag = dynamic_cast(*it); + if (combo) + combo->setPlaceholderText(QString()); + if (edit) + edit->setPlaceholderText(QString()); + if (payee) + payee->setPlaceholderText(QString()); + if (tag) + tag->tagCombo()->setPlaceholderText(QString()); + } + + KMyMoneyTransactionForm::TransactionForm* form = dynamic_cast(m_form); + TabBar* w = dynamic_cast(editWidgets["tabbar"]); + if (w) { + // insert the tabbar in the boxlayout so it will take the place of the original tabbar which was hidden + QBoxLayout* boxLayout = dynamic_cast(form->tabBar()->parentWidget()->layout()); + boxLayout->insertWidget(0, w); + } +} + +void StdTransaction::tabOrderInForm(QWidgetList& tabOrderWidgets) const +{ + QStringList taborder = KMyMoneyGlobalSettings::stdTransactionFormTabOrder().split(',', QString::SkipEmptyParts); + QStringList::const_iterator it_s = taborder.constBegin(); + QWidget* w; + while (it_s != taborder.constEnd()) { + if (*it_s == "account") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(0, ValueColumn1))); + } else if (*it_s == "cashflow") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(1, LabelColumn1))); + } else if (*it_s == "payee") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(1, ValueColumn1))); + } else if (*it_s == "category") { + // make sure to have the category field and the split button as separate tab order widgets + // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but + // it's one of our own widgets, so we actually don't care. Just make sure, that we don't + // go haywire when someone changes the KMyMoneyCategory object ... + QWidget* w = m_form->cellWidget(2, ValueColumn1); + tabOrderWidgets.append(focusWidget(w)); + w = w->findChild("splitButton"); + if (w) + tabOrderWidgets.append(w); + } else if (*it_s == "tag") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(3, ValueColumn1))); + } else if (*it_s == "memo") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(4, ValueColumn1))); + } else if (*it_s == "number") { + if (haveNumberField()) { + if ((w = focusWidget(m_form->cellWidget(1, ValueColumn2)))) + tabOrderWidgets.append(w); + } + } else if (*it_s == "date") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(2, ValueColumn2))); + } else if (*it_s == "amount") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(3, ValueColumn2))); + } else if (*it_s == "state") { + tabOrderWidgets.append(focusWidget(m_form->cellWidget(5, ValueColumn2))); + } + ++it_s; + } +} + +void StdTransaction::arrangeWidgetsInRegister(QMap& editWidgets) +{ + if (!m_parent) + return; + + setupRegisterPalette(editWidgets); + + if (haveNumberField()) + arrangeWidget(m_parent, m_startRow + 0, NumberColumn, editWidgets["number"]); + arrangeWidget(m_parent, m_startRow + 0, DateColumn, editWidgets["postdate"]); + arrangeWidget(m_parent, m_startRow + 1, DateColumn, editWidgets["status"]); + arrangeWidget(m_parent, m_startRow + 0, DetailColumn, editWidgets["payee"]); + arrangeWidget(m_parent, m_startRow + 1, DetailColumn, editWidgets["category"]->parentWidget()); + arrangeWidget(m_parent, m_startRow + 2, DetailColumn, editWidgets["tag"]); + arrangeWidget(m_parent, m_startRow + 3, DetailColumn, editWidgets["memo"]); + arrangeWidget(m_parent, m_startRow + 0, PaymentColumn, editWidgets["payment"]); + arrangeWidget(m_parent, m_startRow + 0, DepositColumn, editWidgets["deposit"]); + + // increase the height of the row containing the memo widget + m_parent->setRowHeight(m_startRow + 3, m_parent->rowHeightHint() * 3); +} + +void StdTransaction::tabOrderInRegister(QWidgetList& tabOrderWidgets) const +{ + QStringList taborder = KMyMoneyGlobalSettings::stdTransactionRegisterTabOrder().split(',', QString::SkipEmptyParts); + QStringList::const_iterator it_s = taborder.constBegin(); + QWidget* w; + while (it_s != taborder.constEnd()) { + if (*it_s == "number") { + if (haveNumberField()) { + if ((w = focusWidget(m_parent->cellWidget(m_startRow + 0, NumberColumn)))) + tabOrderWidgets.append(w); + } + } else if (*it_s == "date") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DateColumn))); + } else if (*it_s == "payee") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DetailColumn))); + } else if (*it_s == "category") { + // make sure to have the category field and the split button as separate tab order widgets + // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but + // it's one of our own widgets, so we actually don't care. Just make sure, that we don't + // go haywire when someone changes the KMyMoneyCategory object ... + w = m_parent->cellWidget(m_startRow + 1, DetailColumn); + tabOrderWidgets.append(focusWidget(w)); + w = w->findChild("splitButton"); + if (w) + tabOrderWidgets.append(w); + } else if (*it_s == "tag") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 2, DetailColumn))); + } else if (*it_s == "memo") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 3, DetailColumn))); + } else if (*it_s == "payment") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, PaymentColumn))); + } else if (*it_s == "deposit") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DepositColumn))); + } else if (*it_s == "state") { + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 1, DateColumn))); + } + ++it_s; + } +} + +int StdTransaction::numRowsRegister(bool expanded) const +{ + int numRows = 1; + if (expanded) { + numRows = 4; + if (!m_inEdit) { + //When not in edit Tags haven't a separate row; + numRows--; + if (m_payee.isEmpty()) { + numRows--; + } + if (m_split.memo().isEmpty()) { + numRows--; + } + // For income and expense accounts that only have + // two splits we only show one line, because the + // account name is already contained in the account column. + if (m_account.accountType() == eMyMoney::Account::Income + || m_account.accountType() == eMyMoney::Account::Expense) { + if (numRows > 2 && m_transaction.splitCount() == 2) + numRows = 1; + } + } + } + return numRows; +} + +TransactionEditor* StdTransaction::createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) +{ +#ifndef KMM_DESIGNER + m_inRegisterEdit = regForm == m_parent; + return new StdTransactionEditor(regForm, this, list, lastPostDate); +#else + return NULL; +#endif +} + +InvestTransaction::InvestTransaction(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : + Transaction(parent, transaction, split, uniqueId) +{ +#ifndef KMM_DESIGNER + // dissect the transaction into its type, splits, currency, security etc. + KMyMoneyUtils::dissectTransaction(m_transaction, m_split, + m_assetAccountSplit, + m_feeSplits, + m_interestSplits, + m_security, + m_currency, + m_transactionType); +#endif + + QList::ConstIterator it_s; + for (it_s = m_feeSplits.constBegin(); it_s != m_feeSplits.constEnd(); ++it_s) { + m_feeAmount += (*it_s).value(); + } + for (it_s = m_interestSplits.constBegin(); it_s != m_interestSplits.constEnd(); ++it_s) { + m_interestAmount += (*it_s).value(); + } + + // check the count of the fee splits and setup the text + switch (m_feeSplits.count()) { + case 0: + break; + + case 1: + m_feeCategory = MyMoneyFile::instance()->accountToCategory(m_feeSplits[0].accountId()); + break; + + default: + m_feeCategory = i18nc("Split transaction (category replacement)", "Split transaction"); + break; + } + + // check the count of the interest splits and setup the text + switch (m_interestSplits.count()) { + case 0: + break; + + case 1: + m_interestCategory = MyMoneyFile::instance()->accountToCategory(m_interestSplits[0].accountId()); + break; + + default: + m_interestCategory = i18nc("Split transaction (category replacement)", "Split transaction"); + break; + } + + m_rowsForm = 7; + + // setup initial size + setNumRowsRegister(numRowsRegister(KMyMoneyGlobalSettings::showRegisterDetailed())); + + emit parent->itemAdded(this); +} + +void InvestTransaction::setupForm(TransactionForm* form) +{ + Transaction::setupForm(form); + form->setSpan(5, 1, 2, 1); +} + +void InvestTransaction::activity(QString& txt, eMyMoney::Split::InvestmentTransactionType type) const +{ + switch (type) { + case eMyMoney::Split::InvestmentTransactionType::AddShares: + txt = i18n("Add shares"); + break; + case eMyMoney::Split::InvestmentTransactionType::RemoveShares: + txt = i18n("Remove shares"); + break; + case eMyMoney::Split::InvestmentTransactionType::BuyShares: + txt = i18n("Buy shares"); + break; + case eMyMoney::Split::InvestmentTransactionType::SellShares: + txt = i18n("Sell shares"); + break; + case eMyMoney::Split::InvestmentTransactionType::Dividend: + txt = i18n("Dividend"); + break; + case eMyMoney::Split::InvestmentTransactionType::ReinvestDividend: + txt = i18n("Reinvest Dividend"); + break; + case eMyMoney::Split::InvestmentTransactionType::Yield: + txt = i18n("Yield"); + break; + case eMyMoney::Split::InvestmentTransactionType::SplitShares: + txt = i18n("Split shares"); + break; + case eMyMoney::Split::InvestmentTransactionType::InterestIncome: + txt = i18n("Interest Income"); + break; + default: + txt = i18nc("Unknown investment activity", "Unknown"); + break; + } +} + +bool InvestTransaction::formCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* /* painter */) +{ + bool fieldEditable = false; + + switch (row) { + case 0: + switch (col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Activity"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + fieldEditable = true; + activity(txt, m_transactionType); + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Date"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + fieldEditable = true; + if (m_transaction != MyMoneyTransaction()) + txt = QLocale().toString(m_transaction.postDate(), QLocale::ShortFormat); + break; + } + break; + + case 1: + switch (col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Security"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + fieldEditable = true; + if (m_account.isInvest()) + txt = m_security.name(); + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if (haveShares()) { + txt = i18n("Shares"); + } else if (haveSplitRatio()) { + txt = i18n("Ratio"); + } + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if ((fieldEditable = haveShares()) == true) { + txt = m_split.shares().abs().formatMoney("", MyMoneyMoney::denomToPrec(m_security.smallestAccountFraction())); + } else if (haveSplitRatio()) { + txt = QString("1 / %1").arg(m_split.shares().abs().formatMoney("", -1)); + } + break; + } + break; + + case 2: + switch (col) { + case LabelColumn1: + align |= Qt::AlignLeft; + if (haveAssetAccount()) + txt = i18n("Account"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + if ((fieldEditable = haveAssetAccount()) == true) { + txt = MyMoneyFile::instance()->accountToCategory(m_assetAccountSplit.accountId()); + } + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if (havePrice()) + txt = i18n("Price/share"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if ((fieldEditable = havePrice()) == true && !m_split.shares().isZero()) { + txt = m_split.price().formatMoney("", m_security.pricePrecision()); + } + break; + } + break; + + case 3: + switch (col) { + case LabelColumn1: + align |= Qt::AlignLeft; + if (haveFees()) + txt = i18n("Fees"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + if ((fieldEditable = haveFees()) == true) { + txt = m_feeCategory; + } + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if (haveFees() && !m_feeCategory.isEmpty()) + txt = i18n("Fee Amount"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if (haveFees()) { + if ((fieldEditable = !m_feeCategory.isEmpty()) == true) { + txt = MyMoneyUtils::formatMoney(m_feeAmount, m_currency); + } + } + break; + } + break; + + case 4: + switch (col) { + case LabelColumn1: + align |= Qt::AlignLeft; + if (haveInterest()) + txt = i18n("Interest"); + break; + + case ValueColumn1: + align |= Qt::AlignLeft; + if ((fieldEditable = haveInterest()) == true) { + txt = m_interestCategory; + } + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if (haveInterest() && !m_interestCategory.isEmpty()) + txt = i18n("Interest"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if (haveInterest()) { + if ((fieldEditable = !m_interestCategory.isEmpty()) == true) { + txt = MyMoneyUtils::formatMoney(-m_interestAmount, m_currency); + } + } + break; + } + break; + + case 5: + switch (col) { + case LabelColumn1: + align |= Qt::AlignLeft; + txt = i18n("Memo"); + break; + + case ValueColumn1: + align &= ~Qt::AlignVCenter; + align |= Qt::AlignTop; + align |= Qt::AlignLeft; + fieldEditable = true; + if (m_transaction != MyMoneyTransaction()) + txt = m_split.memo().section('\n', 0, 2); + break; + + case LabelColumn2: + align |= Qt::AlignLeft; + if (haveAmount()) + txt = i18nc("Total balance", "Total"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + if ((fieldEditable = haveAmount()) == true) { + txt = m_assetAccountSplit.value().abs() + .formatMoney(m_currency.tradingSymbol(), MyMoneyMoney::denomToPrec(m_currency.smallestAccountFraction())); + } + } + break; + + case 6: + switch (col) { + case LabelColumn2: + align |= Qt::AlignLeft; + txt = i18n("Status"); + break; + + case ValueColumn2: + align |= Qt::AlignRight; + fieldEditable = true; + txt = reconcileState(); + break; + } + } + + return fieldEditable; +} + +void InvestTransaction::registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* /* painter */) +{ + switch (row) { + case 0: + switch (col) { + case DateColumn: + align |= Qt::AlignLeft; + txt = QLocale().toString(m_transaction.postDate(), QLocale::ShortFormat); + break; + + case DetailColumn: + align |= Qt::AlignLeft; + activity(txt, m_transactionType); + break; + + case SecurityColumn: + align |= Qt::AlignLeft; + if (m_account.isInvest()) + txt = m_security.name(); + break; + + case ReconcileFlagColumn: + align |= Qt::AlignHCenter; + txt = reconcileState(false); + break; + + case QuantityColumn: + align |= Qt::AlignRight; + if (haveShares()) + txt = m_split.shares().abs().formatMoney("", MyMoneyMoney::denomToPrec(m_security.smallestAccountFraction())); + else if (haveSplitRatio()) { + txt = QString("1 / %1").arg(m_split.shares().abs().formatMoney("", -1)); + } + break; + + case PriceColumn: + align |= Qt::AlignRight; + if (havePrice() && !m_split.shares().isZero()) { + txt = m_split.price().formatMoney(m_currency.tradingSymbol(), m_security.pricePrecision()); + } + break; + + case ValueColumn: + align |= Qt::AlignRight; + if (haveAmount()) { + txt = MyMoneyUtils::formatMoney(m_assetAccountSplit.value().abs(), m_currency); + + } else if (haveInterest()) { + txt = MyMoneyUtils::formatMoney(-m_interestAmount, m_currency); + } + break; + + case BalanceColumn: + align |= Qt::AlignRight; + if (m_showBalance) + txt = m_balance.formatMoney("", MyMoneyMoney::denomToPrec(m_security.smallestAccountFraction())); + else + txt = "----"; + break; + + default: + break; + } + break; + + case 1: + switch (col) { + case DetailColumn: + align |= Qt::AlignLeft; + if (haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty()) { + txt = MyMoneyFile::instance()->accountToCategory(m_assetAccountSplit.accountId()); + } else if (haveInterest() && m_interestSplits.count()) { + txt = m_interestCategory; + } else if (haveFees() && m_feeSplits.count()) { + txt = m_feeCategory; + } else + singleLineMemo(txt, m_split); + break; + + case QuantityColumn: + align |= Qt::AlignRight; + if (haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty()) { + // txt = m_interestAmount.abs().formatMoney(m_currency); + } else if (haveInterest() && m_interestSplits.count()) { + txt = MyMoneyUtils::formatMoney(-m_interestAmount, m_currency); + } else if (haveFees() && m_feeSplits.count()) { + txt = MyMoneyUtils::formatMoney(m_feeAmount, m_currency); + } + break; + + default: + break; + } + break; + + case 2: + switch (col) { + case DetailColumn: + align |= Qt::AlignLeft; + if (haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty() + && haveInterest() && m_interestSplits.count()) { + txt = m_interestCategory; + } else if (haveFees() && m_feeSplits.count()) { + txt = m_feeCategory; + } else + singleLineMemo(txt, m_split); + break; + + case QuantityColumn: + align |= Qt::AlignRight; + if (haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty() + && haveInterest() && m_interestSplits.count()) { + txt = MyMoneyUtils::formatMoney(-m_interestAmount, m_currency); + } else if (haveFees() && m_feeSplits.count()) { + txt = MyMoneyUtils::formatMoney(m_feeAmount, m_currency); + } + break; + + default: + break; + } + break; + + case 3: + switch (col) { + case DetailColumn: + align |= Qt::AlignLeft; + if (haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty() + && haveInterest() && m_interestSplits.count() + && haveFees() && m_feeSplits.count()) { + txt = m_feeCategory; + } else + singleLineMemo(txt, m_split); + break; + + case QuantityColumn: + align |= Qt::AlignRight; + if (haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty() + && haveInterest() && m_interestSplits.count() + && haveFees() && m_feeSplits.count()) { + txt = MyMoneyUtils::formatMoney(m_feeAmount, m_currency); + } + break; + + default: + break; + } + break; + + case 4: + switch (col) { + case DetailColumn: + align |= Qt::AlignLeft; + singleLineMemo(txt, m_split); + break; + + default: + break; + } + break; + } +} + +int InvestTransaction::registerColWidth(int col, const QFontMetrics& cellFontMetrics) +{ + QString txt; + MyMoneyMoney amount; + int nw = 0; + + // for now just check all rows in that column + for (int row = 0; row < m_rowsRegister; ++row) { + int w; + Transaction::registerCellText(txt, row, col); + w = cellFontMetrics.width(txt + " "); + nw = qMax(nw, w); + } + + // TODO the optimized way would be to base the size on the contents of a single row + // as we do it in StdTransaction::registerColWidth() +#if 0 + switch (col) { + default: + break; + + case PriceColumn: + if (havePrice()) { + txt = (m_split.value() / m_split.shares()).formatMoney("", KMyMoneyGlobalSettings::pricePrecision()); + nw = cellFontMetrics.width(txt + " "); + } + break; + } +#endif + return nw; +} + +void InvestTransaction::arrangeWidgetsInForm(QMap& editWidgets) +{ + if (!m_form || !m_parent) + return; + + setupFormPalette(editWidgets); + + // arrange the edit widgets + arrangeWidget(m_form, 0, ValueColumn1, editWidgets["activity"]); + arrangeWidget(m_form, 0, ValueColumn2, editWidgets["postdate"]); + arrangeWidget(m_form, 1, ValueColumn1, editWidgets["security"]); + arrangeWidget(m_form, 1, ValueColumn2, editWidgets["shares"]); + arrangeWidget(m_form, 2, ValueColumn1, editWidgets["asset-account"]); + arrangeWidget(m_form, 2, ValueColumn2, editWidgets["price"]); + arrangeWidget(m_form, 3, ValueColumn1, editWidgets["fee-account"]->parentWidget()); + arrangeWidget(m_form, 3, ValueColumn2, editWidgets["fee-amount"]); + arrangeWidget(m_form, 4, ValueColumn1, editWidgets["interest-account"]->parentWidget()); + arrangeWidget(m_form, 4, ValueColumn2, editWidgets["interest-amount"]); + arrangeWidget(m_form, 5, ValueColumn1, editWidgets["memo"]); + arrangeWidget(m_form, 5, ValueColumn2, editWidgets["total"]); + arrangeWidget(m_form, 6, ValueColumn2, editWidgets["status"]); + + // arrange dynamic labels + arrangeWidget(m_form, 0, LabelColumn1, editWidgets["activity-label"]); + arrangeWidget(m_form, 0, LabelColumn2, editWidgets["postdate-label"]); + arrangeWidget(m_form, 1, LabelColumn1, editWidgets["security-label"]); + arrangeWidget(m_form, 1, LabelColumn2, editWidgets["shares-label"]); + arrangeWidget(m_form, 2, LabelColumn1, editWidgets["asset-label"]); + arrangeWidget(m_form, 2, LabelColumn2, editWidgets["price-label"]); + arrangeWidget(m_form, 3, LabelColumn1, editWidgets["fee-label"]); + arrangeWidget(m_form, 3, LabelColumn2, editWidgets["fee-amount-label"]); + arrangeWidget(m_form, 4, LabelColumn1, editWidgets["interest-label"]); + arrangeWidget(m_form, 4, LabelColumn2, editWidgets["interest-amount-label"]); + arrangeWidget(m_form, 5, LabelColumn1, editWidgets["memo-label"]); + arrangeWidget(m_form, 5, LabelColumn2, editWidgets["total-label"]); + arrangeWidget(m_form, 6, LabelColumn2, editWidgets["status-label"]); + + // get rid of the hints. we don't need them for the form + QMap::iterator it; + for (it = editWidgets.begin(); it != editWidgets.end(); ++it) { + KMyMoneyCombo* combo = dynamic_cast(*it); + kMyMoneyLineEdit* lineedit = dynamic_cast(*it); + kMyMoneyEdit* edit = dynamic_cast(*it); + KMyMoneyPayeeCombo* payee = dynamic_cast(*it); + if (combo) + combo->setPlaceholderText(QString()); + if (edit) + edit->setPlaceholderText(QString()); + if (lineedit) + lineedit->setPlaceholderText(QString()); + if (payee) + payee->setPlaceholderText(QString()); + } +} + +void InvestTransaction::tabOrderInForm(QWidgetList& tabOrderWidgets) const +{ + // activity + tabOrderWidgets.append(focusWidget(m_form->cellWidget(0, ValueColumn1))); + + // date + tabOrderWidgets.append(focusWidget(m_form->cellWidget(0, ValueColumn2))); + + // security + tabOrderWidgets.append(focusWidget(m_form->cellWidget(1, ValueColumn1))); + + // shares + tabOrderWidgets.append(focusWidget(m_form->cellWidget(1, ValueColumn2))); + + // account + tabOrderWidgets.append(focusWidget(m_form->cellWidget(2, ValueColumn1))); + + // price + tabOrderWidgets.append(focusWidget(m_form->cellWidget(2, ValueColumn2))); + + // make sure to have the fee category field and the split button as separate tab order widgets + // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but + // it's one of our own widgets, so we actually don't care. Just make sure, that we don't + // go haywire when someone changes the KMyMoneyCategory object ... + QWidget* w = m_form->cellWidget(3, ValueColumn1); + tabOrderWidgets.append(focusWidget(w)); + w = w->findChild("splitButton"); + if (w) + tabOrderWidgets.append(w); + + // fee amount + tabOrderWidgets.append(focusWidget(m_form->cellWidget(3, ValueColumn2))); + + // the same applies for the interest categories + w = m_form->cellWidget(4, ValueColumn1); + tabOrderWidgets.append(focusWidget(w)); + w = w->findChild("splitButton"); + if (w) + tabOrderWidgets.append(w); + + // interest amount + tabOrderWidgets.append(focusWidget(m_form->cellWidget(4, ValueColumn2))); + + // memo + tabOrderWidgets.append(focusWidget(m_form->cellWidget(5, ValueColumn1))); + + // state + tabOrderWidgets.append(focusWidget(m_form->cellWidget(6, ValueColumn2))); +} + +void InvestTransaction::arrangeWidgetsInRegister(QMap& editWidgets) +{ + if (!m_parent) + return; + + setupRegisterPalette(editWidgets); + + arrangeWidget(m_parent, m_startRow + 0, DateColumn, editWidgets["postdate"]); + arrangeWidget(m_parent, m_startRow + 0, SecurityColumn, editWidgets["security"]); + arrangeWidget(m_parent, m_startRow + 0, DetailColumn, editWidgets["activity"]); + arrangeWidget(m_parent, m_startRow + 1, DetailColumn, editWidgets["asset-account"]); + arrangeWidget(m_parent, m_startRow + 2, DetailColumn, editWidgets["interest-account"]->parentWidget()); + arrangeWidget(m_parent, m_startRow + 3, DetailColumn, editWidgets["fee-account"]->parentWidget()); + arrangeWidget(m_parent, m_startRow + 4, DetailColumn, editWidgets["memo"]); + arrangeWidget(m_parent, m_startRow + 0, QuantityColumn, editWidgets["shares"]); + arrangeWidget(m_parent, m_startRow + 0, PriceColumn, editWidgets["price"]); + arrangeWidget(m_parent, m_startRow + 2, QuantityColumn, editWidgets["interest-amount"]); + arrangeWidget(m_parent, m_startRow + 3, QuantityColumn, editWidgets["fee-amount"]); + arrangeWidget(m_parent, m_startRow + 0, ValueColumn, editWidgets["total"]); + arrangeWidget(m_parent, m_startRow + 1, DateColumn, editWidgets["status"]); + + // increase the height of the row containing the memo widget + m_parent->setRowHeight(m_startRow + 4, m_parent->rowHeightHint() * 3); +} + +void InvestTransaction::tabOrderInRegister(QWidgetList& tabOrderWidgets) const +{ + QWidget* w; + + // date + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DateColumn))); + // security + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, SecurityColumn))); + // activity + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, DetailColumn))); + // shares + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, QuantityColumn))); + // price + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 0, PriceColumn))); + // asset account + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 1, DetailColumn))); + + // make sure to have the category fields and the split button as separate tab order widgets + // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but + // it's one of our own widgets, so we actually don't care. Just make sure, that we don't + // go haywire when someone changes the KMyMoneyCategory object ... + w = m_parent->cellWidget(m_startRow + 2, DetailColumn); // interest account + tabOrderWidgets.append(focusWidget(w)); + w = w->findChild("splitButton"); + if (w) + tabOrderWidgets.append(w); + + // interest amount + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 2, QuantityColumn))); + + w = m_parent->cellWidget(m_startRow + 3, DetailColumn); // fee account + tabOrderWidgets.append(focusWidget(w)); + w = w->findChild("splitButton"); + if (w) + tabOrderWidgets.append(w); + + // fee amount + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 3, QuantityColumn))); + + // memo + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 4, DetailColumn))); + + // status + tabOrderWidgets.append(focusWidget(m_parent->cellWidget(m_startRow + 1, DateColumn))); +} + +int InvestTransaction::numRowsRegister(bool expanded) const +{ + int numRows = 1; + if (expanded) { + if (!m_inEdit) { + if (haveAssetAccount() && !m_assetAccountSplit.accountId().isEmpty()) + ++numRows; + if (haveInterest() && m_interestSplits.count()) + ++numRows; + if (haveFees() && m_feeSplits.count()) + ++numRows; + if (!m_split.memo().isEmpty()) + ++numRows; + } else + numRows = 5; + } + return numRows; +} + +bool InvestTransaction::haveShares() const +{ + bool rc = true; + switch (m_transactionType) { + case eMyMoney::Split::InvestmentTransactionType::Dividend: + case eMyMoney::Split::InvestmentTransactionType::Yield: + case eMyMoney::Split::InvestmentTransactionType::SplitShares: + case eMyMoney::Split::InvestmentTransactionType::InterestIncome: + rc = false; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveFees() const +{ + bool rc = true; + switch (m_transactionType) { + case eMyMoney::Split::InvestmentTransactionType::AddShares: + case eMyMoney::Split::InvestmentTransactionType::RemoveShares: + case eMyMoney::Split::InvestmentTransactionType::SplitShares: + rc = false; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveInterest() const +{ + bool rc = false; + switch (m_transactionType) { + case eMyMoney::Split::InvestmentTransactionType::BuyShares: + case eMyMoney::Split::InvestmentTransactionType::SellShares: + case eMyMoney::Split::InvestmentTransactionType::Dividend: + case eMyMoney::Split::InvestmentTransactionType::ReinvestDividend: + case eMyMoney::Split::InvestmentTransactionType::Yield: + case eMyMoney::Split::InvestmentTransactionType::InterestIncome: + rc = true; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::havePrice() const +{ + bool rc = false; + switch (m_transactionType) { + case eMyMoney::Split::InvestmentTransactionType::BuyShares: + case eMyMoney::Split::InvestmentTransactionType::SellShares: + case eMyMoney::Split::InvestmentTransactionType::ReinvestDividend: + rc = true; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveAmount() const +{ + bool rc = false; + switch (m_transactionType) { + case eMyMoney::Split::InvestmentTransactionType::BuyShares: + case eMyMoney::Split::InvestmentTransactionType::SellShares: + case eMyMoney::Split::InvestmentTransactionType::Dividend: + case eMyMoney::Split::InvestmentTransactionType::Yield: + case eMyMoney::Split::InvestmentTransactionType::InterestIncome: + rc = true; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveAssetAccount() const +{ + bool rc = true; + switch (m_transactionType) { + case eMyMoney::Split::InvestmentTransactionType::AddShares: + case eMyMoney::Split::InvestmentTransactionType::RemoveShares: + case eMyMoney::Split::InvestmentTransactionType::SplitShares: + case eMyMoney::Split::InvestmentTransactionType::ReinvestDividend: + rc = false; + break; + + default: + break; + } + return rc; +} + +bool InvestTransaction::haveSplitRatio() const +{ + return m_transactionType == eMyMoney::Split::InvestmentTransactionType::SplitShares; +} + +void InvestTransaction::splits(MyMoneySplit& assetAccountSplit, QList& interestSplits, QList& feeSplits) const +{ + assetAccountSplit = m_assetAccountSplit; + interestSplits = m_interestSplits; + feeSplits = m_feeSplits; +} + +TransactionEditor* InvestTransaction::createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) +{ +#ifndef KMM_DESIGNER + m_inRegisterEdit = regForm == m_parent; + return new InvestTransactionEditor(regForm, this, list, lastPostDate); +#else + return NULL; +#endif +} + diff --git a/kmymoney/widgets/transaction.h b/kmymoney/widgets/transaction.h index eae2200bb..574e21745 100644 --- a/kmymoney/widgets/transaction.h +++ b/kmymoney/widgets/transaction.h @@ -1,242 +1,453 @@ /*************************************************************************** transaction.h - description ------------------- begin : Tue Jun 13 2006 copyright : (C) 2000-2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef TRANSACTION_H #define TRANSACTION_H // ---------------------------------------------------------------------------- // QT Includes -#include +#include +#include +#include +#include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "registeritem.h" +#include "mymoneymoney.h" +#include "mymoneyaccount.h" +#include "mymoneysecurity.h" +#include "mymoneysplit.h" +#include "mymoneytransaction.h" +#include "selectedtransaction.h" -class QWidget; -class QPalette; -class QFontMetrics; class QTableWidget; class TransactionEditor; class TransactionEditorContainer; -class MyMoneySplit; -class MyMoneyTransaction; - -template class QMap; - -namespace KMyMoneyTransactionForm { class TransactionForm; } +namespace KMyMoneyTransactionForm +{ +class TransactionForm; +} // namespace namespace KMyMoneyRegister { - class SelectedTransactions; - // keep the following list in sync with code in the constructor - // of KMyMoneyRegister::Register in register.cpp - typedef enum { - NumberColumn = 0, - DateColumn, - AccountColumn, - SecurityColumn, - DetailColumn, - ReconcileFlagColumn, - PaymentColumn, - DepositColumn, - QuantityColumn, - PriceColumn, - ValueColumn, - BalanceColumn, - // insert new values above this line - MaxColumns - } Column; - - class TransactionPrivate; - class Transaction : public RegisterItem - { - public: - explicit Transaction(Register* getParent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - Transaction(const Transaction & other); - Transaction(Transaction && other); - friend void swap(Transaction& first, Transaction& second); - virtual ~Transaction(); - - virtual const char* className() override; - bool isSelectable() const override; - bool isSelected() const override; - void setSelected(bool selected) override; - bool canHaveFocus() const override; - bool hasFocus() const override; - bool hasEditorOpen() const override; - virtual bool isScheduled() const; - void setFocus(bool focus, bool updateLens = true) override; - bool isErroneous() const override; - QDate sortPostDate() const override; - virtual int sortSamePostDate() const override; - QDate sortEntryDate() const override; - virtual const QString& sortPayee() const override; - virtual const QList& sortTagList() const; - MyMoneyMoney sortValue() const override; - QString sortNumber() const override; - virtual const QString& sortEntryOrder() const override; - virtual CashFlowDirection sortType() const override; - virtual const QString& sortCategory() const override; - virtual eMyMoney::Split::State sortReconcileState() const override; - virtual const QString& id() const override; - const MyMoneyTransaction& transaction() const; - const MyMoneySplit& split() const; - void setBalance(const MyMoneyMoney& balance); - const MyMoneyMoney& balance() const; - - virtual int rowHeightHint() const override ; - - virtual bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index); - virtual void paintRegisterCell(QPainter* painter, QStyleOptionViewItem& option, const QModelIndex& index) override; - - virtual void paintFormCell(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) override; - virtual bool formCellText(QString& /* txt */, Qt::Alignment& /* align */, int /* row */, int /* col */, QPainter* /* painter */); - virtual void registerCellText(QString& /* txt */, Qt::Alignment& /* align */, int /* row */, int /* col */, QPainter* /* painter */); - virtual int registerColWidth(int /* col */, const QFontMetrics& /* cellFontMetrics */); - - /** + +// keep the following list in sync with code in the constructor +// of KMyMoneyRegister::Register in register.cpp +typedef enum { + NumberColumn = 0, + DateColumn, + AccountColumn, + SecurityColumn, + DetailColumn, + ReconcileFlagColumn, + PaymentColumn, + DepositColumn, + QuantityColumn, + PriceColumn, + ValueColumn, + BalanceColumn, + // insert new values above this line + MaxColumns +} Column; + +class Transaction : public RegisterItem +{ +public: + Transaction(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~Transaction() {} + + virtual const char* className() override { + return "Transaction"; + } + + bool isSelectable() const override { + return true; + } + bool isSelected() const override { + return m_selected; + } + void setSelected(bool selected) override; + + bool canHaveFocus() const override { + return true; + } + bool hasFocus() const override { + return m_focus; + } + bool hasEditorOpen() const override { + return m_inEdit; + } + + virtual bool isScheduled() const { + return false; + } + + void setFocus(bool focus, bool updateLens = true) override; + + bool isErroneous() const override { + return m_erroneous; + } + + QDate sortPostDate() const override { + return m_transaction.postDate(); + } + virtual int sortSamePostDate() const override { + return 2; + } + QDate sortEntryDate() const override { + return m_transaction.entryDate(); + } + virtual const QString& sortPayee() const override { + return m_payee; + } + virtual const QList& sortTagList() const { + return m_tagList; + } + MyMoneyMoney sortValue() const override { + return m_split.shares(); + } + QString sortNumber() const override { + return m_split.number(); + } + virtual const QString& sortEntryOrder() const override { + return m_uniqueId; + } + virtual CashFlowDirection sortType() const override { + return m_split.shares().isNegative() ? Payment : Deposit; + } + virtual const QString& sortCategory() const override { + return m_category; + } + virtual eMyMoney::Split::State sortReconcileState() const override { + return m_split.reconcileFlag(); + } + + virtual const QString& id() const override { + return m_uniqueId; + } + const MyMoneyTransaction& transaction() const { + return m_transaction; + } + const MyMoneySplit& split() const { + return m_split; + } + + void setBalance(const MyMoneyMoney& balance) { + m_balance = balance; + } + const MyMoneyMoney& balance() const { + return m_balance; + } + + virtual int rowHeightHint() const override ; + + virtual bool paintRegisterCellSetup(QPainter *painter, QStyleOptionViewItem &option, const QModelIndex &index); + virtual void paintRegisterCell(QPainter* painter, QStyleOptionViewItem& option, const QModelIndex& index) override; + + virtual void paintFormCell(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) override; + virtual bool formCellText(QString& /* txt */, Qt::Alignment& /* align */, int /* row */, int /* col */, QPainter* /* painter */) { + return false; + } + virtual void registerCellText(QString& /* txt */, Qt::Alignment& /* align */, int /* row */, int /* col */, QPainter* /* painter */) {} + virtual int registerColWidth(int /* col */, const QFontMetrics& /* cellFontMetrics */) { + return 0; + } + + /** * Helper method for the above method. */ - void registerCellText(QString& txt, int row, int col); + void registerCellText(QString& txt, int row, int col); - virtual int formRowHeight(int row); - virtual int formRowHeight() const; + virtual int formRowHeight(int row); + virtual int formRowHeight() const; - virtual void setupForm(KMyMoneyTransactionForm::TransactionForm* form); - virtual void setupFormPalette(QMap& editWidgets); - virtual void setupRegisterPalette(QMap& editWidgets); - virtual void loadTab(KMyMoneyTransactionForm::TransactionForm* form) = 0; + virtual void setupForm(KMyMoneyTransactionForm::TransactionForm* form); + virtual void setupFormPalette(QMap& editWidgets); + virtual void setupRegisterPalette(QMap& editWidgets); + virtual void loadTab(KMyMoneyTransactionForm::TransactionForm* form) = 0; - virtual void arrangeWidgetsInForm(QMap& editWidgets) = 0; - virtual void arrangeWidgetsInRegister(QMap& editWidgets) = 0; - virtual void tabOrderInForm(QWidgetList& tabOrderWidgets) const = 0; - virtual void tabOrderInRegister(QWidgetList& tabOrderWidgets) const = 0; + virtual void arrangeWidgetsInForm(QMap& editWidgets) = 0; + virtual void arrangeWidgetsInRegister(QMap& editWidgets) = 0; + virtual void tabOrderInForm(QWidgetList& tabOrderWidgets) const = 0; + virtual void tabOrderInRegister(QWidgetList& tabOrderWidgets) const = 0; - virtual KMyMoneyRegister::Action actionType() const = 0; + virtual KMyMoneyRegister::Action actionType() const = 0; - QWidget* focusWidget(QWidget*) const; - void arrangeWidget(QTableWidget* tbl, int row, int col, QWidget* w) const; + QWidget* focusWidget(QWidget*) const; + void arrangeWidget(QTableWidget* tbl, int row, int col, QWidget* w) const; - bool haveNumberField() const; + bool haveNumberField() const; - bool matches(const RegisterFilter&) const override; + bool matches(const RegisterFilter&) const override; - /** + /** * Checks if the mouse hovered over an area that has a tooltip associated with it. * The mouse position is given in relative coordinates to the @a startRow and the * @a row and @a col of the item are also passed as relative values. * * If a tooltip shall be shown, this method presets the rectangle @a r with the * area in register coordinates and @a msg with the string that will be passed * to QToolTip::tip. @a true is returned in this case. * * If no tooltip is available, @a false will be returned. */ - virtual bool maybeTip(const QPoint& relpos, int row, int col, QRect& r, QString& msg) override; + virtual bool maybeTip(const QPoint& relpos, int row, int col, QRect& r, QString& msg) override; - /** + /** * This method returns the number of register rows required for a certain * item in expanded (@p expanded equals @a true) or collapsed (@p expanded * is @a false) mode. * * @param expanded returns number of maximum rows required for this item to * display all information (used for ledger lens and register * edit mode) or the minimum number of rows required. * @return number of rows required for mode selected by @p expanded */ - virtual int numRowsRegister(bool expanded) const = 0; + virtual int numRowsRegister(bool expanded) const = 0; - /** + /** * Provided for internal reasons. No API change. See RegisterItem::numRowsRegister() */ - int numRowsRegister() const override; + int numRowsRegister() const override { + return RegisterItem::numRowsRegister(); + } - void leaveEditMode(); - void startEditMode(); + void leaveEditMode(); + void startEditMode(); - /** + /** * This method creates an editor for the transaction */ - virtual TransactionEditor* createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) = 0; + virtual TransactionEditor* createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) = 0; - virtual void setVisible(bool visible) override; + virtual void setVisible(bool visible) override; - virtual void setShowBalance(bool showBalance); + virtual void setShowBalance(bool showBalance); - /** + /** * Return information if @a row should be shown (@a true ) * or hidden (@a false ) in the form. Default is true. */ - virtual bool showRowInForm(int row) const; + virtual bool showRowInForm(int row) const { + Q_UNUSED(row) return true; + } - /** + /** * Control visibility of @a row in the transaction form. * Only row 0 has an effect, others return @a true. */ - virtual void setShowRowInForm(int row, bool show); + virtual void setShowRowInForm(int row, bool show) { + Q_UNUSED(row); Q_UNUSED(show) + } - virtual void setReducedIntensity(bool reduced); + virtual void setReducedIntensity(bool reduced) { + m_reducedIntensity = reduced; + } - protected: - /** +protected: + /** * This method converts m_split.reconcileFlag() into a readable string * * @param text Return textual representation e.g. "Cleared" (@a true) or just * a flag e.g. "C" (@a false). Defaults to textual representation. * @return Textual representation or flag as selected via @p text of the * reconciliation state of the split */ - QString reconcileState(bool text = true) const; + QString reconcileState(bool text = true) const; - /** + /** * Helper method to reduce a multi line memo text into a single line. * * @param txt QString that will receive the single line memo text * @param split const reference to the split to take the memo from */ - void singleLineMemo(QString& txt, const MyMoneySplit& split) const; + void singleLineMemo(QString& txt, const MyMoneySplit& split) const; + + virtual void setupPalette(const QPalette& palette, QMap& editWidgets); + +protected: + MyMoneyTransaction m_transaction; + MyMoneySplit m_split; + MyMoneyAccount m_account; + MyMoneyMoney m_balance; + QTableWidget* m_form; + QString m_category; + QString m_payee; + QString m_payeeHeader; + QList m_tagList; + QList m_tagColorList; + QString m_categoryHeader; + QString m_splitCurrencyId; + QString m_uniqueId; + int m_formRowHeight; + bool m_selected; + bool m_focus; + bool m_erroneous; + bool m_inEdit; + bool m_inRegisterEdit; + bool m_showBalance; + bool m_reducedIntensity; +}; + +class StdTransaction : public Transaction +{ +public: + StdTransaction(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~StdTransaction() {} - virtual void setupPalette(const QPalette& palette, QMap& editWidgets); + virtual const char* className() { + return "StdTransaction"; + } - TransactionPrivate *d_ptr; - Transaction(TransactionPrivate &dd, Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); - Transaction(TransactionPrivate &dd); //for copy-constructor of derived class + bool formCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter = 0); + void registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter = 0); - private: - Transaction(); - Q_DECLARE_PRIVATE(Transaction) - }; + int registerColWidth(int col, const QFontMetrics& cellFontMetrics); + void setupForm(KMyMoneyTransactionForm::TransactionForm* form); + void loadTab(KMyMoneyTransactionForm::TransactionForm* form); - inline void swap(Transaction& first, Transaction& second) // krazy:exclude=inline - { - using std::swap; - swap(first.d_ptr, second.d_ptr); + int numColsForm() const { + return 4; } - inline Transaction::Transaction(Transaction && other) : Transaction() // krazy:exclude=inline - { - swap(*this, other); + void arrangeWidgetsInForm(QMap& editWidgets); + void arrangeWidgetsInRegister(QMap& editWidgets); + void tabOrderInForm(QWidgetList& tabOrderWidgets) const; + void tabOrderInRegister(QWidgetList& tabOrderWidgets) const; + KMyMoneyRegister::Action actionType() const; + + int numRowsRegister(bool expanded) const; + + /** + * Provided for internal reasons. No API change. See RegisterItem::numRowsRegister() + */ + int numRowsRegister() const { + return RegisterItem::numRowsRegister(); } + + TransactionEditor* createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate); + + /** + * Return information if @a row should be shown (@a true ) + * or hidden (@a false ) in the form. Default is true. + */ + virtual bool showRowInForm(int row) const; + + /** + * Control visibility of @a row in the transaction form. + * Only row 0 has an effect, others return @a true. + */ + virtual void setShowRowInForm(int row, bool show); + +protected: + void setupFormHeader(const QString& id); + +private: + bool m_showAccountRow; +}; + +class InvestTransaction : public Transaction +{ +public: + InvestTransaction(Register* parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId); + virtual ~InvestTransaction() {} + + virtual const QString sortSecurity() const override { + return m_security.name(); + } + virtual const char* className() override { + return "InvestTransaction"; + } + + bool formCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter = 0) override; + void registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter = 0) override; + + int registerColWidth(int col, const QFontMetrics& cellFontMetrics) override; + void setupForm(KMyMoneyTransactionForm::TransactionForm* form) override; + + /** + * provide NOP here as the investment transaction form does not supply a tab + */ + void loadTab(KMyMoneyTransactionForm::TransactionForm* /* form */) override {} + + int numColsForm() const override { + return 4; + } + + void arrangeWidgetsInForm(QMap& editWidgets) override; + void arrangeWidgetsInRegister(QMap& editWidgets) override; + void tabOrderInForm(QWidgetList& tabOrderWidgets) const override; + void tabOrderInRegister(QWidgetList& tabOrderWidgets) const override; + KMyMoneyRegister::Action actionType() const override { + return KMyMoneyRegister::ActionNone; + } + + int numRowsRegister(bool expanded) const override; + + /** + * Provided for internal reasons. No API change. See RegisterItem::numRowsRegister() + */ + int numRowsRegister() const override { + return RegisterItem::numRowsRegister(); + } + + TransactionEditor* createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) override; + + void splits(MyMoneySplit& assetAccountSplit, QList& interestSplits, QList& feeSplits) const; + +protected: + bool haveShares() const; + bool haveFees() const; + bool haveInterest() const; + bool havePrice() const; + bool haveAmount() const; + bool haveAssetAccount() const; + bool haveSplitRatio() const; + + /** + * Returns textual representation of the activity identified + * by @p type. + * + * @param txt reference to QString where to store the result + * @param type activity represented as investTransactionTypeE + */ + void activity(QString& txt, eMyMoney::Split::InvestmentTransactionType type) const; + +private: + QList m_feeSplits; + QList m_interestSplits; + MyMoneySplit m_assetAccountSplit; + MyMoneySecurity m_security; + MyMoneySecurity m_currency; + eMyMoney::Split::InvestmentTransactionType m_transactionType; + QString m_feeCategory; + QString m_interestCategory; + MyMoneyMoney m_feeAmount; + MyMoneyMoney m_interestAmount; + MyMoneyMoney m_totalAmount; +}; + } // namespace #endif diff --git a/kmymoney/widgets/transaction_p.h b/kmymoney/widgets/transaction_p.h deleted file mode 100644 index be9658225..000000000 --- a/kmymoney/widgets/transaction_p.h +++ /dev/null @@ -1,143 +0,0 @@ -/*************************************************************************** - transaction_p.h - description - ------------------- - begin : Tue Jun 13 2006 - copyright : (C) 2000-2006 by Thomas Baumgart - (C) 2017 by Łukasz Wojniłowicz - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef TRANSACTION_P_H -#define TRANSACTION_P_H - -#include "transaction.h" -#include "registeritem_p.h" - -#include - -// ---------------------------------------------------------------------------- -// QT Includes - -#include -#include - -// ---------------------------------------------------------------------------- -// KDE Includes - -#include - -// ---------------------------------------------------------------------------- -// Project Includes - -#include "register.h" -#include "mymoneyaccount.h" -#include "mymoneyfile.h" -#include "mymoneymoney.h" -#include "mymoneypayee.h" -#include "mymoneysplit.h" -#include "mymoneytag.h" -#include "mymoneytransaction.h" -#ifndef KMM_DESIGNER -#include "stdtransactioneditor.h" -#endif - -using namespace KMyMoneyRegister; -using namespace KMyMoneyTransactionForm; - -namespace KMyMoneyRegister -{ - class TransactionPrivate : public RegisterItemPrivate - { - public: - TransactionPrivate() - { - } - - virtual ~ TransactionPrivate() - { - } - - void init(int uniqueId) - { - m_formRowHeight = -1; - m_selected = false; - m_focus = false; - m_erroneous = false; - m_inEdit = false; - m_inRegisterEdit = false; - m_showBalance = true; - m_reducedIntensity = false; - - auto file = MyMoneyFile::instance(); - - // load the account - if (!m_split.accountId().isEmpty()) - m_account = file->account(m_split.accountId()); - - // load the payee - if (!m_split.payeeId().isEmpty()) { - m_payee = file->payee(m_split.payeeId()).name(); - } - if (m_parent->account().isIncomeExpense()) { - m_payeeHeader = m_split.shares().isNegative() ? i18n("From") : i18n("Pay to"); - } else { - m_payeeHeader = m_split.shares().isNegative() ? i18n("Pay to") : i18n("From"); - } - - // load the tag - if (!m_split.tagIdList().isEmpty()) { - const QList t = m_split.tagIdList(); - for (auto i = 0; i < t.count(); i++) { - m_tagList << file->tag(t[i]).name(); - m_tagColorList << file->tag(t[i]).tagColor(); - } - } - - // load the currency - if (!m_transaction.id().isEmpty()) - m_splitCurrencyId = m_account.currencyId(); - - // check if transaction is erroneous or not - m_erroneous = !m_transaction.splitSum().isZero(); - - if (!m_uniqueId.isEmpty()) { - m_uniqueId += '-'; - QString id; - id.setNum(uniqueId); - m_uniqueId += id.rightJustified(3, '0'); - } - } - - MyMoneyTransaction m_transaction; - MyMoneySplit m_split; - MyMoneyAccount m_account; - MyMoneyMoney m_balance; - QTableWidget* m_form; - QString m_category; - QString m_payee; - QString m_payeeHeader; - QList m_tagList; - QList m_tagColorList; - QString m_categoryHeader; - QString m_splitCurrencyId; - QString m_uniqueId; - int m_formRowHeight; - bool m_selected; - bool m_focus; - bool m_erroneous; - bool m_inEdit; - bool m_inRegisterEdit; - bool m_showBalance; - bool m_reducedIntensity; - }; -} - -#endif diff --git a/kmymoney/widgets/transactioneditorcontainer.cpp b/kmymoney/widgets/transactioneditorcontainer.cpp index 697883336..d4fd1f27a 100644 --- a/kmymoney/widgets/transactioneditorcontainer.cpp +++ b/kmymoney/widgets/transactioneditorcontainer.cpp @@ -1,37 +1,28 @@ /*************************************************************************** transactioneditorcontainer.cpp ---------- begin : Wed Jun 07 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "transactioneditorcontainer.h" // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes -TransactionEditorContainer::TransactionEditorContainer(QWidget* parent) : QTableWidget(parent) -{ -} - -void TransactionEditorContainer::updateGeometries() -{ - QTableWidget::updateGeometries(); - emit geometriesUpdated(); -} diff --git a/kmymoney/widgets/transactioneditorcontainer.h b/kmymoney/widgets/transactioneditorcontainer.h index 2826f5aec..754caf189 100644 --- a/kmymoney/widgets/transactioneditorcontainer.h +++ b/kmymoney/widgets/transactioneditorcontainer.h @@ -1,67 +1,70 @@ /*************************************************************************** transactioneditorcontainer.h ---------- begin : Wed Jun 07 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef TRANSACTIONEDITORCONTAINER_H #define TRANSACTIONEDITORCONTAINER_H // ---------------------------------------------------------------------------- // QT Includes #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes namespace KMyMoneyRegister { class Transaction; } typedef enum { ProtectNone = 0, ProtectTransfer, ProtectNonTransfer, ProtectAll } ProtectedAction; class TransactionEditorContainer : public QTableWidget { Q_OBJECT public: - TransactionEditorContainer(QWidget* parent); + TransactionEditorContainer(QWidget* parent) : QTableWidget(parent) {} virtual void arrangeEditWidgets(QMap& editWidgets, KMyMoneyRegister::Transaction* t) = 0; virtual void removeEditWidgets(QMap& editWidgets) = 0; virtual void tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const = 0; // FIXME remove tabbar // virtual int action(QMap& editWidgets) const = 0; // virtual void setProtectedAction(QMap& editWidgets, ProtectedAction action) = 0; signals: void geometriesUpdated(); protected slots: - void updateGeometries(); + void updateGeometries() { + QTableWidget::updateGeometries(); + emit geometriesUpdated(); + } }; #endif diff --git a/kmymoney/widgets/transactionform.cpp b/kmymoney/widgets/transactionform.cpp index 2695decac..abfd98120 100644 --- a/kmymoney/widgets/transactionform.cpp +++ b/kmymoney/widgets/transactionform.cpp @@ -1,452 +1,452 @@ /*************************************************************************** transactionform.cpp ------------------- begin : Sun May 14 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "transactionform.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyaccount.h" #include "kmymoneydateinput.h" #include "kmymoneyedit.h" #include "kmymoneycategory.h" #include "transaction.h" #include "kmymoneyglobalsettings.h" using namespace KMyMoneyTransactionForm; TabBar::TabBar(QWidget* parent) : QTabBar(parent), m_signalType(SignalNormal) { connect(this, SIGNAL(currentChanged(int)), this, SLOT(slotTabCurrentChanged(int))); } TabBar::SignalEmissionE TabBar::setSignalEmission(TabBar::SignalEmissionE type) { TabBar::SignalEmissionE _type = m_signalType; m_signalType = type; return _type; } int TabBar::currentIndex() const { QMap::const_iterator it; int id = QTabBar::currentIndex(); for (it = m_idMap.constBegin(); it != m_idMap.constEnd(); ++it) { if (*it == id) { return it.key(); } } return -1; } void TabBar::setCurrentIndex(int id) { if (m_signalType != SignalNormal) blockSignals(true); if (m_idMap.contains(id)) { QTabBar::setCurrentIndex(m_idMap[id]); } if (m_signalType != SignalNormal) blockSignals(false); if (m_signalType == SignalAlways) emit currentChanged(m_idMap[id]); } void TabBar::setTabEnabled(int id, bool enable) { if (m_idMap.contains(id)) { QTabBar::setTabEnabled(m_idMap[id], enable); } } void TabBar::insertTab(int id, const QString& title) { int newId = QTabBar::insertTab(id, title); m_idMap[id] = newId; } void TabBar::slotTabCurrentChanged(int id) { QMap::const_iterator it; for (it = m_idMap.constBegin(); it != m_idMap.constEnd(); ++it) { if (*it == id) { emit tabCurrentChanged(it.key()); break; } } if (it == m_idMap.constEnd()) emit tabCurrentChanged(id); } void TabBar::showEvent(QShowEvent* event) { // make sure we don't emit a signal when simply showing the widget if (m_signalType != SignalNormal) blockSignals(true); QTabBar::showEvent(event); if (m_signalType != SignalNormal) blockSignals(false); } void TabBar::copyTabs(const TabBar* otabbar) { // remove all existing tabs while (count()) { removeTab(0); } // now create new ones. copy text, icon and identifier m_idMap = otabbar->m_idMap; - for (auto i = 0; i < otabbar->count(); ++i) { + for (int i = 0; i < otabbar->count(); ++i) { QTabBar::insertTab(i, otabbar->tabText(i)); if (i == otabbar->QTabBar::currentIndex()) { QTabBar::setCurrentIndex(i); } } } int TabBar::indexAtPos(const QPoint& p) const { if (tabRect(QTabBar::currentIndex()).contains(p)) return QTabBar::currentIndex(); - for (auto i = 0; i < count(); ++i) + for (int i = 0; i < count(); ++i) if (isTabEnabled(i) && tabRect(i).contains(p)) return i; return -1; } void TabBar::mousePressEvent(QMouseEvent *e) { QTabBar::mousePressEvent(e); // in case we receive a mouse press event on the current // selected tab emit a signal no matter what as the base // class does not do that if (indexAtPos(e->pos()) == QTabBar::currentIndex()) { slotTabCurrentChanged(QTabBar::currentIndex()); } } TransactionFormItemDelegate::TransactionFormItemDelegate(TransactionForm *parent) : QStyledItemDelegate(parent), m_transactionForm(parent) { } TransactionFormItemDelegate::~TransactionFormItemDelegate() { } void TransactionFormItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { m_transactionForm->paintCell(painter, option, index); } TransactionForm::TransactionForm(QWidget *parent) : TransactionEditorContainer(parent), m_transaction(0), m_tabBar(0) { m_itemDelegate = new TransactionFormItemDelegate(this); setFrameShape(QTableWidget::NoFrame); setShowGrid(false); setSelectionMode(QTableWidget::NoSelection); verticalHeader()->hide(); horizontalHeader()->hide(); setEditTriggers(QAbstractItemView::NoEditTriggers); // make sure, that the table is 'invisible' by setting up the right background // keep the original color group for painting the cells though QPalette p = palette(); QBrush brush = p.brush(QPalette::Background); QColor color = brush.color(); color.setAlpha(0); brush.setColor(color); p.setBrush(QPalette::Active, QPalette::Base, brush); p.setBrush(QPalette::Inactive, QPalette::Base, brush); p.setBrush(QPalette::Disabled, QPalette::Base, brush); setPalette(p); slotSetTransaction(0); } bool TransactionForm::focusNextPrevChild(bool next) { return QFrame::focusNextPrevChild(next); } void TransactionForm::clear() { slotSetTransaction(0); } void TransactionForm::enableTabBar(bool b) { m_tabBar->setEnabled(b); } void TransactionForm::slotSetTransaction(KMyMoneyRegister::Transaction* transaction) { m_transaction = transaction; setUpdatesEnabled(false); if (m_transaction) { // the next call sets up a back pointer to the form and also sets up the col and row span // as well as the tab of the form m_transaction->setupForm(this); } else { setRowCount(5); setColumnCount(1); } kMyMoneyDateInput dateInput; - KMyMoneyCategory category(true, nullptr); + KMyMoneyCategory category(nullptr, true); // extract the maximal sizeHint height int height = qMax(dateInput.sizeHint().height(), category.sizeHint().height()); for (int row = 0; row < rowCount(); ++row) { if (!transaction || transaction->showRowInForm(row)) { showRow(row); QTableWidget::setRowHeight(row, height); } else hideRow(row); } // adjust vertical size of form table height *= rowCount(); setMaximumHeight(height); setMinimumHeight(height); setUpdatesEnabled(true); // see the call to setUpdatesEnabled(false) above - for (auto i = 0; i < rowCount(); ++i) { + for (int i = 0; i < rowCount(); ++i) { setItemDelegateForRow(i, m_itemDelegate); } // force resizeing of the columns QMetaObject::invokeMethod(this, "resize", Qt::QueuedConnection, QGenericReturnArgument(), Q_ARG(int, ValueColumn1)); } void TransactionForm::paintCell(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) { if (m_transaction) { m_transaction->paintFormCell(painter, option, index); } } TabBar* TransactionForm::tabBar(QWidget* parent) { if (!m_tabBar && parent) { // determine the height of the objects in the table // create the tab bar m_tabBar = new TabBar(parent); m_tabBar->setSignalEmission(TabBar::SignalAlways); QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); sizePolicy.setHeightForWidth(m_tabBar->sizePolicy().hasHeightForWidth()); m_tabBar->setSizePolicy(sizePolicy); connect(m_tabBar, SIGNAL(tabCurrentChanged(int)), this, SLOT(slotActionSelected(int))); } return m_tabBar; } void TransactionForm::slotActionSelected(int id) { emit newTransaction(static_cast(id)); } void TransactionForm::setupForm(const MyMoneyAccount& acc) { bool blocked = m_tabBar->blockSignals(true); // remove all tabs from the tabbar while (m_tabBar->count()) m_tabBar->removeTab(0); m_tabBar->show(); // important: one needs to add the new tabs first and then // change the identifier. Otherwise, addTab() will assign // a different value switch (acc.accountType()) { default: m_tabBar->insertTab(KMyMoneyRegister::ActionDeposit, i18n("&Deposit")); m_tabBar->insertTab(KMyMoneyRegister::ActionTransfer, i18n("&Transfer")); m_tabBar->insertTab(KMyMoneyRegister::ActionWithdrawal, i18n("&Withdrawal")); break; case eMyMoney::Account::CreditCard: m_tabBar->insertTab(KMyMoneyRegister::ActionDeposit, i18n("&Payment")); m_tabBar->insertTab(KMyMoneyRegister::ActionTransfer, i18n("&Transfer")); m_tabBar->insertTab(KMyMoneyRegister::ActionWithdrawal, i18n("&Charge")); break; case eMyMoney::Account::Liability: case eMyMoney::Account::Loan: m_tabBar->insertTab(KMyMoneyRegister::ActionDeposit, i18n("&Decrease")); m_tabBar->insertTab(KMyMoneyRegister::ActionTransfer, i18n("&Transfer")); m_tabBar->insertTab(KMyMoneyRegister::ActionWithdrawal, i18n("&Increase")); break; case eMyMoney::Account::Asset: case eMyMoney::Account::AssetLoan: m_tabBar->insertTab(KMyMoneyRegister::ActionDeposit, i18n("&Increase")); m_tabBar->insertTab(KMyMoneyRegister::ActionTransfer, i18n("&Transfer")); m_tabBar->insertTab(KMyMoneyRegister::ActionWithdrawal, i18n("&Decrease")); break; case eMyMoney::Account::Income: case eMyMoney::Account::Expense: case eMyMoney::Account::Investment: case eMyMoney::Account::Stock: m_tabBar->hide(); break; } m_tabBar->blockSignals(blocked); } void TransactionForm::resize(int col) { setUpdatesEnabled(false); // resize the register int w = viewport()->width(); int nc = columnCount(); // check which space we need if (nc >= LabelColumn1 && columnWidth(LabelColumn1)) adjustColumn(LabelColumn1); if (nc >= ValueColumn1 && columnWidth(ValueColumn1)) adjustColumn(ValueColumn1); if (nc >= LabelColumn2 && columnWidth(LabelColumn2)) adjustColumn(LabelColumn2); if (nc >= ValueColumn2 && columnWidth(ValueColumn2)) adjustColumn(ValueColumn2); - for (auto i = 0; i < nc; ++i) { + for (int i = 0; i < nc; ++i) { if (i == col) continue; w -= columnWidth(i); } if (col < nc && w >= 0) setColumnWidth(col, w); setUpdatesEnabled(true); } void TransactionForm::adjustColumn(Column col) { int w = 0; // preset the width of the right value column with the width of // the possible edit widgets so that they fit if they pop up if (col == ValueColumn2) { kMyMoneyDateInput dateInput; kMyMoneyEdit valInput; w = qMax(dateInput.sizeHint().width(), valInput.sizeHint().width()); } if (m_transaction) { QString txt; QFontMetrics fontMetrics(KMyMoneyGlobalSettings::listCellFont()); // scan through the rows for (int i = rowCount() - 1; i >= 0; --i) { Qt::Alignment align; int spacing = 10; m_transaction->formCellText(txt, align, i, static_cast(col), 0); QWidget* cw = cellWidget(i, col); if (cw) { w = qMax(w, cw->sizeHint().width() + spacing); // if the cell widget contains a push button increase the spacing used // for the cell text value to consider the size of the push button if (QPushButton *pushButton = cw->findChild()) { spacing += pushButton->sizeHint().width() + 5; } } w = qMax(w, fontMetrics.width(txt) + spacing); } } if (col < columnCount()) setColumnWidth(col, w); } void TransactionForm::arrangeEditWidgets(QMap& editWidgets, KMyMoneyRegister::Transaction* t) { t->arrangeWidgetsInForm(editWidgets); resize(ValueColumn1); } void TransactionForm::tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const { t->tabOrderInForm(tabOrderWidgets); } void TransactionForm::removeEditWidgets(QMap& editWidgets) { QMap::iterator it; for (it = editWidgets.begin(); it != editWidgets.end();) { if ((*it)->parentWidget() == this) { editWidgets.erase(it); it = editWidgets.begin(); } else ++it; } for (int row = 0; row < rowCount(); ++row) { for (int col = 0; col < columnCount(); ++col) { if (cellWidget(row, col)) { cellWidget(row, col)->hide(); setCellWidget(row, col, 0); } } } resize(ValueColumn1); // delete all remaining edit widgets (e.g. tabbar) for (it = editWidgets.begin(); it != editWidgets.end();) { delete(*it); // ->deleteLater(); editWidgets.erase(it); it = editWidgets.begin(); } } diff --git a/kmymoney/widgets/transactionform.h b/kmymoney/widgets/transactionform.h index 3500e0cb4..96400ef2d 100644 --- a/kmymoney/widgets/transactionform.h +++ b/kmymoney/widgets/transactionform.h @@ -1,242 +1,242 @@ /*************************************************************************** transactionform.h ---------- begin : Sun May 14 2006 copyright : (C) 2006 by Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef TRANSACTIONFORM_H #define TRANSACTIONFORM_H // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "registeritem.h" #include "transactioneditorcontainer.h" class MyMoneyAccount; namespace KMyMoneyRegister { class Transaction; } +namespace KMyMoneyTransactionForm { class TransactionForm; } namespace KMyMoneyTransactionForm { -class TransactionForm; /** * @author Thomas Baumgart */ class TabBar : public QTabBar { Q_OBJECT public: typedef enum { SignalNormal = 0, // standard signal behaviour SignalNever, // don't signal selection of a tab at all SignalAlways // always signal selection of a tab } SignalEmissionE; - explicit TabBar(QWidget* parent = nullptr); + explicit TabBar(QWidget* parent = 0); virtual ~TabBar() {} SignalEmissionE setSignalEmission(SignalEmissionE type); void copyTabs(const TabBar* otabbar); void insertTab(int id, const QString& title = QString()); void setIdentifier(QWidget* tab, int newId); void setTabEnabled(int id, bool enabled); int currentIndex() const; public slots: /** * overridden for internal reasons, API not changed */ virtual void setCurrentIndex(int); /** * overridden for internal reasons, API not changed */ virtual void showEvent(QShowEvent* event); protected: void mousePressEvent(QMouseEvent* event); protected slots: void slotTabCurrentChanged(int id); signals: void tabCurrentChanged(int id); private: /** * returns the Qt index of tab at pos @a p or -1 * Derived from QTabBarPrivate */ int indexAtPos(const QPoint& p) const; private: SignalEmissionE m_signalType; /** * maps our internal action ids to those used by * Qt/KDE. Since it does not seem possible to tell * Qt/KDE to use our ids everywhere (in QAccel) we * need to know which is which */ QMap m_idMap; }; typedef enum { LabelColumn1 = 0, ValueColumn1, LabelColumn2, ValueColumn2, // insert new values above this line MaxColumns } Column; class TransactionForm; class TransactionFormItemDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit TransactionFormItemDelegate(TransactionForm *parent); ~TransactionFormItemDelegate(); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; private: TransactionForm *m_transactionForm; }; /** * @author Thomas Baumgart */ class TransactionForm : public TransactionEditorContainer { Q_OBJECT public: - explicit TransactionForm(QWidget* parent = nullptr); + explicit TransactionForm(QWidget *parent = 0); virtual ~TransactionForm() {} /** * Override the QTable member function to avoid display of focus */ void paintFocus(QPainter* /*p*/, const QRect& /*cr*/) {} void adjustColumn(Column col); void clear(); void paintCell(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index); void arrangeEditWidgets(QMap& editWidgets, KMyMoneyRegister::Transaction* t); void removeEditWidgets(QMap& editWidgets); void tabOrder(QWidgetList& tabOrderWidgets, KMyMoneyRegister::Transaction* t) const; /** * reimplemented to prevent normal cell selection behavior */ void setCurrentCell(int, int) {} - TabBar* tabBar(QWidget* parent = nullptr); + TabBar* tabBar(QWidget* parent = 0); void setupForm(const MyMoneyAccount& acc); void enableTabBar(bool b); protected: /** * reimplemented to prevent normal mouse press behavior */ void contentsMousePressEvent(QMouseEvent* ev) { ev->ignore(); } /** * reimplemented to prevent normal mouse move behavior */ void contentsMouseMoveEvent(QMouseEvent* ev) { ev->ignore(); } /** * reimplemented to prevent normal mouse release behavior */ void contentsMouseReleaseEvent(QMouseEvent* ev) { ev->ignore(); } /** * reimplemented to prevent normal mouse double click behavior */ void contentsMouseDoubleClickEvent(QMouseEvent* ev) { ev->ignore(); } /** * reimplemented to prevent normal keyboard behavior */ void keyPressEvent(QKeyEvent* ev) { ev->ignore(); } /** * Override logic and use standard QFrame behaviour */ bool focusNextPrevChild(bool next); public slots: void slotSetTransaction(KMyMoneyRegister::Transaction* item); void resize(int col); protected slots: /** * Helper method to convert @a int into @a KMyMoneyRegister::Action */ void slotActionSelected(int); signals: /** * This signal is emitted when a user selects a tab. @a id * contains the tab's id (e.g. KMyMoneyRegister::ActionDeposit) */ void newTransaction(KMyMoneyRegister::Action id); protected: KMyMoneyRegister::Transaction* m_transaction; TabBar* m_tabBar; TransactionFormItemDelegate *m_itemDelegate; }; } // namespace #endif diff --git a/kmymoney/widgets/transactionsortoption.ui b/kmymoney/widgets/transactionsortoptiondecl.ui similarity index 98% rename from kmymoney/widgets/transactionsortoption.ui rename to kmymoney/widgets/transactionsortoptiondecl.ui index 7422dad0e..b25aa053d 100644 --- a/kmymoney/widgets/transactionsortoption.ui +++ b/kmymoney/widgets/transactionsortoptiondecl.ui @@ -1,209 +1,209 @@ Thomas Baumgart <ipwizard@users.sourceforge.net> - TransactionSortOption - + TransactionSortOptionDecl + 0 0 486 230 Sort options true true 20 67 QSizePolicy::Expanding Qt::Vertical 21 67 QSizePolicy::Expanding Qt::Vertical Sort order true true 20 67 QSizePolicy::Expanding Qt::Vertical 20 67 QSizePolicy::Expanding Qt::Vertical QListWidget QListWidget
klistwidget.h
m_addButton clicked() TransactionSortOptionDecl slotAddItem() m_availableList itemSelectionChanged() TransactionSortOptionDecl slotAvailableSelected() m_downButton clicked() TransactionSortOptionDecl slotDownItem() m_removeButton clicked() TransactionSortOptionDecl slotRemoveItem() m_selectedList itemSelectionChanged() TransactionSortOptionDecl slotSelectedSelected() m_upButton clicked() TransactionSortOptionDecl slotUpItem() m_selectedList itemDoubleClicked(QListWidgetItem*) TransactionSortOptionDecl toggleDirection(QListWidgetItem*)
diff --git a/kmymoney/widgets/transactionsortoption.cpp b/kmymoney/widgets/transactionsortoptionimpl.cpp similarity index 73% rename from kmymoney/widgets/transactionsortoption.cpp rename to kmymoney/widgets/transactionsortoptionimpl.cpp index ce7cf8e33..71e5149f5 100644 --- a/kmymoney/widgets/transactionsortoption.cpp +++ b/kmymoney/widgets/transactionsortoptionimpl.cpp @@ -1,322 +1,294 @@ /* This file is part of the KDE project Copyright (C) 2009 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "transactionsortoption.h" +#include "transactionsortoptionimpl.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes +#include "register.h" #include "icons/icons.h" -#include "widgetenums.h" -#include "ui_transactionsortoption.h" +#include "ui_transactionsortoptiondecl.h" using namespace Icons; -static const char * sortOrderText[] = { - I18N_NOOP2("Unknown sort order", "Unknown"), - I18N_NOOP("Post date"), - I18N_NOOP("Date entered"), - I18N_NOOP("Payee"), - I18N_NOOP("Amount"), - I18N_NOOP("Number"), - I18N_NOOP("Entry order"), - I18N_NOOP("Type"), - I18N_NOOP("Category"), - I18N_NOOP("Reconcile state"), - I18N_NOOP("Security") - // add new values above this comment line -}; - -TransactionSortOption::TransactionSortOption(QWidget *parent) : - QWidget(parent), - ui(new Ui::TransactionSortOption) +TransactionSortOption::TransactionSortOption(QWidget *parent) + : QWidget(parent) { + ui = new Ui::TransactionSortOptionDecl; ui->setupUi(this); + init(); +} + +TransactionSortOption::~TransactionSortOption() +{ + delete ui; +} +void TransactionSortOption::init() +{ ui->m_addButton->setIcon(QIcon::fromTheme(g_Icons[Icon::ArrowRight])); ui->m_removeButton->setIcon(QIcon::fromTheme(g_Icons[Icon::ArrowLeft])); ui->m_upButton->setIcon(QIcon::fromTheme(g_Icons[Icon::ArrowUp])); ui->m_downButton->setIcon(QIcon::fromTheme(g_Icons[Icon::ArrowDown])); // don't allow sorting of the selected entries ui->m_selectedList->setSortingEnabled(false); setSettings(QString()); // update UI when focus changes connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)), this, SLOT(slotFocusChanged(QWidget*,QWidget*))); } -TransactionSortOption::~TransactionSortOption() -{ - delete ui; -} - /** * Setup the two lists according to the elements found in @a list. * If an item is negative, it will show up in the available list, * if positive, it shows up in the selected list. * * Special care is taken about the two values @a EntryDateSort and * @a EntryOrderSort. These two entries cannot (should not) exist * alone. Inside this widget, only the @a EntryOrderSort is used. * * setSettings() takes care of hiding the @a EntryDateSort item and if * it exists in @p settings without @a EntryOrderSort being present, it * will add @a EntryOrderSort. */ void TransactionSortOption::setSettings(const QString& settings) { ui->m_availableList->clear(); ui->m_availableList->setCurrentItem(0); ui->m_selectedList->clear(); ui->m_selectedList->setCurrentItem(0); QStringList list = settings.split(',', QString::SkipEmptyParts); QMap selectedMap; // fill selected list QStringList::const_iterator it_s; QListWidgetItem* last = 0; int dateSign = 1; for (it_s = list.constBegin(); it_s != list.constEnd(); ++it_s) { int val = (*it_s).toInt(); selectedMap[abs(val)] = true; // skip EntryDateSort but keep sign - if (abs(val) == static_cast(eWidgets::SortField::EntryDate)) { + if (abs(val) == static_cast(KMyMoneyRegister::EntryDateSort)) { dateSign = (val < 0) ? -1 : 1; continue; } last = addEntry(ui->m_selectedList, last, val); } // make sure to create EntryOrderSort if missing but required - if (selectedMap.find(static_cast(eWidgets::SortField::EntryDate)) != selectedMap.end() - && selectedMap.find(static_cast(eWidgets::SortField::EntryOrder)) == selectedMap.end()) { - int val = dateSign * static_cast(eWidgets::SortField::EntryOrder); - selectedMap[static_cast(eWidgets::SortField::EntryOrder)] = true; + if (selectedMap.find(static_cast(KMyMoneyRegister::EntryDateSort)) != selectedMap.end() + && selectedMap.find(static_cast(KMyMoneyRegister::EntryOrderSort)) == selectedMap.end()) { + int val = dateSign * static_cast(KMyMoneyRegister::EntryOrderSort); + selectedMap[static_cast(KMyMoneyRegister::EntryOrderSort)] = true; last = addEntry(ui->m_selectedList, last, val); } // fill available list QMap::const_iterator it_m; - for (int i = static_cast(eWidgets::SortField::PostDate); - i < static_cast(eWidgets::SortField::MaxFields); ++i) { + for (int i = static_cast(KMyMoneyRegister::PostDateSort); + i < static_cast(KMyMoneyRegister::MaxSortFields); ++i) { // Never add EntryDateSort - if (i == static_cast(eWidgets::SortField::EntryDate)) + if (i == static_cast(KMyMoneyRegister::EntryDateSort)) continue; // Only add those, that are not present in the list of selected items if (selectedMap.find(i) == selectedMap.end()) { int val = i; - if (i == static_cast(eWidgets::SortField::Value)) + if (i == static_cast(KMyMoneyRegister::ValueSort)) val = -val; addEntry(ui->m_availableList, 0, val); } } // update the current item on the lists QListWidgetItem* p; if ((p = ui->m_availableList->item(0)) != 0) { ui->m_availableList->setCurrentItem(p); } if ((p = ui->m_selectedList->item(0)) != 0) { ui->m_selectedList->setCurrentItem(p); } slotAvailableSelected(); } QListWidgetItem* TransactionSortOption::addEntry(QListWidget* p, QListWidgetItem* after, int idx) { - auto txt = sortOrderToText(static_cast(abs(idx))); + QString txt = KMyMoneyRegister::sortOrderToText(static_cast(abs(idx))); if (txt.isEmpty()) txt = "Unknown"; // i18n should be handled in sortOptionToText() int row = p->row(after) + 1; p->insertItem(row, txt); - auto item = p->item(row); + QListWidgetItem* item = p->item(row); int direction = (idx >= 0) ? 1 : -1; item->setData(Qt::UserRole, QVariant(direction)); setDirectionIcon(item); return item; } void TransactionSortOption::toggleDirection(QListWidgetItem* item) { if (item) { int direction = item->data(Qt::UserRole).toInt() * (-1); item->setData(Qt::UserRole, QVariant(direction)); setDirectionIcon(item); emit settingsChanged(settings()); } } void TransactionSortOption::setDirectionIcon(QListWidgetItem* item) { if (item->data(Qt::UserRole).toInt() > 0) { item->setIcon(QIcon::fromTheme(g_Icons[Icon::SortAscending])); } else { item->setIcon(QIcon::fromTheme(g_Icons[Icon::SortDescending])); } } QString TransactionSortOption::settings() const { QString rc; - auto item = dynamic_cast(ui->m_selectedList->item(0)); + QListWidgetItem* item = dynamic_cast(ui->m_selectedList->item(0)); while (item) { - auto option = textToSortOrder(item->text()); + int option = KMyMoneyRegister::textToSortOrder(item->text()); // if we look at the EntryOrderSort option, we have to make // sure, that the EntryDateSort is prepended - if (option == eWidgets::SortField::EntryOrder) { - rc += QString::number(static_cast(eWidgets::SortField::EntryDate) * item->data(Qt::UserRole).toInt()) + ','; + if (option == KMyMoneyRegister::EntryOrderSort) { + rc += QString::number(static_cast(KMyMoneyRegister::EntryDateSort) * item->data(Qt::UserRole).toInt()) + ','; } - rc += QString::number((int)textToSortOrder(item->text()) * item->data(Qt::UserRole).toInt()); + rc += QString::number(KMyMoneyRegister::textToSortOrder(item->text()) * item->data(Qt::UserRole).toInt()); item = ui->m_selectedList->item(ui->m_selectedList->row(item) + 1); if (item != 0) rc += ','; } return rc; } void TransactionSortOption::slotFocusChanged(QWidget *o, QWidget *n) { Q_UNUSED(o); if (n == ui->m_availableList) slotAvailableSelected(); if (n == ui->m_selectedList) slotSelectedSelected(); } void TransactionSortOption::slotAvailableSelected() { - auto item = ui->m_availableList->currentItem(); + QListWidgetItem* item = ui->m_availableList->currentItem(); ui->m_addButton->setEnabled(item != 0); ui->m_removeButton->setDisabled(true); ui->m_upButton->setDisabled(true); ui->m_downButton->setDisabled(true); } void TransactionSortOption::slotSelectedSelected() { - auto item = ui->m_selectedList->currentItem(); + QListWidgetItem* item = ui->m_selectedList->currentItem(); ui->m_addButton->setDisabled(true); ui->m_removeButton->setEnabled(item != 0); if (item) { ui->m_upButton->setEnabled(ui->m_selectedList->row(item) != 0); ui->m_downButton->setEnabled(ui->m_selectedList->row(item) < ui->m_selectedList->count() - 1); } else { ui->m_upButton->setEnabled(false); ui->m_downButton->setEnabled(false); } } void TransactionSortOption::slotAddItem() { QListWidgetItem* item; if ((item = ui->m_availableList->currentItem()) != 0) { - auto next = ui->m_availableList->item(ui->m_availableList->row(item) + 1); + QListWidgetItem* next = ui->m_availableList->item(ui->m_availableList->row(item) + 1); if (!next) next = ui->m_availableList->item(ui->m_availableList->row(item) + 1); ui->m_availableList->takeItem(ui->m_availableList->row(item)); ui->m_selectedList->addItem(item); ui->m_addButton->setEnabled((ui->m_availableList->count() > 0)); if (next) { ui->m_availableList->setCurrentItem(next); } emit settingsChanged(settings()); } } void TransactionSortOption::slotRemoveItem() { QListWidgetItem* item; if ((item = ui->m_selectedList->currentItem()) != 0) { - auto next = ui->m_selectedList->item(ui->m_selectedList->row(item) + 1); + QListWidgetItem* next = ui->m_selectedList->item(ui->m_selectedList->row(item) + 1); if (!next) next = ui->m_selectedList->item(ui->m_selectedList->row(item) + 1); ui->m_selectedList->takeItem(ui->m_selectedList->row(item)); ui->m_availableList->addItem(item); ui->m_removeButton->setEnabled(ui->m_selectedList->count() > 0); if (next) { ui->m_selectedList->setCurrentItem(next); } emit settingsChanged(settings()); } } void TransactionSortOption::slotUpItem() { - auto item = ui->m_selectedList->currentItem(); - auto prev = ui->m_selectedList->item(ui->m_selectedList->row(item) - 1); + QListWidgetItem *item = ui->m_selectedList->currentItem(); + QListWidgetItem *prev = ui->m_selectedList->item(ui->m_selectedList->row(item) - 1); int prevRow = ui->m_selectedList->row(prev); if (prev) { ui->m_selectedList->takeItem(ui->m_selectedList->row(item)); ui->m_selectedList->insertItem(prevRow, item); ui->m_selectedList->setCurrentRow(ui->m_selectedList->row(item)); ui->m_upButton->setEnabled(ui->m_selectedList->row(item) > 0); ui->m_downButton->setEnabled(ui->m_selectedList->row(item) < ui->m_selectedList->count() - 1); emit settingsChanged(settings()); } } void TransactionSortOption::slotDownItem() { - auto item = ui->m_selectedList->currentItem(); - auto next = ui->m_selectedList->item(ui->m_selectedList->row(item) + 1); + QListWidgetItem *item = ui->m_selectedList->currentItem(); + QListWidgetItem *next = ui->m_selectedList->item(ui->m_selectedList->row(item) + 1); int nextRow = ui->m_selectedList->row(next); if (next) { ui->m_selectedList->takeItem(ui->m_selectedList->row(item)); ui->m_selectedList->insertItem(nextRow, item); ui->m_selectedList->setCurrentRow(ui->m_selectedList->row(item)); ui->m_upButton->setEnabled(ui->m_selectedList->row(item) > 0); ui->m_downButton->setEnabled(ui->m_selectedList->row(item) < ui->m_selectedList->count() - 1); emit settingsChanged(settings()); } } - -eWidgets::SortField TransactionSortOption::textToSortOrder(const QString& text) -{ - for (auto idx = 1; idx < static_cast(eWidgets::SortField::MaxFields); ++idx) { - if (text == i18n(sortOrderText[idx])) { - return static_cast(idx); - } - } - return eWidgets::SortField::Unknown; -} - -QString TransactionSortOption::sortOrderToText(eWidgets::SortField idx) -{ - if ((int)idx < (int)eWidgets::SortField::PostDate || (int)idx >= (int)eWidgets::SortField::MaxFields) - idx = eWidgets::SortField::Unknown; - return i18n(sortOrderText[(int)idx]); -} diff --git a/kmymoney/widgets/transactionsortoption.h b/kmymoney/widgets/transactionsortoptionimpl.h similarity index 85% rename from kmymoney/widgets/transactionsortoption.h rename to kmymoney/widgets/transactionsortoptionimpl.h index 56d6a7612..e3f76227a 100644 --- a/kmymoney/widgets/transactionsortoption.h +++ b/kmymoney/widgets/transactionsortoptionimpl.h @@ -1,75 +1,71 @@ /* This file is part of the KDE project Copyright (C) 2009 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TRANSACTIONSORTOPTIONIMPL_H #define TRANSACTIONSORTOPTIONIMPL_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes - -namespace Ui { class TransactionSortOption; } -namespace eWidgets { enum class SortField; } +namespace Ui +{ +class TransactionSortOptionDecl; +} class QListWidget; class QListWidgetItem; class TransactionSortOption : public QWidget { Q_OBJECT - Q_DISABLE_COPY(TransactionSortOption) - public: - explicit TransactionSortOption(QWidget *parent); + TransactionSortOption(QWidget *parent); ~TransactionSortOption(); QString settings() const; - public slots: void setSettings(const QString& settings); void toggleDirection(QListWidgetItem * item); protected: QListWidgetItem * addEntry(QListWidget * p, QListWidgetItem * after, int idx); void setDirectionIcon(QListWidgetItem* item); - protected slots: void slotAvailableSelected(); void slotSelectedSelected(); void slotAddItem(); void slotRemoveItem(); void slotUpItem(); void slotDownItem(); void slotFocusChanged(QWidget *o, QWidget *n); - +private: + void init(); signals: void settingsChanged(const QString&); private: - static eWidgets::SortField textToSortOrder(const QString& text); - static QString sortOrderToText(eWidgets::SortField idx); - Ui::TransactionSortOption *ui; + Ui::TransactionSortOptionDecl *ui; }; #endif /* TRANSACTIONSORTOPTIONIMPL_H */ diff --git a/kmymoney/widgets/widgetenums.h b/kmymoney/widgets/widgetenums.h deleted file mode 100644 index a1ac1fd14..000000000 --- a/kmymoney/widgets/widgetenums.h +++ /dev/null @@ -1,40 +0,0 @@ -/*************************************************************************** - widgetenums.h - ------------------- - copyright : (C) 2017 by Łukasz Wojniłowicz - -***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ - -#ifndef WIDGETENUMS_H -#define WIDGETENUMS_H - -namespace eWidgets { - - enum class SortField { - Unknown = 0, ///< unknown sort criteria - PostDate = 1, ///< sort by post date - EntryDate, ///< sort by entry date - Payee, ///< sort by payee name - Value, ///< sort by value - NoSort, ///< sort by number field - EntryOrder, ///< sort by entry order - Type, ///< sort by CashFlowDirection - Category, ///< sort by Category - ReconcileState, ///< sort by reconciliation state - Security, ///< sort by security (only useful for investment accounts) - // insert new values in front of this line - MaxFields - }; - -} - -#endif diff --git a/kmymoney/wizards/endingbalancedlg/interestchargecheckingswizardpagedecl.ui b/kmymoney/wizards/endingbalancedlg/interestchargecheckingswizardpagedecl.ui index 01b2a8a33..403d6cba2 100644 --- a/kmymoney/wizards/endingbalancedlg/interestchargecheckingswizardpagedecl.ui +++ b/kmymoney/wizards/endingbalancedlg/interestchargecheckingswizardpagedecl.ui @@ -1,219 +1,219 @@ InterestChargeCheckingsWizardPageDecl Interest / Charges If necessary, enter information about interest or charges here. KMyMoney will create transactions and cleares them directly for you. true QFrame::HLine QFrame::Sunken Qt::Vertical QSizePolicy::Expanding 16 16 Qt::Horizontal QSizePolicy::Expanding 60 20 0 0 Payee false 1 0 Interest 2 0 0 false Charges 2 0 0 false Qt::Horizontal QSizePolicy::Expanding 60 20 Qt::Vertical QSizePolicy::Expanding 16 16 KMyMoneyPayeeCombo KComboBox -
kmymoneypayeecombo.h
+
kmymoneymvccombo.h
1
diff --git a/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.cpp b/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.cpp index 33c314e3d..34ee3ed63 100644 --- a/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.cpp +++ b/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.cpp @@ -1,391 +1,389 @@ /*************************************************************************** kendingbalancedlg.cpp ------------------- copyright : (C) 2000,2003 by Michael Edwardes, Thomas Baumgart email : mte@users.sourceforge.net ipwizard@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kendingbalancedlg.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyutils.h" #include "kmymoneyedit.h" #include "mymoneysplit.h" #include "mymoneyfile.h" #include "mymoneyinstitution.h" #include "mymoneyaccount.h" #include "mymoneypayee.h" -#include "mymoneysecurity.h" #include "mymoneytransaction.h" -#include "mymoneytransactionfilter.h" #include "kmymoneycategory.h" #include "kmymoneyaccountselector.h" #include "kmymoneyutils.h" #include "kcurrencycalculator.h" #include "kmymoneysettings.h" class KEndingBalanceDlg::Private { public: explicit Private(int numPages) : m_pages(numPages, true) {} MyMoneyTransaction m_tInterest; MyMoneyTransaction m_tCharges; MyMoneyAccount m_account; QMap m_helpAnchor; QBitArray m_pages; }; KEndingBalanceDlg::KEndingBalanceDlg(const MyMoneyAccount& account, QWidget *parent) : KEndingBalanceDlgDecl(parent), d(new Private(Page_InterestChargeCheckings + 1)) { setModal(true); QString value; MyMoneyMoney endBalance, startBalance; d->m_account = account; MyMoneySecurity currency = MyMoneyFile::instance()->security(account.currencyId()); //FIXME: port m_statementInfoPageCheckings->m_enterInformationLabel->setText(QString("") + i18n("Please enter the following fields with the information as you find them on your statement. Make sure to enter all values in %1.", currency.name()) + QString("")); // If the previous reconciliation was postponed, // we show a different first page value = account.value("lastReconciledBalance"); if (value.isEmpty()) { // if the last statement has been entered long enough ago (more than one month), // then take the last statement date and add one month and use that as statement // date. QDate lastStatementDate = account.lastReconciliationDate(); if (lastStatementDate.addMonths(1) < QDate::currentDate()) { setField("statementDate", lastStatementDate.addMonths(1)); } slotUpdateBalances(); d->m_pages.clearBit(Page_PreviousPostpone); } else { d->m_pages.clearBit(Page_CheckingStart); d->m_pages.clearBit(Page_InterestChargeCheckings); //removePage(m_interestChargeCheckings); // make sure, we show the correct start page setStartId(Page_PreviousPostpone); MyMoneyMoney factor(1, 1); if (d->m_account.accountGroup() == eMyMoney::Account::Liability) factor = -factor; startBalance = MyMoneyMoney(value) * factor; value = account.value("statementBalance"); endBalance = MyMoneyMoney(value) * factor; //FIXME: port m_statementInfoPageCheckings->m_previousBalance->setValue(startBalance); m_statementInfoPageCheckings->m_endingBalance->setValue(endBalance); } // We don't need to add the default into the list (see ::help() why) // m_helpAnchor[m_startPageCheckings] = QString(QString()); d->m_helpAnchor[m_interestChargeCheckings] = QString("details.reconcile.wizard.interest"); d->m_helpAnchor[m_statementInfoPageCheckings] = QString("details.reconcile.wizard.statement"); value = account.value("statementDate"); if (!value.isEmpty()) setField("statementDate", QDate::fromString(value, Qt::ISODate)); //FIXME: port m_statementInfoPageCheckings->m_lastStatementDate->setText(QString()); if (account.lastReconciliationDate().isValid()) { m_statementInfoPageCheckings->m_lastStatementDate->setText(i18n("Last reconciled statement: %1", QLocale().toString(account.lastReconciliationDate()))); } // connect the signals with the slots connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotReloadEditWidgets())); connect(m_statementInfoPageCheckings->m_statementDate, SIGNAL(dateChanged(QDate)), this, SLOT(slotUpdateBalances())); connect(m_interestChargeCheckings->m_interestCategoryEdit, SIGNAL(createItem(QString,QString&)), this, SLOT(slotCreateInterestCategory(QString,QString&))); connect(m_interestChargeCheckings->m_chargesCategoryEdit, SIGNAL(createItem(QString,QString&)), this, SLOT(slotCreateChargesCategory(QString,QString&))); connect(m_interestChargeCheckings->m_payeeEdit, SIGNAL(createItem(QString,QString&)), this, SIGNAL(createPayee(QString,QString&))); KMyMoneyMVCCombo::setSubstringSearchForChildren(m_interestChargeCheckings, !KMyMoneySettings::stringMatchFromStart()); slotReloadEditWidgets(); // preset payee if possible try { // if we find a payee with the same name as the institution, // than this is what we use as payee. if (!d->m_account.institutionId().isEmpty()) { MyMoneyInstitution inst = MyMoneyFile::instance()->institution(d->m_account.institutionId()); MyMoneyPayee payee = MyMoneyFile::instance()->payeeByName(inst.name()); setField("payeeEdit", payee.id()); } } catch (const MyMoneyException &) { } KMyMoneyUtils::updateWizardButtons(this); // setup different text and icon on finish button setButtonText(QWizard::FinishButton, KStandardGuiItem::cont().text()); button(QWizard::FinishButton)->setIcon(KStandardGuiItem::cont().icon()); } KEndingBalanceDlg::~KEndingBalanceDlg() { delete d; } void KEndingBalanceDlg::slotUpdateBalances() { MYMONEYTRACER(tracer); // determine the beginning balance and ending balance based on the following // forumulas: // // end balance = current balance - sum(all non cleared transactions) // - sum(all cleared transactions posted // after statement date) // start balance = end balance - sum(all cleared transactions // up to statement date) MyMoneyTransactionFilter filter(d->m_account.id()); filter.addState((int)eMyMoney::TransactionFilter::State::NotReconciled); filter.setReportAllSplits(true); QList > transactionList; QList >::const_iterator it; // retrieve the list from the engine MyMoneyFile::instance()->transactionList(transactionList, filter); //first retrieve the oldest not reconciled transaction QDate oldestTransactionDate; it = transactionList.constBegin(); if (it != transactionList.constEnd()) { oldestTransactionDate = (*it).first.postDate(); m_statementInfoPageCheckings->m_oldestTransactionDate->setText(i18n("Oldest unmarked transaction: %1", QLocale().toString(oldestTransactionDate))); } filter.addState((int)eMyMoney::TransactionFilter::State::Cleared); // retrieve the list from the engine to calculate the starting and ending balance MyMoneyFile::instance()->transactionList(transactionList, filter); MyMoneyMoney balance = MyMoneyFile::instance()->balance(d->m_account.id()); MyMoneyMoney factor(1, 1); if (d->m_account.accountGroup() == eMyMoney::Account::Liability) factor = -factor; MyMoneyMoney endBalance, startBalance; balance = balance * factor; endBalance = startBalance = balance; tracer.printf("total balance = %s", qPrintable(endBalance.formatMoney(QString(), 2))); for (it = transactionList.constBegin(); it != transactionList.constEnd(); ++it) { const MyMoneySplit& split = (*it).second; balance -= split.shares() * factor; if ((*it).first.postDate() > field("statementDate").toDate()) { tracer.printf("Reducing balances by %s because postdate of %s/%s(%s) is past statement date", qPrintable((split.shares() * factor).formatMoney(QString(), 2)), qPrintable((*it).first.id()), qPrintable(split.id()), qPrintable((*it).first.postDate().toString(Qt::ISODate))); endBalance -= split.shares() * factor; startBalance -= split.shares() * factor; } else { switch (split.reconcileFlag()) { case eMyMoney::Split::State::NotReconciled: tracer.printf("Reducing balances by %s because %s/%s(%s) is not reconciled", qPrintable((split.shares() * factor).formatMoney(QString(), 2)), qPrintable((*it).first.id()), qPrintable(split.id()), qPrintable((*it).first.postDate().toString(Qt::ISODate))); endBalance -= split.shares() * factor; startBalance -= split.shares() * factor; break; case eMyMoney::Split::State::Cleared: tracer.printf("Reducing start balance by %s because %s/%s(%s) is cleared", qPrintable((split.shares() * factor).formatMoney(QString(), 2)), qPrintable((*it).first.id()), qPrintable(split.id()), qPrintable((*it).first.postDate().toString(Qt::ISODate))); startBalance -= split.shares() * factor; break; default: break; } } } //FIXME: port m_statementInfoPageCheckings->m_previousBalance->setValue(startBalance); m_statementInfoPageCheckings->m_endingBalance->setValue(endBalance); tracer.printf("total balance = %s", qPrintable(endBalance.formatMoney(QString(), 2))); tracer.printf("start balance = %s", qPrintable(startBalance.formatMoney(QString(), 2))); setField("interestDateEdit", field("statementDate").toDate()); setField("chargesDateEdit", field("statementDate").toDate()); } void KEndingBalanceDlg::accept() { if ((!field("interestEditValid").toBool() || createTransaction(d->m_tInterest, -1, field("interestEdit").value(), field("interestCategoryEdit").toString(), field("interestDateEdit").toDate())) && (!field("chargesEditValid").toBool() || createTransaction(d->m_tCharges, 1, field("chargesEdit").value(), field("chargesCategoryEdit").toString(), field("chargesDateEdit").toDate()))) KEndingBalanceDlgDecl::accept(); } void KEndingBalanceDlg::slotCreateInterestCategory(const QString& txt, QString& id) { createCategory(txt, id, MyMoneyFile::instance()->income()); } void KEndingBalanceDlg::slotCreateChargesCategory(const QString& txt, QString& id) { createCategory(txt, id, MyMoneyFile::instance()->expense()); } void KEndingBalanceDlg::createCategory(const QString& txt, QString& id, const MyMoneyAccount& parent) { MyMoneyAccount acc; acc.setName(txt); emit createCategory(acc, parent); id = acc.id(); } const MyMoneyMoney KEndingBalanceDlg::endingBalance() const { return adjustedReturnValue(m_statementInfoPageCheckings->m_endingBalance->value()); } const MyMoneyMoney KEndingBalanceDlg::previousBalance() const { return adjustedReturnValue(m_statementInfoPageCheckings->m_previousBalance->value()); } const MyMoneyMoney KEndingBalanceDlg::adjustedReturnValue(const MyMoneyMoney& v) const { return d->m_account.accountGroup() == eMyMoney::Account::Liability ? -v : v; } void KEndingBalanceDlg::slotReloadEditWidgets() { QString payeeId, interestId, chargesId; // keep current selected items payeeId = field("payeeEdit").toString(); interestId = field("interestCategoryEdit").toString(); chargesId = field("chargesCategoryEdit").toString(); // load the payee and category widgets with data from the engine //FIXME: port m_interestChargeCheckings->m_payeeEdit->loadPayees(MyMoneyFile::instance()->payeeList()); // a user request to show all categories in both selectors due to a valid use case. AccountSet aSet; aSet.addAccountGroup(eMyMoney::Account::Expense); aSet.addAccountGroup(eMyMoney::Account::Income); //FIXME: port aSet.load(m_interestChargeCheckings->m_interestCategoryEdit->selector()); aSet.load(m_interestChargeCheckings->m_chargesCategoryEdit->selector()); // reselect currently selected items if (!payeeId.isEmpty()) setField("payeeEdit", payeeId); if (!interestId.isEmpty()) setField("interestCategoryEdit", interestId); if (!chargesId.isEmpty()) setField("chargesCategoryEdit", chargesId); } const MyMoneyTransaction KEndingBalanceDlg::interestTransaction() { return d->m_tInterest; } const MyMoneyTransaction KEndingBalanceDlg::chargeTransaction() { return d->m_tCharges; } bool KEndingBalanceDlg::createTransaction(MyMoneyTransaction &t, const int sign, const MyMoneyMoney& amount, const QString& category, const QDate& date) { t = MyMoneyTransaction(); if (category.isEmpty() || !date.isValid()) return true; MyMoneySplit s1, s2; MyMoneyMoney val = amount * MyMoneyMoney(sign, 1); try { t.setPostDate(date); t.setCommodity(d->m_account.currencyId()); s1.setPayeeId(field("payeeEdit").toString()); s1.setReconcileFlag(eMyMoney::Split::State::Cleared); s1.setAccountId(d->m_account.id()); s1.setValue(-val); s1.setShares(-val); s2 = s1; s2.setAccountId(category); s2.setValue(val); t.addSplit(s1); t.addSplit(s2); QMap priceInfo; // just empty MyMoneyMoney shares; if (!KCurrencyCalculator::setupSplitPrice(shares, t, s2, priceInfo, this)) { t = MyMoneyTransaction(); return false; } s2.setShares(shares); t.modifySplit(s2); } catch (const MyMoneyException &e) { qDebug("%s", qPrintable(e.what())); t = MyMoneyTransaction(); return false; } return true; } void KEndingBalanceDlg::help() { QString anchor = d->m_helpAnchor[currentPage()]; if (anchor.isEmpty()) anchor = QString("details.reconcile.whatis"); KHelpClient::invokeHelp(anchor); } int KEndingBalanceDlg::nextId() const { // Starting from the current page, look for the first enabled page // and return that value // If the end of the list is encountered first, then return -1. for (int i = currentId() + 1; i < d->m_pages.size() && i < pageIds().size(); ++i) { if (d->m_pages.testBit(i)) return pageIds()[i]; } return -1; } diff --git a/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.h b/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.h index cfb39d841..f20fa6ac6 100644 --- a/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.h +++ b/kmymoney/wizards/endingbalancedlg/kendingbalancedlg.h @@ -1,114 +1,112 @@ /*************************************************************************** kendingbalancedlg.h ------------------- copyright : (C) 2000 by Michael Edwardes email : mte@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef KENDINGBALANCEDLG_H #define KENDINGBALANCEDLG_H // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "ui_kendingbalancedlgdecl.h" -#include "mymoneyaccount.h" - class QDate; class MyMoneyAccount; class MyMoneyTransaction; /** * This dialog is wizard based and used to enter additional * information required to start the reconciliation process. * This version implements the behaviour for checkings, * savings and credit card accounts. * * @author Thomas Baumgart */ class KEndingBalanceDlgDecl : public QWizard, public Ui::KEndingBalanceDlgDecl { public: KEndingBalanceDlgDecl(QWidget *parent) : QWizard(parent) { setupUi(this); } }; class KEndingBalanceDlg : public KEndingBalanceDlgDecl { Q_OBJECT public: enum { Page_CheckingStart, Page_PreviousPostpone, Page_CheckingStatementInfo, Page_InterestChargeCheckings }; explicit KEndingBalanceDlg(const MyMoneyAccount& account, QWidget *parent = 0); ~KEndingBalanceDlg(); const MyMoneyMoney endingBalance() const; const MyMoneyMoney previousBalance() const; const QDate statementDate() const { return field("statementDate").toDate(); }; const MyMoneyTransaction interestTransaction(); const MyMoneyTransaction chargeTransaction(); /** * This method returns the id of the next page in the wizard. * It is overloaded here to support the dynamic nature of this wizard. * * @return id of the next page or -1 if there is no next page */ int nextId() const; protected: bool createTransaction(MyMoneyTransaction& t, const int sign, const MyMoneyMoney& amount, const QString& category, const QDate& date); const MyMoneyMoney adjustedReturnValue(const MyMoneyMoney& v) const; void createCategory(const QString& txt, QString& id, const MyMoneyAccount& parent); protected slots: void slotReloadEditWidgets(); void help(); void slotCreateInterestCategory(const QString& txt, QString& id); void slotCreateChargesCategory(const QString& txt, QString& id); void accept(); void slotUpdateBalances(); signals: /** * proxy signal for KMyMoneyPayeeCombo::createItem(const QString&, QString&) */ void createPayee(const QString&, QString&); /** * emit when a category is about to be created */ void createCategory(MyMoneyAccount& acc, const MyMoneyAccount& parent); private: /// \internal d-pointer class. class Private; /// \internal d-pointer instance. Private* const d; }; #endif diff --git a/kmymoney/wizards/endingbalancedlg/kendingbalancedlgdecl.ui b/kmymoney/wizards/endingbalancedlg/kendingbalancedlgdecl.ui index f0ec054bc..1ab6ea801 100644 --- a/kmymoney/wizards/endingbalancedlg/kendingbalancedlgdecl.ui +++ b/kmymoney/wizards/endingbalancedlg/kendingbalancedlgdecl.ui @@ -1,61 +1,61 @@ KEndingBalanceDlgDecl 0 0 589 465 Reconciliation Wizard KComboBox QComboBox
kcombobox.h
kMyMoneyDateInput QFrame
kmymoneydateinput.h
KMyMoneyCategory KComboBox
kmymoneycategory.h
kMyMoneyEdit QFrame
kmymoneyedit.h
KMyMoneyPayeeCombo KComboBox -
kmymoneypayeecombo.h
+
kmymoneymvccombo.h
1
kMyMoneyAccountSelector QWidget
kmymoneyaccountselector.h
diff --git a/kmymoney/wizards/newaccountwizard/kaccounttypepagedecl.ui b/kmymoney/wizards/newaccountwizard/kaccounttypepagedecl.ui index 70be51d16..6d748cfaa 100644 --- a/kmymoney/wizards/newaccountwizard/kaccounttypepagedecl.ui +++ b/kmymoney/wizards/newaccountwizard/kaccounttypepagedecl.ui @@ -1,317 +1,317 @@ KAccountTypePageDecl 0 0 555 404 10 Qt::NoFocus Enter the name of the account under which it is known within KMyMoney. Select the type for the new account. Use the <b>What's this?</b> feature to see more details about the various account types. Enter the date the account was opened and its currency. true Account name false 0 0 Enter a name under which this account is known within KMyMoney. Account type false 0 0 <p><b>Checking</b> Use the checking account type to manage activities on your checking account e.g. payments, checks and cash card purchases.</p> <p><b>Savings</b> Use the savings account type to manage activities on your savings account.</p> <p><b>Credit Card</b> Use the credit card account type to manage activities on your credit card.</p> <p><b>Cash</b> Use the cash account type to manage activities in your wallet.</p> <p><b>Loan</b> Use the loan account type to manage amortization loans (e.g. mortgages, car loan, money you lend, private loans etc.).</p> <p><b>Investment</b> Use the investment account to manage your stock, mutual fund and other investments.</p> <p><b>Asset</b> Use the asset account type to manage assets (e.g. your house, car or art collection).</p> <p><b>Liability</b> Use the liability account type to manage any type of liability except amortization loans. Use it for taxes you owe or money you borrowed from friends. For amortization loans like mortgages you should create a loan account.</p> Currency false 0 0 Select the currency in which this account is denominated. Qt::Horizontal QSizePolicy::Expanding 231 20 Opening date false Qt::StrongFocus <p>The opening date would be the date of the last statement. In case you want to keep track of transactions prior to this date, enter the ending balance and statement date of a prior statement and add all following transactions into KMyMoney. KMyMoney will not allow you to enter or modify transactions prior to this date.</p> <p><i>Note</i>: If you do not know the exact amount of the opening balance, enter an estimate. You can change this value later before you reconcile this account for the first time.</p> Opening balance false 150 0 Qt::StrongFocus <p>The opening balance is the balance of the account when you start using it with KMyMoney. For new accounts this is usually 0 but for existing accounts this may well differ. Please consult the account statements to figure out this value. The opening balance is to be provided in the currency of the account as selected with the currency button.</p> <p><i>Note</i>: If you do not know the exact amount of the opening balance, enter an estimate. You can change this value later before you reconcile this account for the first time.</p> false Conversion rate false Qt::StrongFocus For foreign currencies an initial conversion rate needs to be provided. This should be the price of the foreign currency in the base currency on the opening date of the account. false Online quote Qt::Horizontal QSizePolicy::Expanding 61 21 xxx false Mark this checkbox if the account should be maintained as preferred account Selecting the <b>Preferred Account</b> checkbox will allow preferred access in some dialogs and views of KMyMoney. Preferred account Qt::Vertical QSizePolicy::Expanding 20 16 kMyMoneyDateInput QFrame
kmymoneydateinput.h
KMyMoneySecuritySelector KComboBox
kmymoneycurrencyselector.h
1
kMyMoneyEdit QFrame
kmymoneyedit.h
KMyMoneyGeneralCombo KComboBox -
kmymoneygeneralcombo.h
+
kmymoneymvccombo.h
1
KLineEdit QLineEdit
klineedit.h
KComboBox QComboBox
kcombobox.h
diff --git a/kmymoney/wizards/newaccountwizard/kgeneralloaninfopagedecl.ui b/kmymoney/wizards/newaccountwizard/kgeneralloaninfopagedecl.ui index 82d622a02..af6ea984e 100644 --- a/kmymoney/wizards/newaccountwizard/kgeneralloaninfopagedecl.ui +++ b/kmymoney/wizards/newaccountwizard/kgeneralloaninfopagedecl.ui @@ -1,375 +1,375 @@ KGeneralLoanInfoPageDecl 0 0 675 494 Qt::NoFocus Please select the required options. Please use the <b>What's this?</b> feature to see more information about the items. true What is the type of the loan? false I am borrowing money I am lending money Who is the payee/payer of the loan? false Enter the name of the person or bank you owe money or who owes you money. If the name does not exist within KMyMoney's database, you are asked if you want to create it. Did you make/receive any payments yet? false Were there any payments for this loan whether they are entered into KMyMoney or not? Note: Payments made to obtain the loan (e.g. Disagio) are not considered as payments in this context. No Yes Which payments do you want to record? false <qt>Select which transactions should be recorded. <b>All payments</b> allows you to enter all payments made for this loan. The option to <b>start from the beginning of the current year</b> is meant for loans that have already been active for a longer period of time, and for which you do not want to enter all past transactions. </qt> All payments Start with this year's payments Balance before start of recording Enter the ending balance found on the statement that is the last one before you want to start recording this loan in KMyMoney. false You have selected to record only payments from the beginning of this year. Since prior transactions will not be recorded, you need to supply the balance of the loan on January 1st of this year. Payment frequency false Qt::TabFocus Interest Compounding frequency false Select the frequency with which the interest is compounded. If uncertain, select the same as the payment frequency. Consult your loan contract for details. Due date of first payment to be recorded false Qt::StrongFocus Depending on the answer to <b>What do you want to record?</b> this field means the following:<p> <b>All payments</b><br> Enter the due date of the very first payment<p> <b>Start with this year's payments</b><br> Enter the due date of the first payment in the current year Type of interest rate false Fixed Variable Time between interest changes false 1 999 3 Days Weeks Months Years Next interest change is due false Qt::StrongFocus 29 20 QSizePolicy::Expanding Qt::Horizontal 20 16 QSizePolicy::Expanding Qt::Vertical KComboBox QComboBox
kcombobox.h
QSpinBox QSpinBox
knuminput.h
KMyMoneyPayeeCombo QWidget -
kmymoneypayeecombo.h
+
kmymoneymvccombo.h
1
KMyMoneyFrequencyCombo QWidget -
kmymoneyfrequencycombo.h
+
kmymoneymvccombo.h
1
diff --git a/kmymoney/wizards/newaccountwizard/kloandetailspagedecl.ui b/kmymoney/wizards/newaccountwizard/kloandetailspagedecl.ui index dfd44622b..221ee4da9 100644 --- a/kmymoney/wizards/newaccountwizard/kloandetailspagedecl.ui +++ b/kmymoney/wizards/newaccountwizard/kloandetailspagedecl.ui @@ -1,278 +1,278 @@ KLoanDetailsPageDecl 0 0 592 434 Qt::NoFocus Now enter the details for your loan. You can leave one of the fields empty and KMyMoney will calculate it when you press the <b>Calculate</b> button. Before you can continue with the next page you also need to press <b>Calculate</b> so that KMyMoney can check the logical correctness of the values you have supplied. true 20 70 QSizePolicy::Expanding Qt::Vertical The interest rate gets calculated false when the payment is received when the payment is due Loan amount false true Enter the amount of the loan in this field. If you want to calculate this field out of the other parameters, please leave it empty. If the field is marked as required (colored background) you have informed KMyMoney about the fact that there were already some payments towards the loan. In this case, please enter the ending balance of your last statement. Interest rate false Please enter the interest rate (as a percentage) or leave the field empty to calculate it. Term false 999 Please enter the term of this loan or enter 0 to calculate it. The term is the time that is required to fully repay the loan. This time might be different from the time your loan contract is signed for. Payment (principal and interest) false Please enter the amount you pay for principal and interest or leave the field empty to calculate it. Balloon payment false Please enter the amount of a final amortization payment or leave the field empty to calculate it. 71 20 QSizePolicy::Expanding Qt::Horizontal Calculate Press this button to calculate/verify your loan details. Pressing this button calculates a possibly missing parameter for your loan or verifies that the values entered match. If something is not correct you will receive information about it. 20 16 QSizePolicy::Expanding Qt::Vertical 27 21 QSizePolicy::Expanding Qt::Horizontal 20 16 QSizePolicy::Expanding Qt::Vertical KComboBox QComboBox
kcombobox.h
QSpinBox QSpinBox
knuminput.h
KMyMoneyFrequencyCombo QWidget -
kmymoneyfrequencycombo.h
+
kmymoneymvccombo.h
1
diff --git a/kmymoney/wizards/newaccountwizard/knewaccountwizard.cpp b/kmymoney/wizards/newaccountwizard/knewaccountwizard.cpp index 2f7fe7ce6..0c20bffa6 100644 --- a/kmymoney/wizards/newaccountwizard/knewaccountwizard.cpp +++ b/kmymoney/wizards/newaccountwizard/knewaccountwizard.cpp @@ -1,1675 +1,1673 @@ /*************************************************************************** knewaccountwizard.cpp ------------------- begin : Tue Sep 25 2006 copyright : (C) 2007 Thomas Baumgart email : Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "knewaccountwizard.h" #include "knewaccountwizard_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include -#include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneycurrencyselector.h" #include "kmymoneyaccountselector.h" #include "mymoneyfinancialcalculator.h" #include "kcurrencycalculator.h" #include "kmymoneyglobalsettings.h" #include #include "mymoneyutils.h" #include "ksplittransactiondlg.h" #include "kequitypriceupdatedlg.h" #include "accountsmodel.h" #include "accountsproxymodel.h" #include "models.h" #include "modelenums.h" #include "icons.h" #include "mymoneyfile.h" #include "mymoneyinstitution.h" #include "mymoneyaccountloan.h" #include "mymoneypayee.h" #include "mymoneyprice.h" -#include "mymoneysplit.h" #include "mymoneytransaction.h" using namespace NewAccountWizard; using namespace Icons; using namespace eMyMoney; namespace NewAccountWizard { enum steps { StepInstitution = 1, StepAccount, StepBroker, StepDetails, StepPayments, StepFees, StepSchedule, StepPayout, StepParentAccount, StepFinish }; Wizard::Wizard(QWidget *parent, bool modal, Qt::WindowFlags flags) : KMyMoneyWizard(parent, modal, flags) { setTitle(i18n("KMyMoney New Account Setup")); addStep(i18n("Institution")); addStep(i18n("Account")); addStep(i18n("Broker")); addStep(i18n("Details")); addStep(i18n("Payments")); addStep(i18n("Fees")); addStep(i18n("Schedule")); addStep(i18n("Payout")); addStep(i18n("Parent Account")); addStep(i18nc("Finish the wizard", "Finish")); setStepHidden(StepBroker); setStepHidden(StepSchedule); setStepHidden(StepPayout); setStepHidden(StepDetails); setStepHidden(StepPayments); setStepHidden(StepFees); m_institutionPage = new InstitutionPage(this); m_accountTypePage = new AccountTypePage(this); // Investment Pages m_brokeragepage = new BrokeragePage(this); // Credit Card Pages m_schedulePage = new CreditCardSchedulePage(this); // Loan Pages m_generalLoanInfoPage = new GeneralLoanInfoPage(this); m_loanDetailsPage = new LoanDetailsPage(this); m_loanPaymentPage = new LoanPaymentPage(this); m_loanSchedulePage = new LoanSchedulePage(this); m_loanPayoutPage = new LoanPayoutPage(this); // Not a loan page m_hierarchyPage = new HierarchyPage(this); // Finish m_accountSummaryPage = new AccountSummaryPage(this); setFirstPage(m_institutionPage); } void Wizard::setAccount(const MyMoneyAccount& acc) { m_account = acc; m_accountTypePage->setAccount(m_account); if (!acc.institutionId().isEmpty()) { m_institutionPage->selectExistingInstitution(acc.institutionId()); } } const MyMoneySecurity& Wizard::currency() const { return m_accountTypePage->currency(); } MyMoneyMoney Wizard::interestRate() const { return m_loanDetailsPage->m_interestRate->value() / MyMoneyMoney(100, 1); } int Wizard::precision() const { return MyMoneyMoney::denomToPrec(currency().smallestAccountFraction()); } const MyMoneyAccount& Wizard::account() { m_account = MyMoneyAccountLoan(); m_account.setName(m_accountTypePage->m_accountName->text()); m_account.setOpeningDate(m_accountTypePage->m_openingDate->date()); m_account.setAccountType(m_accountTypePage->accountType()); m_account.setInstitutionId(m_institutionPage->institution().id()); m_account.setNumber(m_institutionPage->m_accountNumber->text()); m_account.setValue("iban", m_institutionPage->m_iban->text()); if (m_accountTypePage->m_preferredAccount->isChecked()) m_account.setValue("PreferredAccount", "Yes"); else m_account.deletePair("PreferredAccount"); m_account.setCurrencyId(currency().id()); if (m_account.isLoan()) { // in case we lend the money we adjust the account type if (!moneyBorrowed()) m_account.setAccountType(Account::AssetLoan); m_account.setLoanAmount(m_loanDetailsPage->m_loanAmount->value()); m_account.setInterestRate(m_loanSchedulePage->firstPaymentDueDate(), m_loanDetailsPage->m_interestRate->value()); m_account.setInterestCalculation(m_loanDetailsPage->m_paymentDue->currentIndex() == 0 ? MyMoneyAccountLoan::paymentReceived : MyMoneyAccountLoan::paymentDue); m_account.setFixedInterestRate(m_generalLoanInfoPage->m_interestType->currentIndex() == 0); m_account.setFinalPayment(m_loanDetailsPage->m_balloonAmount->value()); m_account.setTerm(m_loanDetailsPage->term()); m_account.setPeriodicPayment(m_loanDetailsPage->m_paymentAmount->value()); m_account.setPayee(m_generalLoanInfoPage->m_payee->selectedItem()); m_account.setInterestCompounding((int)m_generalLoanInfoPage->m_compoundFrequency->currentItem()); if (!m_account.fixedInterestRate()) { m_account.setNextInterestChange(m_generalLoanInfoPage->m_interestChangeDateEdit->date()); m_account.setInterestChangeFrequency(m_generalLoanInfoPage->m_interestFrequencyAmountEdit->value(), m_generalLoanInfoPage->m_interestFrequencyUnitEdit->currentIndex()); } } return m_account; } MyMoneyTransaction Wizard::payoutTransaction() { MyMoneyTransaction t; if (m_account.isLoan() // we're creating a loan && openingBalance().isZero() // and don't have an opening balance && !m_loanPayoutPage->m_noPayoutTransaction->isChecked()) { // and the user wants to have a payout transaction t.setPostDate(m_loanPayoutPage->m_payoutDate->date()); t.setCommodity(m_account.currencyId()); MyMoneySplit s; s.setAccountId(m_account.id()); s.setShares(m_loanDetailsPage->m_loanAmount->value()); if (moneyBorrowed()) s.setShares(-s.shares()); s.setValue(s.shares()); t.addSplit(s); s.clearId(); s.setValue(-s.value()); s.setAccountId(m_loanPayoutPage->payoutAccountId()); MyMoneyMoney shares; KCurrencyCalculator::setupSplitPrice(shares, t, s, QMap(), this); s.setShares(shares); t.addSplit(s); } return t; } const MyMoneyAccount& Wizard::parentAccount() { return m_accountTypePage->allowsParentAccount() ? m_hierarchyPage->parentAccount() : (m_accountTypePage->accountType() == Account::Loan ? m_generalLoanInfoPage->parentAccount() : m_accountTypePage->parentAccount()); } MyMoneyAccount Wizard::brokerageAccount() const { MyMoneyAccount account; if (m_account.accountType() == Account::Investment && m_brokeragepage->m_createBrokerageButton->isChecked()) { account.setName(m_account.brokerageName()); account.setAccountType(Account::Checkings); account.setInstitutionId(m_account.institutionId()); account.setOpeningDate(m_account.openingDate()); account.setCurrencyId(m_brokeragepage->m_brokerageCurrency->security().id()); if (m_brokeragepage->m_accountNumber->isEnabled() && !m_brokeragepage->m_accountNumber->text().isEmpty()) account.setNumber(m_brokeragepage->m_accountNumber->text()); if (m_brokeragepage->m_iban->isEnabled() && !m_brokeragepage->m_iban->text().isEmpty()) account.setValue("iban", m_brokeragepage->m_iban->text()); } return account; } const MyMoneySchedule& Wizard::schedule() { m_schedule = MyMoneySchedule(); if (!m_account.id().isEmpty()) { if (m_schedulePage->m_reminderCheckBox->isChecked() && (m_account.accountType() == Account::CreditCard)) { m_schedule.setName(m_schedulePage->m_name->text()); m_schedule.setType(Schedule::Type::Transfer); m_schedule.setPaymentType(static_cast(m_schedulePage->m_method->currentItem())); m_schedule.setFixed(false); m_schedule.setOccurrencePeriod(Schedule::Occurrence::Monthly); m_schedule.setOccurrenceMultiplier(1); MyMoneyTransaction t; MyMoneySplit s; s.setPayeeId(m_schedulePage->m_payee->selectedItem()); s.setAccountId(m_schedulePage->m_paymentAccount->selectedItem()); s.setMemo(i18n("Credit card payment")); s.setShares(-m_schedulePage->m_amount->value()); s.setValue(s.shares()); t.addSplit(s); s.clearId(); s.setAccountId(m_account.id()); s.setShares(m_schedulePage->m_amount->value()); s.setValue(s.shares()); t.addSplit(s); // setup the next due date t.setPostDate(m_schedulePage->m_date->date()); m_schedule.setTransaction(t); } else if (m_account.isLoan()) { m_schedule.setName(i18n("Loan payment for %1", m_accountTypePage->m_accountName->text())); m_schedule.setType(Schedule::Type::LoanPayment); if (moneyBorrowed()) m_schedule.setPaymentType(Schedule::PaymentType::DirectDebit); else m_schedule.setPaymentType(Schedule::PaymentType::DirectDeposit); m_schedule.setFixed(true); m_schedule.setOccurrence(m_generalLoanInfoPage->m_paymentFrequency->currentItem()); MyMoneyTransaction t; t.setCommodity(m_account.currencyId()); MyMoneySplit s; // payment split s.setPayeeId(m_generalLoanInfoPage->m_payee->selectedItem()); s.setAccountId(m_loanSchedulePage->m_paymentAccount->selectedItem()); s.setMemo(i18n("Loan payment")); s.setValue(m_loanPaymentPage->basePayment() + m_loanPaymentPage->additionalFees()); if (moneyBorrowed()) { s.setValue(-s.value()); } s.setShares(s.value()); if (m_account.id() != QLatin1String("Phony-ID")) { // if the real account is already set perform the currency conversion if it's necessary MyMoneyMoney paymentShares; KCurrencyCalculator::setupSplitPrice(paymentShares, t, s, QMap(), this); s.setShares(paymentShares); } t.addSplit(s); // principal split s.clearId(); s.setAccountId(m_account.id()); s.setShares(MyMoneyMoney::autoCalc); s.setValue(MyMoneyMoney::autoCalc); s.setMemo(i18n("Amortization")); s.setAction(MyMoneySplit::ActionAmortization); t.addSplit(s); // interest split //only add if interest is above zero if (!m_loanDetailsPage->m_interestRate->value().isZero()) { s.clearId(); s.setAccountId(m_loanSchedulePage->m_interestCategory->selectedItem()); s.setShares(MyMoneyMoney::autoCalc); s.setValue(MyMoneyMoney::autoCalc); s.setMemo(i18n("Interest")); s.setAction(MyMoneySplit::ActionInterest); t.addSplit(s); } // additional fee splits QList additionalSplits; m_loanPaymentPage->additionalFeesSplits(additionalSplits); QList::const_iterator it; MyMoneyMoney factor(moneyBorrowed() ? 1 : -1, 1); for (it = additionalSplits.constBegin(); it != additionalSplits.constEnd(); ++it) { s = (*it); s.clearId(); s.setShares(s.shares() * factor); s.setValue(s.value() * factor); t.addSplit(s); } // setup the next due date t.setPostDate(m_loanSchedulePage->firstPaymentDueDate()); m_schedule.setTransaction(t); } } return m_schedule; } MyMoneyMoney Wizard::openingBalance() const { // equity accounts don't have an opening balance if (m_accountTypePage->accountType() == Account::Equity) return MyMoneyMoney(); if (m_accountTypePage->accountType() == Account::Loan) { if (m_generalLoanInfoPage->recordAllPayments()) return MyMoneyMoney(); if (moneyBorrowed()) return -(m_generalLoanInfoPage->m_openingBalance->value()); return m_generalLoanInfoPage->m_openingBalance->value(); } return m_accountTypePage->m_openingBalance->value(); } MyMoneyPrice Wizard::conversionRate() const { if (MyMoneyFile::instance()->baseCurrency().id() == m_accountTypePage->m_currencyComboBox->security().id()) return MyMoneyPrice(MyMoneyFile::instance()->baseCurrency().id(), m_accountTypePage->m_currencyComboBox->security().id(), m_accountTypePage->m_openingDate->date(), MyMoneyMoney::ONE, i18n("User")); return MyMoneyPrice(MyMoneyFile::instance()->baseCurrency().id(), m_accountTypePage->m_currencyComboBox->security().id(), m_accountTypePage->m_openingDate->date(), m_accountTypePage->m_conversionRate->value(), i18n("User")); } bool Wizard::moneyBorrowed() const { return m_generalLoanInfoPage->m_loanDirection->currentIndex() == 0; } class InstitutionPage::Private { public: QList m_list; }; InstitutionPage::InstitutionPage(Wizard* wizard) : KInstitutionPageDecl(wizard), WizardPage(StepInstitution, this, wizard), d(new Private()) { connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotLoadWidgets())); connect(m_newInstitutionButton, SIGNAL(clicked()), this, SLOT(slotNewInstitution())); connect(m_institutionComboBox, SIGNAL(activated(int)), this, SLOT(slotSelectInstitution(int))); slotLoadWidgets(); m_institutionComboBox->setCurrentItem(0); slotSelectInstitution(0); } InstitutionPage::~InstitutionPage() { delete d; } void InstitutionPage::slotLoadWidgets() { m_institutionComboBox->clear(); d->m_list.clear(); MyMoneyFile::instance()->institutionList(d->m_list); qSort(d->m_list); QList::const_iterator it_l; m_institutionComboBox->addItem(QString()); for (it_l = d->m_list.constBegin(); it_l != d->m_list.constEnd(); ++it_l) { m_institutionComboBox->addItem((*it_l).name()); } } void InstitutionPage::slotNewInstitution() { MyMoneyInstitution institution; emit m_wizard->createInstitution(institution); if (!institution.id().isEmpty()) { QList::const_iterator it_l; int i = 0; for (it_l = d->m_list.constBegin(); it_l != d->m_list.constEnd(); ++it_l) { if ((*it_l).id() == institution.id()) { // select the item and remember that the very first one is the empty item m_institutionComboBox->setCurrentIndex(i + 1); slotSelectInstitution(i + 1); m_accountNumber->setFocus(); break; } ++i; } } } void InstitutionPage::slotSelectInstitution(const int index) { m_accountNumber->setEnabled(index != 0); m_iban->setEnabled(index != 0); } void InstitutionPage::selectExistingInstitution(const QString& id) { for (int i = 0; i < d->m_list.length(); ++i) { if (d->m_list[i].id() == id) { m_institutionComboBox->setCurrentIndex(i + 1); slotSelectInstitution(i + 1); break; } } } const MyMoneyInstitution& InstitutionPage::institution() const { static MyMoneyInstitution emptyInstitution; if (m_institutionComboBox->currentIndex() == 0) return emptyInstitution; return d->m_list[m_institutionComboBox->currentIndex()-1]; } KMyMoneyWizardPage* InstitutionPage::nextPage() const { return m_wizard->m_accountTypePage; } AccountTypePage::AccountTypePage(Wizard* wizard) : KAccountTypePageDecl(wizard), WizardPage(StepAccount, this, wizard), m_showPriceWarning(true) { m_typeSelection->insertItem(i18n("Checking"), (int)Account::Checkings); m_typeSelection->insertItem(i18n("Savings"), (int)Account::Savings); m_typeSelection->insertItem(i18n("Credit Card"), (int)Account::CreditCard); m_typeSelection->insertItem(i18n("Cash"), (int)Account::Cash); m_typeSelection->insertItem(i18n("Loan"), (int)Account::Loan); m_typeSelection->insertItem(i18n("Investment"), (int)Account::Investment); m_typeSelection->insertItem(i18n("Asset"), (int)Account::Asset); m_typeSelection->insertItem(i18n("Liability"), (int)Account::Liability); if (KMyMoneyGlobalSettings::expertMode()) { m_typeSelection->insertItem(i18n("Equity"), (int)Account::Equity); } m_typeSelection->setCurrentItem((int)Account::Checkings); m_currencyComboBox->setSecurity(MyMoneyFile::instance()->baseCurrency()); m_mandatoryGroup->add(m_accountName); m_mandatoryGroup->add(m_conversionRate->lineedit()); m_conversionRate->setValue(MyMoneyMoney::ONE); slotUpdateCurrency(); connect(m_typeSelection, SIGNAL(itemSelected(int)), this, SLOT(slotUpdateType(int))); connect(m_currencyComboBox, SIGNAL(activated(int)), this, SLOT(slotUpdateCurrency())); connect(m_conversionRate, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateConversionRate(QString))); connect(m_conversionRate, SIGNAL(valueChanged(QString)), this, SLOT(slotPriceWarning())); connect(m_onlineQuote, SIGNAL(clicked()), this, SLOT(slotGetOnlineQuote())); } void AccountTypePage::slotUpdateType(int i) { hideShowPages(static_cast(i)); m_openingBalance->setDisabled(static_cast(i) == Account::Equity); } void AccountTypePage::hideShowPages(Account accountType) const { bool hideSchedulePage = (accountType != Account::CreditCard) && (accountType != Account::Loan); bool hideLoanPage = (accountType != Account::Loan); m_wizard->setStepHidden(StepDetails, hideLoanPage); m_wizard->setStepHidden(StepPayments, hideLoanPage); m_wizard->setStepHidden(StepFees, hideLoanPage); m_wizard->setStepHidden(StepSchedule, hideSchedulePage); m_wizard->setStepHidden(StepPayout, (accountType != Account::Loan)); m_wizard->setStepHidden(StepBroker, accountType != Account::Investment); m_wizard->setStepHidden(StepParentAccount, accountType == Account::Loan); // Force an update of the steps in case the list has changed m_wizard->reselectStep(); } KMyMoneyWizardPage* AccountTypePage::nextPage() const { if (accountType() == Account::Loan) return m_wizard->m_generalLoanInfoPage; if (accountType() == Account::CreditCard) return m_wizard->m_schedulePage; if (accountType() == Account::Investment) return m_wizard->m_brokeragepage; return m_wizard->m_hierarchyPage; } void AccountTypePage::slotUpdateCurrency() { MyMoneyAccount acc; acc.setAccountType(accountType()); m_openingBalance->setPrecision(MyMoneyMoney::denomToPrec(acc.fraction(currency()))); bool show = m_currencyComboBox->security().id() != MyMoneyFile::instance()->baseCurrency().id(); m_conversionLabel->setVisible(show); m_conversionRate->setVisible(show); m_conversionExample->setVisible(show); m_onlineQuote->setVisible(show); m_conversionRate->setEnabled(show); // make sure to include/exclude in mandatoryGroup m_conversionRate->setPrecision(m_currencyComboBox->security().pricePrecision()); m_mandatoryGroup->changed(); slotUpdateConversionRate(m_conversionRate->lineedit()->text()); } void AccountTypePage::slotGetOnlineQuote() { QString id = MyMoneyFile::instance()->baseCurrency().id() + ' ' + m_currencyComboBox->security().id(); QPointer dlg = new KEquityPriceUpdateDlg(this, id); if (dlg->exec() == QDialog::Accepted) { const MyMoneyPrice &price = dlg->price(id); if (price.isValid()) { m_conversionRate->setValue(price.rate(m_currencyComboBox->security().id())); if (price.date() != m_openingDate->date()) { priceWarning(true); } } } delete dlg; } void AccountTypePage::slotPriceWarning() { priceWarning(false); } void AccountTypePage::priceWarning(bool always) { if (m_showPriceWarning || always) { KMessageBox::information(this, i18n("Please make sure to enter the correct conversion for the selected opening date. If you requested an online quote it might be provided for a different date."), i18n("Check date")); } m_showPriceWarning = false; } void AccountTypePage::slotUpdateConversionRate(const QString& txt) { m_conversionExample->setText(i18n("1 %1 equals %2", MyMoneyFile::instance()->baseCurrency().tradingSymbol(), MyMoneyMoney(txt).formatMoney(m_currencyComboBox->security().tradingSymbol(), m_currencyComboBox->security().pricePrecision()))); } bool AccountTypePage::isComplete() const { // check that the conversion rate is positive if enabled bool rc = !m_conversionRate->isVisible() || (!m_conversionRate->value().isZero() && !m_conversionRate->value().isNegative()); if (!rc) { m_wizard->m_nextButton->setToolTip(i18n("Conversion rate is not positive")); } else { rc = KMyMoneyWizardPage::isComplete(); if (!rc) { m_wizard->m_nextButton->setToolTip(i18n("No account name supplied")); } } hideShowPages(accountType()); return rc; } Account AccountTypePage::accountType() const { return static_cast(m_typeSelection->currentItem()); } const MyMoneySecurity& AccountTypePage::currency() const { return m_currencyComboBox->security(); } void AccountTypePage::setAccount(const MyMoneyAccount& acc) { if (acc.accountType() != Account::Unknown) { if (acc.accountType() == Account::AssetLoan) { m_typeSelection->setCurrentItem((int)Account::Loan); } else { m_typeSelection->setCurrentItem((int)acc.accountType()); } } m_openingDate->setDate(acc.openingDate()); m_accountName->setText(acc.name()); } const MyMoneyAccount& AccountTypePage::parentAccount() { switch (accountType()) { case Account::CreditCard: case Account::Liability: case Account::Loan: // Can be either but we return liability here return MyMoneyFile::instance()->liability(); break; case Account::Equity: return MyMoneyFile::instance()->equity(); default: break; } return MyMoneyFile::instance()->asset(); } bool AccountTypePage::allowsParentAccount() const { return accountType() != Account::Loan; } BrokeragePage::BrokeragePage(Wizard* wizard) : KBrokeragePageDecl(wizard), WizardPage(StepBroker, this, wizard) { connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotLoadWidgets())); } void BrokeragePage::slotLoadWidgets() { m_brokerageCurrency->update(QString("x")); } void BrokeragePage::enterPage() { // assign the currency of the investment account to the // brokerage account if nothing else has ever been selected if (m_brokerageCurrency->security().id().isEmpty()) { m_brokerageCurrency->setSecurity(m_wizard->m_accountTypePage->m_currencyComboBox->security()); } // check if the institution relevant fields should be enabled or not bool enabled = m_wizard->m_institutionPage->m_accountNumber->isEnabled(); m_accountNumberLabel->setEnabled(enabled); m_accountNumber->setEnabled(enabled); m_ibanLabel->setEnabled(enabled); m_iban->setEnabled(enabled); } KMyMoneyWizardPage* BrokeragePage::nextPage() const { return m_wizard->m_hierarchyPage; } CreditCardSchedulePage::CreditCardSchedulePage(Wizard* wizard) : KSchedulePageDecl(wizard), WizardPage(StepSchedule, this, wizard) { m_mandatoryGroup->add(m_name); m_mandatoryGroup->add(m_payee); m_mandatoryGroup->add(m_amount->lineedit()); m_mandatoryGroup->add(m_paymentAccount); connect(m_paymentAccount, SIGNAL(itemSelected(QString)), object(), SIGNAL(completeStateChanged())); connect(m_payee, SIGNAL(itemSelected(QString)), object(), SIGNAL(completeStateChanged())); connect(m_date, SIGNAL(dateChanged(QDate)), object(), SIGNAL(completeStateChanged())); connect(m_payee, SIGNAL(createItem(QString,QString&)), wizard, SIGNAL(createPayee(QString,QString&))); m_reminderCheckBox->setChecked(true); connect(m_reminderCheckBox, SIGNAL(toggled(bool)), object(), SIGNAL(completeStateChanged())); connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotLoadWidgets())); m_method->insertItem(i18n("Write check"), (int)Schedule::PaymentType::WriteChecque); #if 0 m_method->insertItem(i18n("Direct debit"), Schedule::PaymentType::DirectDebit); m_method->insertItem(i18n("Bank transfer"), Schedule::PaymentType::BankTransfer); #endif m_method->insertItem(i18n("Standing order"), (int)Schedule::PaymentType::StandingOrder); m_method->insertItem(i18n("Manual deposit"), (int)Schedule::PaymentType::ManualDeposit); m_method->insertItem(i18n("Direct deposit"), (int)Schedule::PaymentType::DirectDeposit); m_method->insertItem(i18nc("Other payment method", "Other"), (int)Schedule::PaymentType::Other); m_method->setCurrentItem((int)Schedule::PaymentType::DirectDebit); slotLoadWidgets(); } void CreditCardSchedulePage::enterPage() { if (m_name->text().isEmpty()) m_name->setText(i18n("Credit Card %1 monthly payment", m_wizard->m_accountTypePage->m_accountName->text())); } bool CreditCardSchedulePage::isComplete() const { bool rc = true; QString msg = i18n("Finish entry and create account"); if (m_reminderCheckBox->isChecked()) { msg = i18n("Finish entry and create account and schedule"); if (m_date->date() < m_wizard->m_accountTypePage->m_openingDate->date()) { rc = false; msg = i18n("Next due date is prior to opening date"); } if (m_paymentAccount->selectedItem().isEmpty()) { rc = false; msg = i18n("No account selected"); } if (m_amount->lineedit()->text().isEmpty()) { rc = false; msg = i18n("No amount for payment selected"); } if (m_payee->selectedItem().isEmpty()) { rc = false; msg = i18n("No payee for payment selected"); } if (m_name->text().isEmpty()) { rc = false; msg = i18n("No name assigned for schedule"); } } m_wizard->m_finishButton->setToolTip(msg); return rc; } void CreditCardSchedulePage::slotLoadWidgets() { AccountSet set; set.addAccountGroup(Account::Asset); set.load(m_paymentAccount->selector()); m_payee->loadPayees(MyMoneyFile::instance()->payeeList()); } KMyMoneyWizardPage* CreditCardSchedulePage::nextPage() const { return m_wizard->m_hierarchyPage; } GeneralLoanInfoPage::GeneralLoanInfoPage(Wizard* wizard) : KGeneralLoanInfoPageDecl(wizard), WizardPage(StepDetails, this, wizard), m_firstTime(true) { m_mandatoryGroup->add(m_payee); // remove the unsupported payment and compounding frequencies and setup default m_paymentFrequency->removeItem((int)Schedule::Occurrence::Once); m_paymentFrequency->removeItem((int)Schedule::Occurrence::EveryOtherYear); m_paymentFrequency->setCurrentItem((int)Schedule::Occurrence::Monthly); m_compoundFrequency->removeItem((int)Schedule::Occurrence::Once); m_compoundFrequency->removeItem((int)Schedule::Occurrence::EveryOtherYear); m_compoundFrequency->setCurrentItem((int)Schedule::Occurrence::Monthly); slotLoadWidgets(); connect(m_payee, SIGNAL(createItem(QString,QString&)), wizard, SIGNAL(createPayee(QString,QString&))); connect(m_anyPayments, SIGNAL(activated(int)), object(), SIGNAL(completeStateChanged())); connect(m_recordings, SIGNAL(activated(int)), object(), SIGNAL(completeStateChanged())); connect(m_interestType, SIGNAL(activated(int)), object(), SIGNAL(completeStateChanged())); connect(m_interestChangeDateEdit, SIGNAL(dateChanged(QDate)), object(), SIGNAL(completeStateChanged())); connect(m_openingBalance, SIGNAL(textChanged(QString)), object(), SIGNAL(completeStateChanged())); connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotLoadWidgets())); } KMyMoneyWizardPage* GeneralLoanInfoPage::nextPage() const { return m_wizard->m_loanDetailsPage; } bool GeneralLoanInfoPage::recordAllPayments() const { bool rc = true; // all payments if (m_recordings->isEnabled()) { if (m_recordings->currentIndex() != 0) rc = false; } return rc; } void GeneralLoanInfoPage::enterPage() { if (m_firstTime) { // setup default dates to last of this month and one year on top QDate firstDay(QDate::currentDate().year(), QDate::currentDate().month(), 1); m_firstPaymentDate->setDate(firstDay.addMonths(1).addDays(-1)); m_interestChangeDateEdit->setDate(m_firstPaymentDate->date().addYears(1));; m_firstTime = false; } } bool GeneralLoanInfoPage::isComplete() const { m_wizard->setStepHidden(StepPayout, !m_wizard->openingBalance().isZero()); bool rc = KMyMoneyWizardPage::isComplete(); if (!rc) { m_wizard->m_nextButton->setToolTip(i18n("No payee supplied")); } // fixup availability of items on this page m_recordings->setDisabled(m_anyPayments->currentIndex() == 0); m_interestFrequencyAmountEdit->setDisabled(m_interestType->currentIndex() == 0); m_interestFrequencyUnitEdit->setDisabled(m_interestType->currentIndex() == 0); m_interestChangeDateEdit->setDisabled(m_interestType->currentIndex() == 0); m_openingBalance->setDisabled(recordAllPayments()); if (m_openingBalance->isEnabled() && m_openingBalance->lineedit()->text().length() == 0) { rc = false; m_wizard->m_nextButton->setToolTip(i18n("No opening balance supplied")); } if (rc && (m_interestType->currentIndex() != 0) && (m_interestChangeDateEdit->date() <= m_firstPaymentDate->date())) { rc = false; m_wizard->m_nextButton->setToolTip(i18n("An interest change can only happen after the first payment")); } return rc; } const MyMoneyAccount& GeneralLoanInfoPage::parentAccount() { return (m_loanDirection->currentIndex() == 0) ? MyMoneyFile::instance()->liability() : MyMoneyFile::instance()->asset(); } void GeneralLoanInfoPage::slotLoadWidgets() { m_payee->loadPayees(MyMoneyFile::instance()->payeeList()); } LoanDetailsPage::LoanDetailsPage(Wizard* wizard) : KLoanDetailsPageDecl(wizard), WizardPage(StepPayments, this, wizard), m_needCalculate(true) { // force the balloon payment to zero (default) m_balloonAmount->setValue(MyMoneyMoney()); // allow any precision for the interest rate m_interestRate->setPrecision(-1); connect(m_paymentDue, SIGNAL(activated(int)), this, SLOT(slotValuesChanged())); connect(m_termAmount, SIGNAL(valueChanged(int)), this, SLOT(slotValuesChanged())); connect(m_termUnit, SIGNAL(highlighted(int)), this, SLOT(slotValuesChanged())); connect(m_loanAmount, SIGNAL(textChanged(QString)), this, SLOT(slotValuesChanged())); connect(m_interestRate, SIGNAL(textChanged(QString)), this, SLOT(slotValuesChanged())); connect(m_paymentAmount, SIGNAL(textChanged(QString)), this, SLOT(slotValuesChanged())); connect(m_balloonAmount, SIGNAL(textChanged(QString)), this, SLOT(slotValuesChanged())); connect(m_calculateButton, SIGNAL(clicked()), this, SLOT(slotCalculate())); } void LoanDetailsPage::enterPage() { // we need to remove a bunch of entries of the payment frequencies m_termUnit->clear(); m_mandatoryGroup->clear(); if (!m_wizard->openingBalance().isZero()) { m_mandatoryGroup->add(m_loanAmount->lineedit()); if (m_loanAmount->lineedit()->text().length() == 0) { m_loanAmount->setValue(m_wizard->openingBalance().abs()); } } switch (m_wizard->m_generalLoanInfoPage->m_paymentFrequency->currentItem()) { default: m_termUnit->insertItem(i18n("Payments"), (int)Schedule::Occurrence::Once); m_termUnit->setCurrentItem((int)Schedule::Occurrence::Once); break; case Schedule::Occurrence::Monthly: m_termUnit->insertItem(i18n("Months"), (int)Schedule::Occurrence::Monthly); m_termUnit->insertItem(i18n("Years"), (int)Schedule::Occurrence::Yearly); m_termUnit->setCurrentItem((int)Schedule::Occurrence::Monthly); break; case Schedule::Occurrence::Yearly: m_termUnit->insertItem(i18n("Years"), (int)Schedule::Occurrence::Yearly); m_termUnit->setCurrentItem((int)Schedule::Occurrence::Yearly); break; } } void LoanDetailsPage::slotValuesChanged() { m_needCalculate = true; m_wizard->completeStateChanged(); } void LoanDetailsPage::slotCalculate() { MyMoneyFinancialCalculator calc; double val; int PF, CF; QString result; bool moneyBorrowed = m_wizard->moneyBorrowed(); bool moneyLend = !moneyBorrowed; // FIXME: for now, we only support interest calculation at the end of the period calc.setBep(); // FIXME: for now, we only support periodic compounding calc.setDisc(); PF = m_wizard->m_generalLoanInfoPage->m_paymentFrequency->eventsPerYear(); CF = m_wizard->m_generalLoanInfoPage->m_compoundFrequency->eventsPerYear(); if (PF == 0 || CF == 0) return; calc.setPF(PF); calc.setCF(CF); if (!m_loanAmount->lineedit()->text().isEmpty()) { val = m_loanAmount->value().abs().toDouble(); if (moneyBorrowed) val = -val; calc.setPv(val); } if (!m_interestRate->lineedit()->text().isEmpty()) { val = m_interestRate->value().abs().toDouble(); calc.setIr(val); } if (!m_paymentAmount->lineedit()->text().isEmpty()) { val = m_paymentAmount->value().abs().toDouble(); if (moneyLend) val = -val; calc.setPmt(val); } if (!m_balloonAmount->lineedit()->text().isEmpty()) { val = m_balloonAmount->value().abs().toDouble(); if (moneyLend) val = -val; calc.setFv(val); } if (m_termAmount->value() != 0) { calc.setNpp(term()); } // setup of parameters is done, now do the calculation try { if (m_loanAmount->lineedit()->text().isEmpty()) { // calculate the amount of the loan out of the other information val = calc.presentValue(); m_loanAmount->loadText(MyMoneyMoney(static_cast(val)).abs().formatMoney(QString(), m_wizard->precision())); result = i18n("KMyMoney has calculated the amount of the loan as %1.", m_loanAmount->lineedit()->text()); } else if (m_interestRate->lineedit()->text().isEmpty()) { // calculate the interest rate out of the other information val = calc.interestRate(); m_interestRate->loadText(MyMoneyMoney(static_cast(val)).abs().formatMoney(QString(), 3)); result = i18n("KMyMoney has calculated the interest rate to %1%.", m_interestRate->lineedit()->text()); } else if (m_paymentAmount->lineedit()->text().isEmpty()) { // calculate the periodical amount of the payment out of the other information val = calc.payment(); m_paymentAmount->setValue(MyMoneyMoney(static_cast(val)).abs()); // reset payment as it might have changed due to rounding val = m_paymentAmount->value().abs().toDouble(); if (moneyLend) val = -val; calc.setPmt(val); result = i18n("KMyMoney has calculated a periodic payment of %1 to cover principal and interest.", m_paymentAmount->lineedit()->text()); val = calc.futureValue(); if ((moneyBorrowed && val < 0 && qAbs(val) >= qAbs(calc.payment())) || (moneyLend && val > 0 && qAbs(val) >= qAbs(calc.payment()))) { calc.setNpp(calc.npp() - 1); // updateTermWidgets(calc.npp()); val = calc.futureValue(); MyMoneyMoney refVal(static_cast(val)); m_balloonAmount->loadText(refVal.abs().formatMoney(QString(), m_wizard->precision())); result += QString(" "); result += i18n("The number of payments has been decremented and the balloon payment has been modified to %1.", m_balloonAmount->lineedit()->text()); } else if ((moneyBorrowed && val < 0 && qAbs(val) < qAbs(calc.payment())) || (moneyLend && val > 0 && qAbs(val) < qAbs(calc.payment()))) { m_balloonAmount->loadText(MyMoneyMoney().formatMoney(QString(), m_wizard->precision())); } else { MyMoneyMoney refVal(static_cast(val)); m_balloonAmount->loadText(refVal.abs().formatMoney(QString(), m_wizard->precision())); result += i18n("The balloon payment has been modified to %1.", m_balloonAmount->lineedit()->text()); } } else if (m_termAmount->value() == 0) { // calculate the number of payments out of the other information val = calc.numPayments(); if (val == 0) throw MYMONEYEXCEPTION("incorrect fincancial calculation"); // if the number of payments has a fractional part, then we // round it to the smallest integer and calculate the balloon payment result = i18n("KMyMoney has calculated the term of your loan as %1. ", updateTermWidgets(qFloor(val))); if (val != qFloor(val)) { calc.setNpp(qFloor(val)); val = calc.futureValue(); MyMoneyMoney refVal(static_cast(val)); m_balloonAmount->loadText(refVal.abs().formatMoney(QString(), m_wizard->precision())); result += i18n("The balloon payment has been modified to %1.", m_balloonAmount->lineedit()->text()); } } else { // calculate the future value of the loan out of the other information val = calc.futureValue(); // we differentiate between the following cases: // a) the future value is greater than a payment // b) the future value is less than a payment or the loan is overpaid // c) all other cases // // a) means, we have paid more than we owed. This can't be // b) means, we paid more than we owed but the last payment is // less in value than regular payments. That means, that the // future value is to be treated as (fully payed back) // c) the loan is not payed back yet if ((moneyBorrowed && val < 0 && qAbs(val) > qAbs(calc.payment())) || (moneyLend && val > 0 && qAbs(val) > qAbs(calc.payment()))) { // case a) qDebug("Future Value is %f", val); throw MYMONEYEXCEPTION("incorrect fincancial calculation"); } else if ((moneyBorrowed && val < 0 && qAbs(val) <= qAbs(calc.payment())) || (moneyLend && val > 0 && qAbs(val) <= qAbs(calc.payment()))) { // case b) val = 0; } MyMoneyMoney refVal(static_cast(val)); result = i18n("KMyMoney has calculated a balloon payment of %1 for this loan.", refVal.abs().formatMoney(QString(), m_wizard->precision())); if (!m_balloonAmount->lineedit()->text().isEmpty()) { if ((m_balloonAmount->value().abs() - refVal.abs()).abs().toDouble() > 1) { throw MYMONEYEXCEPTION("incorrect fincancial calculation"); } result = i18n("KMyMoney has successfully verified your loan information."); } m_balloonAmount->loadText(refVal.abs().formatMoney(QString(), m_wizard->precision())); } } catch (const MyMoneyException &) { KMessageBox::error(0, i18n("You have entered mis-matching information. Please modify " "your figures or leave one value empty " "to let KMyMoney calculate it for you"), i18n("Calculation error")); return; } result += i18n("\n\nAccept this or modify the loan information and recalculate."); KMessageBox::information(0, result, i18n("Calculation successful")); m_needCalculate = false; // now update change m_wizard->completeStateChanged(); } int LoanDetailsPage::term() const { int factor = 0; if (m_termAmount->value() != 0) { factor = 1; switch (m_termUnit->currentItem()) { case Schedule::Occurrence::Yearly: // years factor = 12; // intentional fall through case Schedule::Occurrence::Monthly: // months factor *= 30; factor *= m_termAmount->value(); // factor now is the duration in days. we divide this by the // payment frequency and get the number of payments factor /= m_wizard->m_generalLoanInfoPage->m_paymentFrequency->daysBetweenEvents(); break; default: qDebug("Unknown term unit %d in LoanDetailsPage::term(). Using payments.", (int)m_termUnit->currentItem()); // intentional fall through case Schedule::Occurrence::Once: // payments factor = m_termAmount->value(); break; } } return factor; } QString LoanDetailsPage::updateTermWidgets(const double val) { long vl = qFloor(val); QString valString; Schedule::Occurrence unit = m_termUnit->currentItem(); if ((unit == Schedule::Occurrence::Monthly) && ((vl % 12) == 0)) { vl /= 12; unit = Schedule::Occurrence::Yearly; } switch (unit) { case Schedule::Occurrence::Monthly: valString = i18np("one month", "%1 months", vl); m_termUnit->setCurrentItem((int)Schedule::Occurrence::Monthly); break; case Schedule::Occurrence::Yearly: valString = i18np("one year", "%1 years", vl); m_termUnit->setCurrentItem((int)Schedule::Occurrence::Yearly); break; default: valString = i18np("one payment", "%1 payments", vl); m_termUnit->setCurrentItem((int)Schedule::Occurrence::Once); break; } m_termAmount->setValue(vl); return valString; } bool LoanDetailsPage::isComplete() const { // bool rc = KMyMoneyWizardPage::isComplete(); int fieldCnt = 0; if (m_loanAmount->lineedit()->text().length() > 0) { fieldCnt++; } if (m_interestRate->lineedit()->text().length() > 0) { fieldCnt++; } if (m_termAmount->value() != 0) { fieldCnt++; } if (m_paymentAmount->lineedit()->text().length() > 0) { fieldCnt++; } if (m_balloonAmount->lineedit()->text().length() > 0) { fieldCnt++; } m_calculateButton->setEnabled(fieldCnt == 4 || (fieldCnt == 5 && m_needCalculate)); m_calculateButton->setAutoDefault(false); m_calculateButton->setDefault(false); if (m_needCalculate && fieldCnt == 4) { m_wizard->m_nextButton->setToolTip(i18n("Press Calculate to verify the values")); m_calculateButton->setAutoDefault(true); m_calculateButton->setDefault(true); } else if (fieldCnt != 5) { m_wizard->m_nextButton->setToolTip(i18n("Not all details supplied")); m_calculateButton->setAutoDefault(true); m_calculateButton->setDefault(true); } m_wizard->m_nextButton->setAutoDefault(!m_calculateButton->autoDefault()); m_wizard->m_nextButton->setDefault(!m_calculateButton->autoDefault()); return (fieldCnt == 5) && !m_needCalculate; } KMyMoneyWizardPage* LoanDetailsPage::nextPage() const { return m_wizard->m_loanPaymentPage; } class LoanPaymentPage::Private { public: MyMoneyAccount phonyAccount; MyMoneySplit phonySplit; MyMoneyTransaction additionalFeesTransaction; MyMoneyMoney additionalFees; }; LoanPaymentPage::LoanPaymentPage(Wizard* wizard) : KLoanPaymentPageDecl(wizard), WizardPage(StepFees, this, wizard), d(new Private) { d->phonyAccount = MyMoneyAccount(QLatin1String("Phony-ID"), MyMoneyAccount()); d->phonySplit.setAccountId(d->phonyAccount.id()); d->phonySplit.setValue(MyMoneyMoney()); d->phonySplit.setShares(MyMoneyMoney()); d->additionalFeesTransaction.addSplit(d->phonySplit); connect(m_additionalFeesButton, SIGNAL(clicked()), this, SLOT(slotAdditionalFees())); } LoanPaymentPage::~LoanPaymentPage() { delete d; } MyMoneyMoney LoanPaymentPage::basePayment() const { return m_wizard->m_loanDetailsPage->m_paymentAmount->value(); } MyMoneyMoney LoanPaymentPage::additionalFees() const { return d->additionalFees; } void LoanPaymentPage::additionalFeesSplits(QList& list) { list.clear(); QList::ConstIterator it; for (it = d->additionalFeesTransaction.splits().constBegin(); it != d->additionalFeesTransaction.splits().constEnd(); ++it) { if ((*it).accountId() != d->phonyAccount.id()) { list << (*it); } } } void LoanPaymentPage::updateAmounts() { m_additionalFees->setText(d->additionalFees.formatMoney(m_wizard->currency().tradingSymbol(), m_wizard->precision())); m_totalPayment->setText((basePayment() + d->additionalFees).formatMoney(m_wizard->currency().tradingSymbol(), m_wizard->precision())); } void LoanPaymentPage::enterPage() { const MyMoneySecurity& currency = m_wizard->currency(); m_basePayment->setText(basePayment().formatMoney(currency.tradingSymbol(), m_wizard->precision())); d->phonyAccount.setCurrencyId(currency.id()); d->additionalFeesTransaction.setCommodity(currency.id()); updateAmounts(); } void LoanPaymentPage::slotAdditionalFees() { QMap priceInfo; QPointer dlg = new KSplitTransactionDlg(d->additionalFeesTransaction, d->phonySplit, d->phonyAccount, false, !m_wizard->moneyBorrowed(), MyMoneyMoney(), priceInfo); // connect(dlg, SIGNAL(newCategory(MyMoneyAccount&)), this, SIGNAL(newCategory(MyMoneyAccount&))); if (dlg->exec() == QDialog::Accepted) { d->additionalFeesTransaction = dlg->transaction(); // sum up the additional fees QList::ConstIterator it; d->additionalFees = MyMoneyMoney(); for (it = d->additionalFeesTransaction.splits().constBegin(); it != d->additionalFeesTransaction.splits().constEnd(); ++it) { if ((*it).accountId() != d->phonyAccount.id()) { d->additionalFees += (*it).shares(); } } updateAmounts(); } delete dlg; } KMyMoneyWizardPage* LoanPaymentPage::nextPage() const { return m_wizard->m_loanSchedulePage; } LoanSchedulePage::LoanSchedulePage(Wizard* wizard) : KLoanSchedulePageDecl(wizard), WizardPage(StepSchedule, this, wizard) { m_mandatoryGroup->add(m_interestCategory->lineEdit()); m_mandatoryGroup->add(m_paymentAccount->lineEdit()); connect(m_interestCategory, SIGNAL(createItem(QString,QString&)), this, SLOT(slotCreateCategory(QString,QString&))); connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotLoadWidgets())); } void LoanSchedulePage::slotCreateCategory(const QString& name, QString& id) { MyMoneyAccount acc, parent; acc.setName(name); if (m_wizard->moneyBorrowed()) parent = MyMoneyFile::instance()->expense(); else parent = MyMoneyFile::instance()->income(); emit m_wizard->createCategory(acc, parent); // return id id = acc.id(); } QDate LoanSchedulePage::firstPaymentDueDate() const { if (m_firstPaymentDueDate->isEnabled()) return m_firstPaymentDueDate->date(); return m_wizard->m_generalLoanInfoPage->m_firstPaymentDate->date(); } void LoanSchedulePage::enterPage() { m_interestCategory->setFocus(); m_firstPaymentDueDate->setDisabled(m_wizard->m_generalLoanInfoPage->recordAllPayments()); slotLoadWidgets(); } void LoanSchedulePage::slotLoadWidgets() { AccountSet set; if (m_wizard->moneyBorrowed()) set.addAccountGroup(Account::Expense); else set.addAccountGroup(Account::Income); set.load(m_interestCategory->selector()); set.clear(); set.addAccountGroup(Account::Asset); set.load(m_paymentAccount->selector()); } KMyMoneyWizardPage* LoanSchedulePage::nextPage() const { // if the balance widget of the general loan info page is enabled and // the value is not zero, then the payout already happened and we don't // aks for it. if (m_wizard->openingBalance().isZero()) return m_wizard->m_loanPayoutPage; return m_wizard->m_accountSummaryPage; } LoanPayoutPage::LoanPayoutPage(Wizard* wizard) : KLoanPayoutPageDecl(wizard), WizardPage(StepPayout, this, wizard) { m_mandatoryGroup->add(m_assetAccount->lineEdit()); m_mandatoryGroup->add(m_loanAccount->lineEdit()); KGuiItem createAssetButtenItem(i18n("&Create..."), QIcon::fromTheme(g_Icons[Icon::DocumentNew]), i18n("Create a new asset account"), i18n("If the asset account does not yet exist, press this button to create it.")); KGuiItem::assign(m_createAssetButton, createAssetButtenItem); m_createAssetButton->setToolTip(createAssetButtenItem.toolTip()); m_createAssetButton->setWhatsThis(createAssetButtenItem.whatsThis()); connect(m_createAssetButton, SIGNAL(clicked()), this, SLOT(slotCreateAssetAccount())); connect(m_noPayoutTransaction, SIGNAL(toggled(bool)), this, SLOT(slotButtonsToggled())); connect(m_refinanceLoan, SIGNAL(toggled(bool)), this, SLOT(slotButtonsToggled())); connect(MyMoneyFile::instance(), SIGNAL(dataChanged()), this, SLOT(slotLoadWidgets())); slotLoadWidgets(); } void LoanPayoutPage::slotButtonsToggled() { // we don't go directly, as the order of the emission of signals to slots is // not defined. Using a single shot timer postpones the call of m_mandatoryGroup::changed() // until the next round of the main loop so we can be sure to see all relevant changes // that happened in the meantime (eg. widgets are enabled and disabled) QTimer::singleShot(0, m_mandatoryGroup, SLOT(changed())); } void LoanPayoutPage::slotCreateAssetAccount() { MyMoneyAccount acc; acc.setAccountType(Account::Asset); acc.setOpeningDate(m_wizard->m_accountTypePage->m_openingDate->date()); emit m_wizard->createAccount(acc); if (!acc.id().isEmpty()) { m_assetAccount->setSelectedItem(acc.id()); } } void LoanPayoutPage::slotLoadWidgets() { AccountSet set; set.addAccountGroup(Account::Asset); set.load(m_assetAccount->selector()); set.clear(); set.addAccountType(Account::Loan); set.load(m_loanAccount->selector()); } void LoanPayoutPage::enterPage() { // only allow to create new asset accounts for liability loans m_createAssetButton->setEnabled(m_wizard->moneyBorrowed()); m_refinanceLoan->setEnabled(m_wizard->moneyBorrowed()); if (!m_wizard->moneyBorrowed()) { m_refinanceLoan->setChecked(false); } m_payoutDetailFrame->setDisabled(m_noPayoutTransaction->isChecked()); } KMyMoneyWizardPage* LoanPayoutPage::nextPage() const { return m_wizard->m_accountSummaryPage; } bool LoanPayoutPage::isComplete() const { return KMyMoneyWizardPage::isComplete() | m_noPayoutTransaction->isChecked(); } const QString& LoanPayoutPage::payoutAccountId() const { if (m_refinanceLoan->isChecked()) { return m_loanAccount->selectedItem(); } else { return m_assetAccount->selectedItem(); } } HierarchyFilterProxyModel::HierarchyFilterProxyModel(QObject *parent) : AccountsProxyModel(parent) { } /** * Filter the favorites accounts group. */ bool HierarchyFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { if (!source_parent.isValid()) { auto accCol = m_mdlColumns->indexOf(eAccountsModel::Column::Account); QVariant data = sourceModel()->index(source_row, accCol, source_parent).data((int)eAccountsModel::Role::ID); if (data.isValid() && data.toString() == AccountsModel::favoritesAccountId) return false; } return AccountsProxyModel::filterAcceptsRow(source_row, source_parent); } /** * Filter all but the first column. */ bool HierarchyFilterProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const { Q_UNUSED(source_parent) if (source_column == 0) return true; return false; } HierarchyPage::HierarchyPage(Wizard* wizard) : KHierarchyPageDecl(wizard), WizardPage(StepParentAccount, this, wizard), m_filterProxyModel(nullptr) { // the proxy filter model m_filterProxyModel = new HierarchyFilterProxyModel(this); m_filterProxyModel->setHideClosedAccounts(true); m_filterProxyModel->setHideEquityAccounts(!KMyMoneyGlobalSettings::expertMode()); m_filterProxyModel->addAccountGroup(QVector {Account::Asset, Account::Liability}); auto const model = Models::instance()->accountsModel(); m_filterProxyModel->setSourceModel(model); m_filterProxyModel->setSourceColumns(model->getColumns()); m_filterProxyModel->setDynamicSortFilter(true); m_parentAccounts->setModel(m_filterProxyModel); m_parentAccounts->sortByColumn((int)eAccountsModel::Column::Account, Qt::AscendingOrder); connect(m_parentAccounts->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(parentAccountChanged())); } void HierarchyPage::enterPage() { // Ensure that the list reflects the Account Type MyMoneyAccount topAccount = m_wizard->m_accountTypePage->parentAccount(); m_filterProxyModel->clear(); m_filterProxyModel->addAccountGroup(QVector {topAccount.accountGroup()}); m_parentAccounts->expandAll(); } KMyMoneyWizardPage* HierarchyPage::nextPage() const { return m_wizard->m_accountSummaryPage; } const MyMoneyAccount& HierarchyPage::parentAccount() { QVariant data = m_parentAccounts->model()->data(m_parentAccounts->currentIndex(), (int)eAccountsModel::Role::Account); if (data.isValid()) { m_parentAccount = data.value(); } else { m_parentAccount = MyMoneyAccount(); } return m_parentAccount; } bool HierarchyPage::isComplete() const { return m_parentAccounts->currentIndex().isValid(); } void HierarchyPage::parentAccountChanged() { completeStateChanged(); } AccountSummaryPage::AccountSummaryPage(Wizard* wizard) : KAccountSummaryPageDecl(wizard), WizardPage(StepFinish, this, wizard) { } void AccountSummaryPage::enterPage() { MyMoneyAccount acc = m_wizard->account(); MyMoneySecurity sec = m_wizard->currency(); acc.fraction(sec); // assign an id to the account inside the wizard which is required for a schedule // get the schedule and clear the id again in the wizards object. MyMoneyAccount tmp(QLatin1String("Phony-ID"), acc); m_wizard->setAccount(tmp); MyMoneySchedule sch = m_wizard->schedule(); m_wizard->setAccount(acc); m_dataList->clear(); // Account data m_dataList->setFontWeight(QFont::Bold); m_dataList->append(i18n("Account information")); m_dataList->setFontWeight(QFont::Normal); m_dataList->append(i18nc("Account name", "Name: %1", acc.name())); if (!acc.isLoan()) m_dataList->append(i18n("Subaccount of %1", m_wizard->parentAccount().name())); QString accTypeText; if (acc.accountType() == Account::AssetLoan) accTypeText = i18n("Loan"); else accTypeText = m_wizard->m_accountTypePage->m_typeSelection->currentText(); m_dataList->append(i18n("Type: %1", accTypeText)); m_dataList->append(i18n("Currency: %1", m_wizard->currency().name())); m_dataList->append(i18n("Opening date: %1", QLocale().toString(acc.openingDate()))); if (m_wizard->currency().id() != MyMoneyFile::instance()->baseCurrency().id()) { m_dataList->append(i18n("Conversion rate: %1", m_wizard->conversionRate().rate(QString()).formatMoney(QString(), m_wizard->currency().pricePrecision()))); } if (!acc.isLoan() || !m_wizard->openingBalance().isZero()) m_dataList->append(i18n("Opening balance: %1", MyMoneyUtils::formatMoney(m_wizard->openingBalance(), acc, sec))); if (!m_wizard->m_institutionPage->institution().id().isEmpty()) { m_dataList->append(i18n("Institution: %1", m_wizard->m_institutionPage->institution().name())); if (!acc.number().isEmpty()) { m_dataList->append(i18n("Number: %1", acc.number())); } if (!acc.value("iban").isEmpty()) { m_dataList->append(i18n("IBAN: %1", acc.value("iban"))); } } if (acc.accountType() == Account::Investment) { if (m_wizard->m_brokeragepage->m_createBrokerageButton->isChecked()) { m_dataList->setFontWeight(QFont::Bold); m_dataList->append(i18n("Brokerage Account")); m_dataList->setFontWeight(QFont::Normal); m_dataList->append(i18nc("Account name", "Name: %1 (Brokerage)", acc.name())); m_dataList->append(i18n("Currency: %1", m_wizard->m_brokeragepage->m_brokerageCurrency->security().name())); if (m_wizard->m_brokeragepage->m_accountNumber->isEnabled() && !m_wizard->m_brokeragepage->m_accountNumber->text().isEmpty()) m_dataList->append(i18n("Number: %1", m_wizard->m_brokeragepage->m_accountNumber->text())); if (m_wizard->m_brokeragepage->m_iban->isEnabled() && !m_wizard->m_brokeragepage->m_iban->text().isEmpty()) m_dataList->append(i18n("IBAN: %1", m_wizard->m_brokeragepage->m_iban->text())); } } // Loan if (acc.isLoan()) { m_dataList->setFontWeight(QFont::Bold); m_dataList->append(i18n("Loan information")); m_dataList->setFontWeight(QFont::Normal); if (m_wizard->moneyBorrowed()) { m_dataList->append(i18n("Amount borrowed: %1", m_wizard->m_loanDetailsPage->m_loanAmount->value().formatMoney(m_wizard->currency().tradingSymbol(), m_wizard->precision()))); } else { m_dataList->append(i18n("Amount lent: %1", m_wizard->m_loanDetailsPage->m_loanAmount->value().formatMoney(m_wizard->currency().tradingSymbol(), m_wizard->precision()))); } m_dataList->append(i18n("Interest rate: %1 %", m_wizard->m_loanDetailsPage->m_interestRate->value().formatMoney(QString(), -1))); m_dataList->append(i18n("Interest rate is %1", m_wizard->m_generalLoanInfoPage->m_interestType->currentText())); m_dataList->append(i18n("Principal and interest: %1", MyMoneyUtils::formatMoney(m_wizard->m_loanDetailsPage->m_paymentAmount->value(), acc, sec))); m_dataList->append(i18n("Additional Fees: %1", MyMoneyUtils::formatMoney(m_wizard->m_loanPaymentPage->additionalFees(), acc, sec))); m_dataList->append(i18n("Payment frequency: %1", m_wizard->m_generalLoanInfoPage->m_paymentFrequency->currentText())); m_dataList->append(i18n("Payment account: %1", m_wizard->m_loanSchedulePage->m_paymentAccount->currentText())); if (!m_wizard->m_loanPayoutPage->m_noPayoutTransaction->isChecked() && m_wizard->openingBalance().isZero()) { m_dataList->setFontWeight(QFont::Bold); m_dataList->append(i18n("Payout information")); m_dataList->setFontWeight(QFont::Normal); if (m_wizard->m_loanPayoutPage->m_refinanceLoan->isChecked()) { m_dataList->append(i18n("Refinance: %1", m_wizard->m_loanPayoutPage->m_loanAccount->currentText())); } else { if (m_wizard->moneyBorrowed()) m_dataList->append(i18n("Transfer amount to %1", m_wizard->m_loanPayoutPage->m_assetAccount->currentText())); else m_dataList->append(i18n("Transfer amount from %1", m_wizard->m_loanPayoutPage->m_assetAccount->currentText())); } m_dataList->append(i18n("Payment date: %1 ", QLocale().toString(m_wizard->m_loanPayoutPage->m_payoutDate->date()))); } } // Schedule if (!(sch == MyMoneySchedule())) { m_dataList->setFontWeight(QFont::Bold); m_dataList->append(i18n("Schedule information")); m_dataList->setFontWeight(QFont::Normal); m_dataList->append(i18nc("Schedule name", "Name: %1", sch.name())); if (acc.accountType() == Account::CreditCard) { MyMoneyAccount paymentAccount = MyMoneyFile::instance()->account(m_wizard->m_schedulePage->m_paymentAccount->selectedItem()); m_dataList->append(i18n("Occurrence: Monthly")); m_dataList->append(i18n("Paid from %1", paymentAccount.name())); m_dataList->append(i18n("Pay to %1", m_wizard->m_schedulePage->m_payee->currentText())); m_dataList->append(i18n("Amount: %1", MyMoneyUtils::formatMoney(m_wizard->m_schedulePage->m_amount->value(), acc, sec))); m_dataList->append(i18n("First payment due on %1", QLocale().toString(sch.nextDueDate()))); m_dataList->append(i18n("Payment method: %1", m_wizard->m_schedulePage->m_method->currentText())); } if (acc.isLoan()) { m_dataList->append(i18n("Occurrence: %1", m_wizard->m_generalLoanInfoPage->m_paymentFrequency->currentText())); m_dataList->append(i18n("Amount: %1", MyMoneyUtils::formatMoney(m_wizard->m_loanPaymentPage->basePayment() + m_wizard->m_loanPaymentPage->additionalFees(), acc, sec))); m_dataList->append(i18n("First payment due on %1", QLocale().toString(m_wizard->m_loanSchedulePage->firstPaymentDueDate()))); } } } } diff --git a/kmymoney/wizards/newaccountwizard/kschedulepagedecl.ui b/kmymoney/wizards/newaccountwizard/kschedulepagedecl.ui index 6acff6f68..3c34d4023 100644 --- a/kmymoney/wizards/newaccountwizard/kschedulepagedecl.ui +++ b/kmymoney/wizards/newaccountwizard/kschedulepagedecl.ui @@ -1,202 +1,202 @@ KSchedulePageDecl 0 0 544 391 For credit card accounts you can create a monthly schedule which will automatically remind you about the payment. Please fill in the details about this payment here. true Qt::StrongFocus Yes, remind me when the payment is due QFrame::NoFrame QFrame::Plain 0 0 5 0 0 180 0 Name of schedule false Payee false Estimated monthly payment false Qt::StrongFocus Due date of next payment false Qt::StrongFocus Payment should be made from account Qt::AlignTop false Payment method false 20 31 QSizePolicy::Expanding Qt::Vertical m_reminderCheckBox toggled(bool) m_paymentFrame setEnabled(bool) KMyMoneyGeneralCombo QWidget -
kmymoneygeneralcombo.h
+
kmymoneymvccombo.h
1
KMyMoneyPayeeCombo QWidget -
kmymoneypayeecombo.h
+
kmymoneymvccombo.h
1
KLineEdit QLineEdit
klineedit.h
diff --git a/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.ui b/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.ui index 51b72308f..69fce367c 100644 --- a/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.ui +++ b/kmymoney/wizards/newinvestmentwizard/kinvestmentdetailswizardpage.ui @@ -1,299 +1,299 @@ KInvestmentDetailsWizardPage 0 0 216 351 Investment details Enter the details below and click <b>Next</b> to continue entering the online update details. Qt::AlignTop true Qt::Vertical QSizePolicy::Expanding 20 16 Trading symbol false Enter the ticker symbol (e.g. RHAT). Full name false Fraction false 0 0 1 / Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false Trading market false true AMEX EUREX FUND NASDAQ NYSE XETRA Identification number false Enter the CUSIP/ISIN/WKN identification number here Trading Currency false Price entry false Price precision This determines how many digits after decimal symbol price can have. 2 10 This controls what to do with digit situated after precision digits in amount values. Remainder Qt::Vertical QSizePolicy::Expanding 20 16 KComboBox QComboBox
kcombobox.h
kMyMoneyEdit QWidget
kmymoneyedit.h
kMyMoneyLineEdit QWidget
../widgets/kmymoneylineedit.h
KMyMoneyGeneralCombo QWidget -
kmymoneygeneralcombo.h
+
kmymoneymvccombo.h
1
KMyMoneySecuritySelector QWidget
kmymoneycurrencyselector.h
1
m_investmentSymbol m_investmentName m_fraction m_tradingMarket m_investmentIdentification m_tradingCurrencyEdit m_priceMode
diff --git a/kmymoney/wizards/newloanwizard/durationwizardpage.ui b/kmymoney/wizards/newloanwizard/durationwizardpage.ui index d4b110d97..a2c41f7c7 100644 --- a/kmymoney/wizards/newloanwizard/durationwizardpage.ui +++ b/kmymoney/wizards/newloanwizard/durationwizardpage.ui @@ -1,259 +1,259 @@ DurationWizardPage Duration Please enter the term of this loan or leave the field empty to calculate it. The term is the time that is required to fully repay the loan. This time might be different from the time your loan contract is signed for. Qt::AlignTop true 16 54 QSizePolicy::Expanding Qt::Vertical 1 0 Term false 0 1000 QFrame::StyledPanel QFrame::Raised 11 6 Loan amount: Qt::AlignVCenter|Qt::AlignRight true Interest rate: Qt::AlignVCenter|Qt::AlignRight false Term: Qt::AlignVCenter|Qt::AlignRight false Principal + Interest: Qt::AlignVCenter|Qt::AlignRight false Final amortization payment Qt::AlignVCenter|Qt::AlignRight false true 1 Qt::PlainText false true 1 Qt::PlainText false true 1 Qt::PlainText false true 1 Qt::PlainText false true 1 Qt::PlainText false KMyMoneyGeneralCombo QWidget -
kmymoneygeneralcombo.h
+
kmymoneymvccombo.h
1
diff --git a/kmymoney/wizards/newloanwizard/keditloanwizard.cpp b/kmymoney/wizards/newloanwizard/keditloanwizard.cpp index a4a6df3b7..2305dcf2d 100644 --- a/kmymoney/wizards/newloanwizard/keditloanwizard.cpp +++ b/kmymoney/wizards/newloanwizard/keditloanwizard.cpp @@ -1,545 +1,544 @@ /*************************************************************************** keditloanwizard.cpp - description ------------------- begin : Wed Nov 12 2003 copyright : (C) 2000-2003 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio (C) 2017 by Łukasz Wojniłowicz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "keditloanwizard.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_interesteditwizardpage.h" #include "ui_paymentfrequencywizardpage.h" #include "ui_paymenteditwizardpage.h" #include "ui_loanattributeswizardpage.h" #include "ui_effectivedatewizardpage.h" #include "ui_interesttypewizardpage.h" #include "ui_editselectionwizardpage.h" #include "ui_finalpaymentwizardpage.h" #include "knewloanwizard.h" #include "knewloanwizard_p.h" #include "kmymoneylineedit.h" #include "kmymoneyedit.h" #include "kmymoneyaccountselector.h" #include "mymoneyfile.h" #include "mymoneyinstitution.h" #include "mymoneyaccount.h" #include "mymoneyaccountloan.h" #include "mymoneypayee.h" #include "mymoneyschedule.h" -#include "mymoneytransactionfilter.h" class KEditLoanWizardPrivate : public KNewLoanWizardPrivate { Q_DISABLE_COPY(KEditLoanWizardPrivate) public: KEditLoanWizardPrivate(KEditLoanWizard *qq) : KNewLoanWizardPrivate(qq) { } MyMoneySchedule m_schedule; int m_lastSelection; bool m_fullyRepayLoan; }; KEditLoanWizard::KEditLoanWizard(const MyMoneyAccount& account, QWidget *parent) : KNewLoanWizard(*new KEditLoanWizardPrivate(this), parent) { Q_D(KEditLoanWizard); auto file = MyMoneyFile::instance(); setWindowTitle(i18n("Edit loan wizard")); d->m_account = account; try { QString id = d->m_account.value("schedule"); d->m_schedule = file->schedule(id); } catch (const MyMoneyException &) { } d->m_lastSelection = -1; loadWidgets(d->m_account); if (d->m_account.openingDate() > QDate::currentDate()) { //FIXME: port d->ui->m_effectiveDatePage->ui->m_effectiveDateNoteLabel->setText(QString("\n") + i18n( "Note: you will not be able to modify this account today, because the opening date \"%1\" is in the future. " "Please revisit this dialog when the time has come.", QLocale().toString(d->m_account.openingDate()))); } else { d->ui->m_effectiveDatePage->ui->m_effectiveDateNoteLabel->hide(); } // turn off all pages that are contained here for derived classes d->m_pages.clearBit(Page_Intro); d->m_pages.clearBit(Page_NewGeneralInfo); d->m_pages.clearBit(Page_LendBorrow); d->m_pages.clearBit(Page_Name); d->m_pages.clearBit(Page_NewCalculateLoan); d->m_pages.clearBit(Page_NewPayments); removePage(Page_AssetAccount); d->ui->m_assetAccountPage = 0; // turn on all pages that are contained here for derived classes d->m_pages.setBit(Page_EditIntro); d->m_pages.setBit(Page_EditSelection); // make sure, we show the correct start page setStartId(Page_EditIntro); } KEditLoanWizard::~KEditLoanWizard() { } void KEditLoanWizard::loadWidgets(const MyMoneyAccount& /* account */) { Q_D(KEditLoanWizard); auto file = MyMoneyFile::instance(); QString paymentAccountId, interestAccountId; //FIXME: port d->ui->m_namePage->ui->m_nameEdit->loadText(d->m_account.name()); d->ui->m_loanAmountPage->ui->m_loanAmountEdit->loadText(d->m_account.loanAmount().formatMoney(d->m_account.fraction(MyMoneyFile::instance()->security(d->m_account.currencyId())))); d->ui->m_finalPaymentPage->ui->m_finalPaymentEdit->loadText(d->m_account.finalPayment().formatMoney(d->m_account.fraction(MyMoneyFile::instance()->security(d->m_account.currencyId())))); setField("firstDueDateEdit", d->m_account.openingDate()); //FIXME: port if (d->m_account.fixedInterestRate()) { d->ui->m_interestTypePage->ui->m_fixedInterestButton->click(); } else { d->ui->m_interestTypePage->ui->m_variableInterestButton->click(); } QString institutionName = file->institution(d->m_account.institutionId()).name(); d->ui->m_loanAttributesPage->setInstitution(institutionName); MyMoneyMoney ir; if (d->m_schedule.startDate() > QDate::currentDate()) { ir = d->m_account.interestRate(d->m_schedule.startDate()); } else { ir = d->m_account.interestRate(QDate::currentDate()); } //FIXME: port d->ui->m_interestPage->ui->m_interestRateEdit->loadText(ir.formatMoney(QString(), 3)); d->ui->m_interestPage->ui->m_interestRateEdit->setPrecision(3); d->ui->m_interestEditPage->ui->m_newInterestRateEdit->loadText(ir.formatMoney(QString(), 3)); d->ui->m_interestEditPage->ui->m_newInterestRateEdit->setPrecision(3); d->ui->m_interestEditPage->ui->m_interestRateLabel->setText(QString(" ") + ir.formatMoney(QString(), 3) + QString("%")); d->ui->m_paymentFrequencyPage->ui->m_paymentFrequencyUnitEdit->setCurrentIndex(d->ui->m_paymentFrequencyPage->ui->m_paymentFrequencyUnitEdit->findData(QVariant((int)d->m_schedule.occurrencePeriod()), Qt::UserRole, Qt::MatchExactly)); d->ui->m_durationPage->updateTermWidgets(d->m_account.term()); // the base payment (amortization and interest) is determined // by adding all splits that are not automatically calculated. // If the loan is a liability, we reverse the sign at the end MyMoneyMoney basePayment; MyMoneyMoney addPayment; d->m_transaction = d->m_schedule.transaction(); foreach (const MyMoneySplit& it_s, d->m_schedule.transaction().splits()) { MyMoneyAccount acc = file->account(it_s.accountId()); // if it's the split that references the source/dest // of the money, we check if we borrow or loan money if (paymentAccountId.isEmpty() && acc.isAssetLiability() && !acc.isLoan() && it_s.value() != MyMoneyMoney::autoCalc) { if (it_s.value().isNegative()) { setField("lendButton", false); setField("borrowButton", true); } else { setField("lendButton", true); setField("borrowButton", false); } // we keep the amount of the full payment and subtract the // base payment later to get information about the additional payment addPayment = it_s.value(); paymentAccountId = it_s.accountId(); MyMoneyPayee payee; if (!it_s.payeeId().isEmpty()) { try { payee = file->payee(it_s.payeeId()); setField("payeeEdit", payee.id()); } catch (const MyMoneyException &) { qWarning("Payee for schedule has been deleted"); } } // remove this split with one that will be replaced // later and has a phony id d->m_transaction.removeSplit(it_s); d->m_split.clearId(); d->m_transaction.addSplit(d->m_split); } if (it_s.action() == MyMoneySplit::ActionInterest) { interestAccountId = it_s.accountId(); } if (it_s.value() != MyMoneyMoney::autoCalc) { basePayment += it_s.value(); } else { // remove the splits which should not show up // for additional fees d->m_transaction.removeSplit(it_s); } } if (field("borrowButton").toBool()) { basePayment = -basePayment; addPayment = -addPayment; } // now make adjustment to get the amount of the additional fees addPayment -= basePayment; // load account selection widgets now that we know if // we borrow or lend money d->loadAccountList(); int fraction = d->m_account.fraction(MyMoneyFile::instance()->security(d->m_account.currencyId())); //FIXME: port d->ui->m_paymentPage->ui->m_paymentEdit->loadText(basePayment.formatMoney(fraction)); d->ui->m_paymentEditPage->ui->m_newPaymentEdit->loadText(basePayment.formatMoney(fraction)); d->ui->m_paymentEditPage->ui->m_paymentLabel->setText(QString(" ") + basePayment.formatMoney(fraction)); setField("additionalCost", addPayment.formatMoney(fraction)); d->ui->m_interestCategoryPage->ui->m_interestAccountEdit->setSelected(interestAccountId); d->ui->m_schedulePage->ui->m_paymentAccountEdit->setSelected(paymentAccountId); setField("nextDueDateEdit", d->m_schedule.nextPayment()); int changeFrequencyUnit; int amt = d->m_account.interestChangeFrequency(&changeFrequencyUnit); if (amt != -1) { setField("interestFrequencyAmountEdit", amt); setField("interestFrequencyUnitEdit", changeFrequencyUnit); } // keep track, if the loan should be fully repayed d->m_fullyRepayLoan = d->m_account.finalPayment() < basePayment; d->updateLoanInfo(); } bool KEditLoanWizard::validateCurrentPage() { Q_D(KEditLoanWizard); auto dontLeavePage = false; //FIXME: port m_lastSelection QAbstractButton* button = d->ui->m_editSelectionPage->ui->m_selectionButtonGroup->button(d->m_lastSelection); if (currentPage() == d->ui->m_editSelectionPage) { if (button != 0 && d->m_lastSelection != d->ui->m_editSelectionPage->ui->m_selectionButtonGroup->checkedId()) { QString errMsg = i18n( "Your previous selection was \"%1\". If you select another option, " "KMyMoney will dismiss the changes you have just entered. " "Do you wish to proceed?", button->text()); if (KMessageBox::questionYesNo(this, errMsg) == KMessageBox::No) { dontLeavePage = true; } else { loadWidgets(d->m_account); } } if (!dontLeavePage) { // turn off all pages except the summary at the end // and the one's we need for the selected option // and load the widgets with the current values // general info d->m_pages.clearBit(Page_Name); d->m_pages.clearBit(Page_InterestType); d->m_pages.clearBit(Page_PreviousPayments); d->m_pages.clearBit(Page_RecordPayment); d->m_pages.clearBit(Page_VariableInterestDate); d->m_pages.clearBit(Page_FirstPayment); // loan calculation d->m_pages.clearBit(Page_PaymentEdit); d->m_pages.clearBit(Page_InterestEdit); d->m_pages.clearBit(Page_PaymentFrequency); d->m_pages.clearBit(Page_InterestCalculation); d->m_pages.clearBit(Page_LoanAmount); d->m_pages.clearBit(Page_Interest); d->m_pages.clearBit(Page_Duration); d->m_pages.clearBit(Page_Payment); d->m_pages.clearBit(Page_FinalPayment); d->m_pages.clearBit(Page_CalculationOverview); // payment d->m_pages.clearBit(Page_InterestCategory); d->m_pages.clearBit(Page_AdditionalFees); d->m_pages.clearBit(Page_Schedule); d->m_pages.setBit(Page_Summary); // Attributes d->m_pages.clearBit(Page_LoanAttributes); d->m_pages.setBit(Page_EffectiveDate); if (page(Page_Summary) != 0) { removePage(Page_Summary); } if (field("editInterestRateButton").toBool()) { d->m_pages.setBit(Page_PaymentFrequency); d->m_pages.setBit(Page_InterestType); d->m_pages.setBit(Page_VariableInterestDate); d->m_pages.setBit(Page_PaymentEdit); d->m_pages.setBit(Page_InterestEdit); d->m_pages.setBit(Page_InterestCategory); d->m_pages.setBit(Page_Schedule); d->m_pages.setBit(Page_SummaryEdit); } else if (field("editOtherCostButton").toBool()) { d->m_pages.setBit(Page_PaymentFrequency); d->m_pages.setBit(Page_AdditionalFees); d->m_pages.setBit(Page_InterestCategory); d->m_pages.setBit(Page_Schedule); d->m_pages.setBit(Page_SummaryEdit); } else if (field("editOtherInfoButton").toBool()) { d->m_pages.setBit(Page_Name); d->m_pages.setBit(Page_InterestCalculation); d->m_pages.setBit(Page_Interest); d->m_pages.setBit(Page_Duration); d->m_pages.setBit(Page_Payment); d->m_pages.setBit(Page_FinalPayment); d->m_pages.setBit(Page_CalculationOverview); d->m_pages.setBit(Page_InterestCategory); d->m_pages.setBit(Page_AdditionalFees); d->m_pages.setBit(Page_Schedule); d->m_pages.clearBit(Page_SummaryEdit); setPage(Page_Summary, d->ui->m_summaryPage); d->m_pages.setBit(Page_Summary); } else if (field("editAttributesButton").toBool()) { d->m_pages.setBit(Page_LoanAttributes); d->m_pages.clearBit(Page_EffectiveDate); } else { qWarning("%s,%d: This should never happen", __FILE__, __LINE__); } d->m_lastSelection = d->ui->m_editSelectionPage->ui->m_selectionButtonGroup->checkedId(); } // if(!dontLeavePage) } else if (currentPage() == d->ui->m_additionalFeesPage) { if (field("editOtherCostButton").toBool()) { d->updateLoanInfo(); updateEditSummary(); } } else if (currentPage() == d->ui->m_interestEditPage) { // copy the necessary data to the widgets used for calculation //FIXME: port to fields d->ui->m_interestPage->ui->m_interestRateEdit->setValue(field("newInterestRateEdit").value()); d->ui->m_paymentPage->ui->m_paymentEdit->setValue(field("newPaymentEdit").value()); // if interest rate and payment amount is given, then force // the term to be recalculated. The final payment is adjusted to // 0 if the loan was ment to be fully repayed d->ui->m_durationPage->updateTermWidgets(d->m_account.term()); if (field("interestRateEditValid").toBool() && field("paymentEditValid").toBool()) { // if there's an amortization going on, we can evaluate // the new term. If the amortization is 0 (interest only // payments) then we keep the term as entered by the user. if (field("loanAmountEdit").value() != field("finalPaymentEdit").value()) { setField("durationValueEdit", 0); } if (d->m_fullyRepayLoan) d->ui->m_finalPaymentPage->ui->m_finalPaymentEdit->loadText(MyMoneyMoney().formatMoney(d->m_account.fraction(MyMoneyFile::instance()->security(d->m_account.currencyId())))); } /* // we need to calculate the balance at the time of the change // in order to accurately recalculate the term. A special // situation arises, when we keep track of all payments and // the full loan is not yet paid out. In this case, we take the // the loan amount minus all amortization payments as the current // balance. // FIXME: This needs some more thoughts. We leave it out for // now and always calculate with the full loan amount. MyMoneyMoney balance = m_account.openingBalance(); QList list; QList::ConstIterator it; MyMoneySplit split; MyMoneyTransactionFilter filter(m_account.id()); filter.setDateFilter(QDate(), m_effectiveChangeDateEdit->date().addDays(-1)); list = MyMoneyFile::instance()->transactionList(filter); for(it = list.begin(); it != list.end(); ++it) { try { split = (*it).splitByAccount(m_account.id()); balance += split.value(); } catch(const MyMoneyException &e) { // account is not referenced within this transaction } } m_loanAmountEdit->setText(balance.formatMoney()); */ // now re-calculate the figures dontLeavePage = !d->calculateLoan(); // reset the original loan amount to the widget //FIXME: port to fields d->ui->m_loanAmountPage->ui->m_loanAmountEdit->setValue(d->m_account.loanAmount()); if (!dontLeavePage) { d->updateLoanInfo(); updateEditSummary(); } } if (!dontLeavePage) dontLeavePage = ! KNewLoanWizard::validateCurrentPage(); // These might have been set by KNewLoanWizard d->m_pages.clearBit(Page_PreviousPayments); d->m_pages.clearBit(Page_RecordPayment); if (dontLeavePage) return false; // we never need to show this page if (currentPage() == d->ui->m_previousPaymentsPage) dontLeavePage = KNewLoanWizard::validateCurrentPage(); return ! dontLeavePage; } void KEditLoanWizard::updateEditSummary() { Q_D(KEditLoanWizard); // calculate the number of affected transactions MyMoneyTransactionFilter filter(d->m_account.id()); filter.setDateFilter(field("effectiveChangeDateEdit").toDate(), QDate()); int count = 0; QList list; list = MyMoneyFile::instance()->transactionList(filter); foreach (const MyMoneyTransaction& it, list) { int match = 0; foreach (const MyMoneySplit& it_s, it.splits()) { // we only count those transactions that have an interest // and amortization part if (it_s.action() == MyMoneySplit::ActionInterest) match |= 0x01; if (it_s.action() == MyMoneySplit::ActionAmortization) match |= 0x02; } if (match == 0x03) ++count; } setField("affectedPayments", QString().sprintf("%d", count)); } const MyMoneySchedule KEditLoanWizard::schedule() const { Q_D(const KEditLoanWizard); MyMoneySchedule sched = d->m_schedule; sched.setTransaction(transaction()); sched.setOccurrence(eMyMoney::Schedule::Occurrence(field("paymentFrequencyUnitEdit").toInt())); if (field("nextDueDateEdit").toDate() < d->m_schedule.startDate()) sched.setStartDate(field("nextDueDateEdit").toDate()); return sched; } const MyMoneyAccount KEditLoanWizard::account() const { Q_D(const KEditLoanWizard); MyMoneyAccountLoan acc(d->m_account); if (field("interestOnReceptionButton").toBool()) acc.setInterestCalculation(MyMoneyAccountLoan::paymentReceived); else acc.setInterestCalculation(MyMoneyAccountLoan::paymentDue); auto file = MyMoneyFile::instance(); QString institution = d->ui->m_loanAttributesPage->ui->m_qcomboboxInstitutions->currentText(); if (institution != i18n("(No Institution)")) { QList list; file->institutionList(list); Q_FOREACH(const MyMoneyInstitution& testInstitution, list) { if (testInstitution.name() == institution) { acc.setInstitutionId(testInstitution.id()); break; } } } else { acc.setInstitutionId(QString()); } acc.setFixedInterestRate(field("fixedInterestButton").toBool()); acc.setFinalPayment(field("finalPaymentEdit").value()); acc.setTerm(d->ui->m_durationPage->term()); acc.setPeriodicPayment(field("paymentEdit").value()); acc.setInterestRate(field("effectiveChangeDateEdit").toDate(), field("interestRateEdit").value()); acc.setPayee(field("payeeEdit").toString()); if (field("variableInterestButton").toBool()) { acc.setNextInterestChange(field("interestChangeDateEdit").toDate()); acc.setInterestChangeFrequency(field("interestFrequencyAmountEdit").toInt(), field("interestFrequencyUnitEdit").toInt()); } return acc; } const MyMoneyTransaction KEditLoanWizard::transaction() const { Q_D(const KEditLoanWizard); auto t = d->transaction(); auto s = t.splitByAccount(QString("Phony-ID")); s.setAccountId(d->m_account.id()); t.modifySplit(s); return t; } diff --git a/kmymoney/wizards/newloanwizard/knewloanwizard.ui b/kmymoney/wizards/newloanwizard/knewloanwizard.ui index 631b9d2e3..61bfd5d69 100644 --- a/kmymoney/wizards/newloanwizard/knewloanwizard.ui +++ b/kmymoney/wizards/newloanwizard/knewloanwizard.ui @@ -1,160 +1,160 @@ KNewLoanWizard 0 0 589 458 800 32767 New Loan Wizard true NewPaymentsWizardPage QWizardPage
newpaymentswizardpage.h
NewIntroWizardPage QWizardPage
newintrowizardpage.h
EditIntroWizardPage QWizardPage
editintrowizardpage.h
kMyMoneyLineEdit QWidget
../widgets/kmymoneylineedit.h
90 25 0 5 5 image0
kMyMoneyAccountSelector QWidget
../widgets/kmymoneyaccountselector.h
80 80 0 5 5 image0
KComboBox QComboBox
kcombobox.h
KMyMoneyGeneralCombo QWidget -
kmymoneygeneralcombo.h
+
kmymoneymvccombo.h
1
89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000041d49444154388d8d95c18b1c4514c67fbb29e5b54ea05b26da1d36b08d09a6c508138cb8c13d647141163de49043bca9f1628eb9294810f24f88c15b728b07617358888785d980427290b4a8d00b19a8821da8228cf42316ac87eed9247ac95c9aeaeef9eaabdffb5ebf85f178ccfcf7f5b79fec07a7280a08a008f25ceb7c29e5da573716e65a0be3f198ef6f5ededff9b9e1b34faf90882092a1ea21428b9220ffbf46054055b19386ba6970ae6665b5e0d2c5eb0b066067bbe1f21757c88f5504eff0c192189081303a5582b16854fccce2fe041f00cdd0a8103b874556405476b62d972ec2c2b90ff3fd8df5cb946549d80b34aea15ccaa94625450e2d0e558b460b7371a734b5e027251a155545678ab62d7eea90b4c504078908c107acb354cb251b1f8f08b1c18631aabbb4d18211404804f22545863561d2f060fb34aa8a18d0086204e72c46a32292e1a796f278c1da7a859d8da9a737506db04d0a24103d8949480b214d3bbee952a05c1d536f9d2684160c744f048311543d459eb1b65ee1754cedae13a64abd5d6127053c9d0281f22d4f75c6a2118a5c09272d7ab740638bf4ef2dd2174032d0d8d04c6fe19c32bedd8bc65e342aa3f785e2784bfd6bc1bd9db24b05505616a4edd67d5a8c1801a03a9962754c504bf34b8986acfb9be91cac7d54315a15541b36b134f74bd2dc53940115a5381e087bc25c6f71be831a8baa234cc1ee3eeb74ed83116beb2b8849d108a3730e8ce27e2bfb82419a7b407ac7c222fd0e6280a8078ce64e47ef54ac9d5be9aaae0162e0de568a44a18d3df90845d919c4f01463406340a44f40efb43a99b371618488e2670d1a2d7ee6f1d3be39342104e937ed85fb86599c33e9ee0be9a004946259d8b858909880d59aa00daa0d3280b5f39e34074c4b3a5434029a76a9e9f5cc0163b5c80024158a1cfc4cb1d3061d5834066cb0a403c008599a70f6bc27b8d03106ea3ae91987278c5b143b015547224ab5e24185cd9b81665273e7b6e7cecd82ddbf8098002de94049737bc09828cf3036f31c3735c8b0e98eb704672f348c7f7c93cd1fb2ae905119ff54e0dff3542b0e9dd983dcea2cc5eea6cf303e74ec687af5c41b6fa38f5e203c7ec8e1238ef85831a214afef21060ea781ec68247b65c6916353cc8b73d1ae79354606af2a930709aa118da1636c274dd7e3774b884a79aa6b5711a538d33cf9c01c1c9b83bc6a1f55c91ccb23b05b1deb43a7decdaffefd2862f61366aa8449c69e33642f012f2bb208fa98eeda1f3346f076c8eeef19475e9be115423364f78f84105a8643d34d90cfbfdcd8cf872589a45dc7f54cf3134a36f4e495c23f9da8db15bccb708d4014f2ca512cb5d4f7c13e84300d5cff6eb39b202bab053bdb9674f0648609427d0f8402ddeac30f4fcd3ced3efc3b422d406c09b340bedc1571e1bfc3d44dc281634c5fe5e758e7395cfbe6d6c130fd17d92372d277c159b70000000049454e44ae426082
diff --git a/kmymoney/wizards/newloanwizard/namewizardpage.ui b/kmymoney/wizards/newloanwizard/namewizardpage.ui index b204ae902..1b7598bca 100644 --- a/kmymoney/wizards/newloanwizard/namewizardpage.ui +++ b/kmymoney/wizards/newloanwizard/namewizardpage.ui @@ -1,135 +1,135 @@ NameWizardPage Name of the loan How do you want to call this loan? Examples for names are 'car loan', 'school loan', 'home owner loan'. Qt::AlignTop true 16 56 QSizePolicy::Expanding Qt::Vertical 0 6 Name false 16 56 QSizePolicy::Expanding Qt::Vertical TextLabel6 false TextLabel7 false 16 56 QSizePolicy::Expanding Qt::Vertical KMyMoneyPayeeCombo QWidget -
kmymoneypayeecombo.h
+
kmymoneymvccombo.h
1
diff --git a/kmymoney/wizards/newloanwizard/paymentfrequencywizardpage.ui b/kmymoney/wizards/newloanwizard/paymentfrequencywizardpage.ui index cdcdd073b..93ad9744a 100644 --- a/kmymoney/wizards/newloanwizard/paymentfrequencywizardpage.ui +++ b/kmymoney/wizards/newloanwizard/paymentfrequencywizardpage.ui @@ -1,125 +1,125 @@ PaymentFrequencyWizardPage Payment frequency 11 6 How often will there be payments made to this loan? Qt::AlignTop true 0 49 QSizePolicy::Expanding Qt::Vertical 0 6 39 0 QSizePolicy::Expanding Qt::Horizontal 56 0 QSizePolicy::Expanding Qt::Horizontal 0 49 QSizePolicy::Expanding Qt::Vertical KMyMoneyFrequencyCombo KComboBox -
kmymoneyfrequencycombo.h
+
kmymoneymvccombo.h
KComboBox QComboBox
kcombobox.h
diff --git a/kmymoney/wizards/newloanwizard/variableinterestdatewizardpage.ui b/kmymoney/wizards/newloanwizard/variableinterestdatewizardpage.ui index cfca9708e..5e7bae226 100644 --- a/kmymoney/wizards/newloanwizard/variableinterestdatewizardpage.ui +++ b/kmymoney/wizards/newloanwizard/variableinterestdatewizardpage.ui @@ -1,120 +1,120 @@ VariableInterestDateWizardPage Date of next interest change Select the date when the interest rate for this loan will be modified and the frequency of the future changes. Qt::AlignTop true 16 99 QSizePolicy::Expanding Qt::Vertical Next interest change on false 0 1000 1 0 Time until next change false 20 80 QSizePolicy::Expanding Qt::Vertical KMyMoneyGeneralCombo QWidget -
kmymoneygeneralcombo.h
+
kmymoneymvccombo.h
1